Finding the local min/max in a 1D-Array/Histogram - java

I'm trying to find the indices of all the local minima and maxima within an Array.
Example:
int[] array = {5,4,3,3,3,3,3,2,2,2, 6,6,8,5,5,5,3,3,2,1, 1,4,4,7};
// | | |
// Indices: 0,1,2,3,4,5,6,7,8,9, 10,1,2,3,4,5,6,7,8,9, 20,1,2,3
// Minima: 8, 20
// Maxima: 12
I came up with an algorithm about which I have few questions:
Is there a much better one? :)
I used an Enum with methods to achieve this dualism that UP and STRAIGHT_UP are both "UP". Seems messy to me. Any suggestions?
Do you have better method-names? direction() (+return value) kind of implies that STRAIGHT is not a dir. But at the same time it is, since its an element in the Emum. Hm.
It works for the given array. Do you see a situation where it does not?
-
import java.util.ArrayList;
public class MinMaxFinder {
private int[] array;
private ArrayList<Integer> minima;
private ArrayList<Integer> maxima;
private enum Direction{
UP, DOWN, STRAIGHT_UP, STRAIGHT_DOWN, STRAIGHT;
public Direction direction(){
if(this==UP || this==STRAIGHT_UP){
return UP;
}else if(this==DOWN || this==STRAIGHT_DOWN){
return DOWN;
}else{
return STRAIGHT;
}
}
public boolean isStraight(){
if(this==STRAIGHT_DOWN || this==STRAIGHT_UP || this==STRAIGHT){
return true;
}else{
return false;
}
}
public boolean hasDifferentDirection(Direction other){
if(this!=STRAIGHT && other!=STRAIGHT && this.direction() != other.direction() ){
return true;
}
return false;
}
}
public MinMaxFinder(int[] array){
this.array = array;
}
public void update() {
minima = new ArrayList<Integer>();
maxima = new ArrayList<Integer>();
Direction segmentDir = Direction.DOWN;
int indexOfDirectionChange = 0;
int prevVal = array[0];
int arrayLength = array.length;
for(int i=1; i<arrayLength; i++){
int currVal = array[i];
Direction currentDir = currVal<prevVal?Direction.DOWN:(currVal>prevVal?Direction.UP:Direction.STRAIGHT);
prevVal = currVal;
if(currentDir.hasDifferentDirection(segmentDir)){
int changePos = (indexOfDirectionChange+i-1)/2;
if(currentDir.direction() == Direction.DOWN){
maxima.add(changePos);
}else{
minima.add(changePos);
}
segmentDir = currentDir;
indexOfDirectionChange = i;
}else if( currentDir.isStraight() ^ segmentDir.isStraight() ){
indexOfDirectionChange = i;
if(currentDir.isStraight() && segmentDir.direction()==Direction.UP){
segmentDir=Direction.STRAIGHT_UP;
}else if(currentDir.isStraight() && segmentDir.direction()==Direction.DOWN){
segmentDir=Direction.STRAIGHT_DOWN;
}else{
segmentDir = currentDir;
}
}
}
}
public ArrayList<Integer> getMinima() {
return minima;
}
public ArrayList<Integer> getMaxima() {
return maxima;
}
}

Consider an array of first differences d[i] = a[i] - a[i-1].
If d[i] is positive, then a increased over the last step and if d[i] is negative then a decreased. So, a change in sign of d from positive to negative indicates a was increasing, now decreasing, a local max. Similarly, negative to positive indicates a local min.

Something like this "should" work and it's probably conceptually less complicated.
Scans the array once and registers mins and maxs.
Things worth mentioning:
1) The if(direction < 0){}else{} can probably be removed, but I didn't have time to think about the details.
2) The key idea is, depending on the first "direction" (whether we see a min or a max first), the for loops order changes.
3) in case of multiple items, it will always keep the last element (highest index).
if(a.length < 2){
return;
}
List<Integer> mins = new ArrayList<Integer>();
List<Integer> maxs = new ArrayList<Integer>();
int i=1;
int prev = 0;
int direction = 0;
for(int j=1, k = 0;j<a.length && (direction = a[j]-a[k]) == 0;j++, k++);
if(direction == 0){
//Array contains only same value.
return;
}
if(direction < 0){
while(i<a.length){
for(;i<a.length && a[prev] >= a[i];i++,prev++);
mins.add(prev);
for(;i<a.length && a[prev] <= a[i];i++,prev++);
maxs.add(prev);
i++;prev++;
}
}
else{
while(i<a.length){
for(;i<a.length && a[prev] <= a[i];i++,prev++);
maxs.add(prev);
for(;i<a.length && a[prev] >= a[i];i++,prev++);
mins.add(prev);
i++;prev++;
}
}
//maxs and mins now contain what requested

I think i got it. Thanks guys! Your ideas helped me a lot!
The following solution will do for me:
ArrayList<Integer> mins = new ArrayList<Integer>();
ArrayList<Integer> maxs = new ArrayList<Integer>();
int prevDiff = array[0] - array[1];
int i=1;
while(i<array.length-1){
int currDiff = 0;
int zeroCount = 0;
while(currDiff == 0 && i<array.length-1){
zeroCount++;
i++;
currDiff = array[i-1] - array[i];
}
int signCurrDiff = Integer.signum(currDiff);
int signPrevDiff = Integer.signum(prevDiff);
if( signPrevDiff != signCurrDiff && signCurrDiff != 0){ //signSubDiff==0, the case when prev while ended bcoz of last elem
int index = i-1-(zeroCount)/2;
if(signPrevDiff == 1){
mins.add( index );
}else{
maxs.add( index );
}
}
prevDiff = currDiff;
}

Related

I am confused on how to properly code the while loop. How to make sure it iterates through all the values in the array

The output should be that the array is sorted into descending order but my code stops after one iteration and doesn't fully sort the entire array and I don't know how to fix it. I am a beginner to please explain the logic in simple terms
public static int[] insertionSort(int[] array){
for(int i1 = 1; i1 < array.length; i1++){
int indexCurrent = i1;
boolean done = false;
while(indexCurrent <= (array.length -1) && done == false){
if(array[indexCurrent] > array[indexCurrent-1]){
int temp = array[indexCurrent-1];
array[indexCurrent-1] = array[indexCurrent];
array[indexCurrent] = temp;
indexCurrent++;
}else{
done = true;
}
}
}
return array;
}
You should be decrementing indexCurrent.
while(indexCurrent > 0 && !done){
if(array[indexCurrent] > array[indexCurrent-1]){
int temp = array[indexCurrent-1];
array[indexCurrent-1] = array[indexCurrent];
array[indexCurrent] = temp;
indexCurrent--;
}else{
done = true;
}
}

Calculate if a given input falls into a given value range

I have this code that I need to return an index based on the given Double distance input:
public static int calculateHashPrecision(Double reference) {
double[][] ranges = {
{0.037, 0.018},
{0.149, 0.149},
{1.19, 0.6},
{4.47, 4.78},
{38.2, 19.1},
{152.8, 152.8},
{1200, 610},
{4900, 4900},
{39000, 19500},
{156000, 156000},
{1251000, 625000},
{5004000, 5004000},
};
return 6;
}
Say the value of reference passed is 1000 so it falls into the 1200, 610 range, so this function should return index 6. What would be the best implementation for this function?
UPDATE:
And if it is not in the specific hard-coded range, it would return the nearest one.
With for each loop you can do more quickly and fast
int index = 0;
for(double[] range: ranges){
if( range[1] < reference && reference < range[0]){
return index;
}
index ++;
}
public static int calculateHashPrecision(Double reference) {
double[][] ranges = {
{0.037, 0.018},
{0.149, 0.149},
{1.19, 0.6},
{4.47, 4.78},
{38.2, 19.1},
{152.8, 152.8},
{1200, 610},
{4900, 4900},
{39000, 19500},
{156000, 156000},
{1251000, 625000},
{5004000, 5004000},
};
int maxValueIndexInRaw = 0;
int minValueIndexInRaw = 1;
for (int i = 0; i < ranges.length; i++) {
if (reference >=ranges[i][minValueIndexInRaw] && reference <= ranges[i][maxValueIndexInRaw] ) {
return i;
}
}
return -1;
}
public static int calculateHashPrecision(double reference) {
double[][] ranges = {
{0.037, 0.018},
{0.149, 0.149},
{1.19, 0.6},
{4.47, 4.78},
{38.2, 19.1},
{152.8, 152.8},
{1200, 610},
{4900, 4900},
{39000, 19500},
{156000, 156000},
{1251000, 625000},
{5004000, 5004000},
};
// search through the array of ranges, and return the first index it falls into
for (int i = 0; i < ranges.length; i++) {
double[] rng = ranges[i];
// ranges as given have the higher number first, lower number second
// adjust this logic as necessary
double rng_bottom = rng[1];
double rng_top = rng[0];
if (rng_bottom < reference && reference < rng_top) {
return i;
}
}
// if no range contains the reference, e.g. for the number 6000, then return -1
return -1;
}
You may want to carefully examine your ranges to make sure they're coded correctly. Did you mean for half of them to have the same start and end?
If the data is sorted you can use binary search
int hi = ranges.length - 1, lo = 0;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int cmp = Double.compare(ranges[mid][0], reference);
if (cmp > 0 || cmp == 0) {
if (ranges[mid][1] <= reference) return mid;
else hi = mid - 1;
} else if (cmp < 0) {
lo = mid + 1;
}
}
return -1;

EmptyStackException - Java Depth First Search algorithm on 2D array

I have looked at this question here I tried most of the code samples from there but when i use it in my code it just skips the algorithm.
I have this DFS algorithm that uses stack, I am getting a EmptyStackException, I have debugged the algorithm and after first recursive search the stack is empty, the first search works but then the size of stack is set to 0, What am I missing here? github
How can I make sure that the stack is not empty after the first search?
The line that I get the exception on is while(true){AddBridges state = gameTree.peek(); ...
I am using a 2d Array to generate the nodes at random from 0 to 4 0 = null 1-4 = island The array generates Random Integers every time the user starts the game, could that cause the Algorithm to brake,
After a weekend of debugging I found that the algorithm sometimes brakes after 4-6 searches, and sometimes breaks after the first search.
public int[][] debug_board_state_easy = new int[4][4];
//This Generates random 2d array
private void InitializeEasy() {
Random rand = new Random();
setCurrentState(new State(WIDTH_EASY));
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
debug_board_state_easy[row][column] = Integer.valueOf(rand.nextInt(5));
}
}
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
System.out.print(debug_board_state_easy[row][column] + " ");
}
System.out.println(debug_board_state_easy);
}
//I am applying the search algorithm here...
this.search();
for (int row = 0; row < WIDTH_EASY; ++row) {
for (int column = 0; column < WIDTH_EASY; ++column) {
getCurrentState().board_elements[row][column] = new BoardElement();
getCurrentState().board_elements[row][column].max_connecting_bridges = Integer.valueOf(debug_board_state_easy[row][column]);
getCurrentState().board_elements[row][column].row = row;
getCurrentState().board_elements[row][column].col = column;
if (getCurrentState().board_elements[row][column].max_connecting_bridges > 0) {
getCurrentState().board_elements[row][column].is_island = true;
}
}
}
}
void search() {
Map<Point, List<Direction>> remainingOptions = new HashMap<>();
Stack<Land> gameTree = new Stack<>();
gameTree.push(new AddBridges(debug_board_state_easy));
while(true) {
AddBridges state = gameTree.peek();
int[] p = state.lowestTodo();
if (p == null)
System.out.println("solution found");
// move to next game state
int row = p[0];
int column = p[1];
System.out.println("expanding game state for node at (" + row + ", " + column + ")");
List<Direction> ds = null;
if(remainingOptions.containsKey(new Point(row,column)))
ds = remainingOptions.get(new Point(row,column));
else{
ds = new ArrayList<>();
for(Direction dir : Direction.values()) {
int[] tmp = state.nextIsland(row, column, dir);
if(tmp == null)
continue;
if(state.canBuildBridge(row,column,tmp[0], tmp[1]))
ds.add(dir);
}
remainingOptions.put(new Point(row,column), ds);
}
// if the node can no longer be expanded, and backtracking is not possible we quit
if(ds.isEmpty() && gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue;
}
Direction dir = ds.remove(0);
System.out.println("connecting " + dir.name());
remainingOptions.put(new Point(row,column), ds);
AddBridgesnextState = new AddBridges(state);
int[] tmp = state.nextIsland(row,column,dir);
nextState.connect(row,column, tmp[0], tmp[1]);
gameTree.push(nextState);
}
}
}
Add bridges class
public class AddBridges {
private int[][] BRIDGES_TO_BUILD;
private boolean[][] IS_ISLAND;
private Direction[][] BRIDGES_ALREADY_BUILT;
public Land(int[][] bridgesToDo){
BRIDGES_TO_BUILD = copy(bridgesToDo);
int numberRows = bridgesToDo.length;
int numberColumns = bridgesToDo[0].length;
BRIDGES_ALREADY_BUILT = new Direction[numberRows][numberColumns];
IS_ISLAND = new boolean[numberRows][numberColumns];
for(int i=0;i<numberRows;i++) {
for (int j = 0; j < numberColumns; j++) {
BRIDGES_ALREADY_BUILT[i][j] = null;
IS_ISLAND[i][j] = bridgesToDo[i][j] > 0;
}
}
}
public AddBridges (AddBridges other){
BRIDGES_TO_BUILD = copy(other.BRIDGES_TO_BUILD);
int numberRows = BRIDGES_TO_BUILD.length;
int numberColumns = BRIDGES_TO_BUILD[0].length;
BRIDGES_ALREADY_BUILT = new Direction[numberRows][numberColumns];
IS_ISLAND = new boolean[numberRows][numberColumns];
for(int i=0;i<numberRows;i++) {
for (int j = 0; j < numberColumns; j++) {
BRIDGES_ALREADY_BUILT[i][j] = other.BRIDGES_ALREADY_BUILT[i][j];
IS_ISLAND[i][j] = other.IS_ISLAND[i][j];
}
}
}
public int[] next(int r, int c, Direction dir){
int numberRows = BRIDGES_TO_BUILD.length;
int numberColumns = BRIDGES_TO_BUILD[0].length;
// out of bounds
if(r < 0 || r >=numberRows || c < 0 || c >= numberColumns)
return null;
// motion vectors
int[][] motionVector = {{-1, 0},{0,1},{1,0},{0,-1}};
int i = Arrays.asList(Direction.values()).indexOf(dir);
// calculate next
int[] out = new int[]{r + motionVector[i][0], c + motionVector[i][1]};
r = out[0];
c = out[1];
// out of bounds
if(r < 0 || r >=numberRows || c < 0 || c >= numberColumns)
return null;
// return
return out;
}
public int[] nextIsland(int row, int column, Direction dir){
int[] tmp = next(row,column,dir);
if(tmp == null)
return null;
while(!IS_ISLAND[tmp[0]][tmp[1]]){
tmp = next(tmp[0], tmp[1], dir);
if(tmp == null)
return null;
}
return tmp;
}
public boolean canBuildBridge(int row0, int column0, int row1, int column1){
if(row0 == row1 && column0 > column1){
return canBuildBridge(row0, column1, row1, column0);
}
if(column0 == column1 && row0 > row1){
return canBuildBridge(row1, column0, row0, column1);
}
if(row0 == row1){
int[] tmp = nextIsland(row0, column0, Direction.EAST);
if(tmp == null)
return false;
if(tmp[0] != row1 || tmp[1] != column1)
return false;
if(BRIDGES_TO_BUILD[row0][column0] == 0)
return false;
if(BRIDGES_TO_BUILD[row1][column1] == 0)
return false;
for (int i = column0; i <= column1 ; i++) {
if(IS_ISLAND[row0][i])
continue;
if(BRIDGES_ALREADY_BUILT[row0][i] == Direction.NORTH)
return false;
}
}
if(column0 == column1){
int[] tmp = nextIsland(row0, column0, Direction.SOUTH);
if(tmp == null)
return false;
if(tmp[0] != row1 || tmp[1] != column1)
return false;
if(BRIDGES_TO_BUILD[row0][column0] == 0 || BRIDGES_TO_BUILD[row1][column1] == 0)
return false;
for (int i = row0; i <= row1 ; i++) {
if(IS_ISLAND[i][column0])
continue;
if(BRIDGES_ALREADY_BUILT[i][column0] == Direction.EAST)
return false;
}
}
// default
return true;
}
public int[] lowestTodo(){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
int[] out = {0, 0};
for (int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
if(BRIDGES_TO_BUILD[i][j] == 0)
continue;
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0)
out = new int[]{i, j};
if (BRIDGES_TO_BUILD[i][j] < BRIDGES_TO_BUILD[out[0]][out[1]])
out = new int[]{i, j};
}
}
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0) {
return null;
}
return out;
}
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int[][] copy(int[][] other){
int[][] out = new int[other.length][other.length == 0 ? 0 : other[0].length];
for(int r=0;r<other.length;r++)
out[r] = Arrays.copyOf(other[r], other[r].length);
return out;
}
public void connect(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
connect(r0, c1, r1, c0);
return;
}
if(c0 == c1 && r0 > r1){
connect(r1, c0, r0, c1);
return;
}
if(!canBuildBridge(r0, c0, r1, c1))
return;
BRIDGES_TO_BUILD[r0][c0]--;
BRIDGES_TO_BUILD[r1][c1]--;
if(r0 == r1){
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
BRIDGES_ALREADY_BUILT[r0][i] = Direction.EAST;
}
}
if(c0 == c1){
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
BRIDGES_ALREADY_BUILT[i][c0] = Direction.NORTH;
}
}
}
}
One part of your question stood out to me as the root of the problem: "What am I missing here"? Unit tests, unless I just didn't see them in your project.
Questions like "the array generates Random Integers every time the user starts the game, could that cause the Algorithm to brake?", are the reason unit tests exist, along with the following:
In the course of writing tests on sections of code that don't end up
being the problem, you'll prove definitively that they aren't the
problem.
If the code you're working with can't be tested as-is, or
is "too complex" to test, re-writing it will make you a better
designer and will often lead to "I can't believe I didn't see that"
moments.
When you refactor this program (reduce complexity, rename variables to make them easier to understand, etc), you'll be notified immediately if something breaks
and you won't have to spend another weekend trying to figure it out.
As an example, instead of randomizing the board within the method that searches it, randomize it elsewhere and then drop it into that method as an argument (or into the class' constructor). That way, you can initialize your own test board(s) with your own supplied values and see if some boards work better than others and why. Split up larger methods into smaller methods, each with their own parameters and tests. Aim to make a program out of smaller confirmed-working pieces, instead of making a huge thing and then wondering if the problem is the small part you think it is or something else.
You'll save so much time and frustration in the long run, and you'll end up leagues ahead of those who code for hours and then debug for hours.
There's a lot going on in the code, but the first thing I notice might help:
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue;
}
you pop from the stack, and continue onto the next while(true) iteration, at that point, there may be nothing on the stack since you have not added anything else.
I agree with #Rosa -
The EmptyStackException should occur when removing or looking up empty Stack-
======Iteration/State in code ======
**if(ds.isEmpty()){** //HashMap isEmpty = true and gameTree.size() = 1
gameTree.pop(); // After removing element gameTree.size() = 0 (no elements in stack to peek or pop)
remainingOptions.remove(new Point(row,column));
System.out.println("going back to previous decision");
continue; //Skip all the instruction below this, continue to next iteration
}
========Next iteration========
while(true){
AddBridges state = gameTree.peek(); // gameTree.size() = 0 and a peek
operation shall fail and program will return EmptyStackException.
Required isEmpty check -
if(gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
AddBridges state = gameTree.peek();
As, no actions have been performed by function but while loop. In case other instcutions to be processed , a "break" is required.

Recursive method to find max value

maxRec() is meant to calculate the maximum value within an array using a helper
method maximize(). When this code executes, it always seems to return zero, however
it will print out the correct value. When using a debugger, I noticed that
the maxRec() method will get the right return value but wont return it; instead it sets it back to zero and moves up to the else statement.I would be grateful for any suggestions that could help fix this.
public int maxRec(int[] v) {
int maxValue = 0;
int[] tempArray = maximize(v);
boolean executeCode = true;
if (tempArray.length == 1) {
maxValue = tempArray[0];
executeCode = false;
System.out.println(maxValue);
} else if (executeCode == true && tempArray.length != 1) {
maxRec(tempArray);
}
return maxValue;
}
public int[] maximize(int[] v) {
int count = 0;
int secondCount = 0;
for (int i = 0; i < v.length; i++) {
if (v[i] > v[0]) {
count++;
}
}
int[] newArray;
newArray = new int[count];
for (int i = 0; i < v.length; i++) {
if (v[i] > v[0]) {
newArray[secondCount] = v[i];
secondCount++;
}
}
return newArray;
}
Code should be changed like this.
public int maxRec(int[] v)
{
int maxValue=0;
int[] tempArray = maximize(v);
boolean executeCode = true;
if(tempArray.length==1)
{
maxValue=tempArray[0];
executeCode=false;
}
else if(executeCode==true && tempArray.length!=1 && tempArray.length > 0)
{
maxValue = maxRec(tempArray);
}
return maxValue;
}
public int[] maximize(int[] v)
{
int count=0;
int secondCount=0;
for(int i=0;i<v.length;i++)
{
if(v[i]>v[0])
{
count++;
}
}
int[] newArray;
newArray = new int[count];
if(count == 0)
{
newArray = new int[1];
newArray[0] = v[0];
return newArray;
}
for(int i=0;i<v.length;i++)
{
if(v[i]>v[0])
{
newArray[secondCount]=v[i];
secondCount++;
}
}
return newArray;
}
maximize returns an array of all values greater than the first item of the array.
To make a recursive function, one starts with the simplest case, the least work.
The rest one delegates to a clone of oneself, the recursive call.
public int maxRec(int[] v) {
if (v.length == 0) {
throw IllegalArgumentException();
}
int[] greaterThanFirst = maximize(v);
int maxValue = 0;
if (greaterThanFirst.length == 0) {
maxValue = v[0];
} else {
maxValue = maxRec(greaterThanFirst);
}
return maxValue;
}
First a sanity check, v not being empty.
If maximize did not yield a larger number, yield the first value, being the largest.
//-------------------------------------------------------------------
// 1. maxRec --> Computes the maximum item of MyList
//-------------------------------------------------------------------
/**
* The function computes the maximum item of m (-1 if m is empty).
* #param m: The MyList we want to compute its maximum item.
* #return: The maximum item of MyList
*/
public int maxRec(MyList<Integer> m){
int max = 0;
int res = 0;
int e0 = 0;
int e1 = 0;
// Scenarios Identification
int scenario = 0;
// Type 1. MyLyst is empty
if(m.length() == 0) {
scenario = 1;
}else {
scenario = 2;
}
// Scenario Implementation
switch(scenario) {
// If MyLyst is empty
case 1:
res = -1;
break;
// If there is 1 or more elements
case 2:
// Old School
for(int i = 0; i <= m.length()-1; i++)
if(m.getElement(i) > max) {
max = m.getElement(i);
}
// Recursively
//1. Get and store first element of array
e0 = m.getElement(0);
//2. We remove the first element from MyList we just checked
m.removeElement(0);
//3. We recursively solve the smaller problem
e1 = maxRec(m);
//4. Compare and store results
if(e0 > e1) {
res = e0;
}
else {
res = e1;
}
//5. Return removed element back to the array
m.addElement(0, e0);
break;
}
//6.Display the process to see what's going on
System.out.println("My Way: "+ max);
System.out.println("Recursive Way: " + res);
//7. Return result
return res;
}

java codility Frog-River-One

I have been trying to solve a Java exercise on a Codility web page.
Below is the link to the mentioned exercise and my solution.
https://codility.com/demo/results/demoH5GMV3-PV8
Can anyone tell what can I correct in my code in order to improve the score?
Just in case here is the task description:
A small frog wants to get to the other side of a river. The frog is currently located at position 0, and wants to get to position X. Leaves fall from a tree onto the surface of the river.
You are given a non-empty zero-indexed array A consisting of N integers representing the falling leaves. A[K] represents the position where one leaf falls at time K, measured in minutes.
The goal is to find the earliest time when the frog can jump to the other side of the river. The frog can cross only when leaves appear at every position across the river from 1 to X.
For example, you are given integer X = 5 and array A such that:
A[0] = 1
A[1] = 3
A[2] = 1
A[3] = 4
A[4] = 2
A[5] = 3
A[6] = 5
A[7] = 4
In minute 6, a leaf falls into position 5. This is the earliest time when leaves appear in every position across the river.
Write a function:
class Solution { public int solution(int X, int[] A); }
that, given a non-empty zero-indexed array A consisting of N integers and integer X, returns the earliest time when the frog can jump to the other side of the river.
If the frog is never able to jump to the other side of the river, the function should return −1.
For example, given X = 5 and array A such that:
A[0] = 1
A[1] = 3
A[2] = 1
A[3] = 4
A[4] = 2
A[5] = 3
A[6] = 5
A[7] = 4
the function should return 6, as explained above. Assume that:
N and X are integers within the range [1..100,000];
each element of array A is an integer within the range [1..X].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(X), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
And here is my solution:
import java.util.ArrayList;
import java.util.List;
class Solution {
public int solution(int X, int[] A) {
int list[] = A;
int sum = 0;
int searchedValue = X;
List<Integer> arrayList = new ArrayList<Integer>();
for (int iii = 0; iii < list.length; iii++) {
if (list[iii] <= searchedValue && !arrayList.contains(list[iii])) {
sum += list[iii];
arrayList.add(list[iii]);
}
if (list[iii] == searchedValue) {
if (sum == searchedValue * (searchedValue + 1) / 2) {
return iii;
}
}
}
return -1;
}
}
You are using arrayList.contains inside a loop, which will traverse the whole list unnecessarily.
Here is my solution (I wrote it some time ago, but I believe it scores 100/100):
public int frog(int X, int[] A) {
int steps = X;
boolean[] bitmap = new boolean[steps+1];
for(int i = 0; i < A.length; i++){
if(!bitmap[A[i]]){
bitmap[A[i]] = true;
steps--;
if(steps == 0) return i;
}
}
return -1;
}
Here is my solution. It got me 100/100:
public int solution(int X, int[] A)
{
int[] B = A.Distinct().ToArray();
return (B.Length != X) ? -1 : Array.IndexOf<int>(A, B[B.Length - 1]);
}
100/100
public static int solution (int X, int[] A){
int[]counter = new int[X+1];
int ans = -1;
int x = 0;
for (int i=0; i<A.length; i++){
if (counter[A[i]] == 0){
counter[A[i]] = A[i];
x += 1;
if (x == X){
return i;
}
}
}
return ans;
}
A Java solution using Sets (Collections Framework) Got a 100%
import java.util.Set;
import java.util.TreeSet;
public class Froggy {
public static int solution(int X, int[] A){
int steps=-1;
Set<Integer> values = new TreeSet<Integer>();
for(int i=0; i<A.length;i++){
if(A[i]<=X){
values.add(A[i]);
}
if(values.size()==X){
steps=i;
break;
}
}
return steps;
}
Better approach would be to use Set, because it only adds unique values to the list. Just add values to the Set and decrement X every time a new value is added, (Set#add() returns true if value is added, false otherwise);
have a look,
public static int solution(int X, int[] A) {
Set<Integer> values = new HashSet<Integer>();
for (int i = 0; i < A.length; i++) {
if (values.add(A[i])) X--;
if (X == 0) return i;
}
return -1;
}
do not forget to import,
import java.util.HashSet;
import java.util.Set;
Here's my solution, scored 100/100:
import java.util.HashSet;
class Solution {
public int solution(int X, int[] A) {
HashSet<Integer> hset = new HashSet<Integer>();
for (int i = 0 ; i < A.length; i++) {
if (A[i] <= X)
hset.add(A[i]);
if (hset.size() == X)
return i;
}
return -1;
}
}
Simple solution 100%
public int solution(final int X, final int[] A) {
Set<Integer> emptyPosition = new HashSet<Integer>();
for (int i = 1; i <= X; i++) {
emptyPosition.add(i);
}
// Once all the numbers are covered for position, that would be the
// moment when the frog will jump
for (int i = 0; i < A.length; i++) {
emptyPosition.remove(A[i]);
if (emptyPosition.size() == 0) {
return i;
}
}
return -1;
}
Here's my solution.
It isn't perfect, but it's good enough to score 100/100.
(I think that it shouldn't have passed a test with a big A and small X)
Anyway, it fills a new counter array with each leaf that falls
counter has the size of X because I don't care for leafs that fall farther than X, therefore the try-catch block.
AFTER X leafs fell (because it's the minimum amount of leafs) I begin checking whether I have a complete way - I'm checking that every int in count is greater than 0.
If so, I return i, else I break and try again.
public static int solution(int X, int[] A){
int[] count = new int[X];
for (int i = 0; i < A.length; i++){
try{
count[A[i]-1]++;
} catch (ArrayIndexOutOfBoundsException e){ }
if (i >= X - 1){
for (int j = 0; j< count.length; j++){
if (count[j] == 0){
break;
}
if (j == count.length - 1){
return i;
}
}
}
}
return -1;
}
Here's my solution with 100 / 100.
public int solution(int X, int[] A) {
int len = A.length;
if (X > len) {
return -1;
}
int[] isFilled = new int[X];
int jumped = 0;
Arrays.fill(isFilled, 0);
for (int i = 0; i < len; i++) {
int x = A[i];
if (x <= X) {
if (isFilled[x - 1] == 0) {
isFilled[x - 1] = 1;
jumped += 1;
if (jumped == X) {
return i;
}
}
}
}
return -1;
}
Here's what I have in C#. It can probably still be refactored.
We throw away numbers greater than X, which is where we want to stop, and then we add numbers to an array if they haven't already been added.
When the count of the list has reached the expected number, X, then return the result. 100%
var tempArray = new int[X+1];
var totalNumbers = 0;
for (int i = 0; i < A.Length; i++)
{
if (A[i] > X || tempArray.ElementAt(A[i]) != 0)
continue;
tempArray[A[i]] = A[i];
totalNumbers++;
if (totalNumbers == X)
return i;
}
return -1;
below is my solution. I basically created a set which allows uniques only and then go through the array and add every element to set and keep a counter to get the sum of the set and then using the sum formula of consecutive numbers then I got 100% . Note : if you add up the set using java 8 stream api the solution is becoming quadratic and you get %56 .
public static int solution2(int X, int[] A) {
long sum = X * (X + 1) / 2;
Set<Integer> set = new HashSet<Integer>();
int setSum = 0;
for (int i = 0; i < A.length; i++) {
if (set.add(A[i]))
setSum += A[i];
if (setSum == sum) {
return i;
}
}
return -1;
}
My JavaScript solution that got 100 across the board. Since the numbers are assumed to be in the range of the river width, simply storing booleans in a temporary array that can be checked against duplicates will do. Then, once you have amassed as many numbers as the quantity X, you know you have all the leaves necessary to cross.
function solution(X, A) {
covered = 0;
tempArray = [];
for (let i = 0; i < A.length; i++) {
if (!tempArray[A[i]]) {
tempArray[A[i]] = true;
covered++
if(covered === X) return i;
}
}
return -1;
}
Here is my answer in Python:
def solution(X, A):
# write your code in Python 3.6
values = set()
for i in range (len(A)):
if A[i]<=X :
values.add(A[i])
if len(values)==X:
return i
return -1
Just tried this problem as well and here is my solution. Basically, I just declared an array whose size is equal to position X. Then, I declared a counter to monitor if the necessary leaves have fallen at the particular spots. The loop exits when these leaves have been met and if not, returns -1 as instructed.
class Solution {
public int solution(int X, int[] A) {
int size = A.length;
int[] check = new int[X];
int cmp = 0;
int time = -1;
for (int x = 0; x < size; x++) {
int temp = A[x];
if (temp <= X) {
if (check[temp-1] > 0) {
continue;
}
check[temp - 1]++;
cmp++;
}
if ( cmp == X) {
time = x;
break;
}
}
return time;
}
}
It got a 100/100 on the evaluation but I'm not too sure of its performance. I am still a beginner when it comes to programming so if anybody can critique the code, I would be grateful.
Maybe it is not perfect but its straightforward. Just made a counter Array to track the needed "leaves" and verified on each iteration if the path was complete. Got me 100/100 and O(N).
public static int frogRiver(int X, int[] A)
{
int leaves = A.Length;
int[] counter = new int[X + 1];
int stepsAvailForTravel = 0;
for(int i = 0; i < leaves; i++)
{
//we won't get to that leaf anyway so we shouldnt count it,
if (A[i] > X)
{
continue;
}
else
{
//first hit!, keep a count of the available leaves to jump
if (counter[A[i]] == 0)
stepsAvailForTravel++;
counter[A[i]]++;
}
//We did it!!
if (stepsAvailForTravel == X)
{
return i;
}
}
return -1;
}
This is my solution. I think it's very simple. It gets 100/100 on codibility.
set.contains() let me eliminate duplicate position from table.
The result of first loop get us expected sum. In the second loop we get sum of input values.
class Solution {
public int solution(int X, int[] A) {
Set<Integer> set = new HashSet<Integer>();
int sum1 = 0, sum2 = 0;
for (int i = 0; i <= X; i++){
sum1 += i;
}
for (int i = 0; i < A.length; i++){
if (set.contains(A[i])) continue;
set.add(A[i]);
sum2 += A[i];
if (sum1 == sum2) return i;
}
return -1;
}
}
Your algorithm is perfect except below code
Your code returns value only if list[iii] matches with searchedValue.
The algorithm must be corrected in such a way that, it returns the value if sum == n * ( n + 1) / 2.
import java.util.ArrayList;
import java.util.List;
class Solution {
public int solution(int X, int[] A) {
int list[] = A;
int sum = 0;
int searchedValue = X;
int sumV = searchedValue * (searchedValue + 1) / 2;
List<Integer> arrayList = new ArrayList<Integer>();
for (int iii = 0; iii < list.length; iii++) {
if (list[iii] <= searchedValue && !arrayList.contains(list[iii])) {
sum += list[iii];
if (sum == sumV) {
return iii;
}
arrayList.add(list[iii]);
}
}
return -1;
}
}
I think you need to check the performance as well. I just ensured the output only
This solution I've posted today gave 100% on codility, but respectivly #rafalio 's answer it requires K times less memory
public class Solution {
private static final int ARRAY_SIZE_LOWER = 1;
private static final int ARRAY_SIZE_UPPER = 100000;
private static final int NUMBER_LOWER = ARRAY_SIZE_LOWER;
private static final int NUMBER_UPPER = ARRAY_SIZE_UPPER;
public static class Set {
final long[] buckets;
public Set(int size) {
this.buckets = new long[(size % 64 == 0 ? (size/64) : (size/64) + 1)];
}
/**
* number should be greater than zero
* #param number
*/
public void put(int number) {
buckets[getBucketindex(number)] |= getFlag(number);
}
public boolean contains(int number) {
long flag = getFlag(number);
// check if flag is stored
return (buckets[getBucketindex(number)] & flag) == flag;
}
private int getBucketindex(int number) {
if (number <= 64) {
return 0;
} else if (number <= 128) {
return 1;
} else if (number <= 192) {
return 2;
} else if (number <= 256) {
return 3;
} else if (number <= 320) {
return 4;
} else if (number <= 384) {
return 5;
} else
return (number % 64 == 0 ? (number/64) : (number/64) + 1) - 1;
}
private long getFlag(int number) {
if (number <= 64) {
return 1L << number;
} else
return 1L << (number % 64);
}
}
public static final int solution(final int X, final int[] A) {
if (A.length < ARRAY_SIZE_LOWER || A.length > ARRAY_SIZE_UPPER) {
throw new RuntimeException("Array size out of bounds");
}
Set set = new Set(X);
int ai;
int counter = X;
final int NUMBER_REAL_UPPER = min(NUMBER_UPPER, X);
for (int i = 0 ; i < A.length; i++) {
if ((ai = A[i]) < NUMBER_LOWER || ai > NUMBER_REAL_UPPER) {
throw new RuntimeException("Number out of bounds");
} else if (ai <= X && !set.contains(ai)) {
counter--;
if (counter == 0) {
return i;
}
set.put(ai);
}
}
return -1;
}
private static int min(int x, int y) {
return (x < y ? x : y);
}
}
This is my solution it got me 100/100 and O(N).
public int solution(int X, int[] A) {
Map<Integer, Integer> leaves = new HashMap<>();
for (int i = A.length - 1; i >= 0 ; i--)
{
leaves.put(A[i] - 1, i);
}
return leaves.size() != X ? -1 : Collections.max(leaves.values());
}
This is my solution
public func FrogRiverOne(_ X : Int, _ A : inout [Int]) -> Int {
var B = [Int](repeating: 0, count: X+1)
for i in 0..<A.count {
if B[A[i]] == 0 {
B[A[i]] = i+1
}
}
var time = 0
for i in 1...X {
if( B[i] == 0 ) {
return -1
} else {
time = max(time, B[i])
}
}
return time-1
}
A = [1,2,1,4,2,3,5,4]
print("FrogRiverOne: ", FrogRiverOne(5, &A))
Actually I re-wrote this exercise without seeing my last answer and came up with another solution 100/100 and O(N).
public int solution(int X, int[] A) {
Set<Integer> leaves = new HashSet<>();
for(int i=0; i < A.length; i++) {
leaves.add(A[i]);
if (leaves.contains(X) && leaves.size() == X) return i;
}
return -1;
}
I like this one better because it is even simpler.
This one works good on codality 100% out of 100%. It's very similar to the marker array above but uses a map:
public int solution(int X, int[] A) {
int index = -1;
Map<Integer, Integer> map = new HashMap();
for (int i = 0; i < A.length; i++) {
if (!map.containsKey(A[i])) {
map.put(A[i], A[i]);
X--;
if (X == 0) {index = i;break;}
}
}
return index;
}
%100 with js
function solution(X, A) {
let leafSet = new Set();
for (let i = 0; i < A.length; i += 1) {
if(A[i] <= 0)
continue;
if (A[i] <= X )
leafSet.add(A[i]);
if (leafSet.size == X)
return i;
}
return -1;
}
With JavaScript following solution got 100/100.
Detected time complexity: O(N)
function solution(X, A) {
let leaves = new Set();
for (let i = 0; i < A.length; i++) {
if (A[i] <= X) {
leaves.add(A[i])
if (leaves.size == X) {
return i;
}
}
}
return -1;
}
100% Solution using Javascript.
function solution(X, A) {
if (A.length === 0) return -1
if (A.length < X) return -1
let steps = X
const leaves = {}
for (let i = 0; i < A.length; i++) {
if (!leaves[A[i]]) {
leaves[A[i]] = true
steps--
}
if (steps === 0) {
return i
}
}
return -1
}
C# Solution with 100% score:
using System;
using System.Collections.Generic;
class Solution {
public int solution(int X, int[] A) {
// go through the array
// fill a hashset, until the size of hashset is X
var set = new HashSet<int>();
int i = 0;
foreach (var a in A)
{
if (a <= X)
{
set.Add(a);
}
if (set.Count == X)
{
return i;
}
i++;
}
return -1;
}
}
https://app.codility.com/demo/results/trainingXE7QFJ-TZ7/
I have a very simple solution (100% / 100%) using HashSet. Lots of people check unnecessarily whether the Value is less than or equal to X. This task cannot be otherwise.
public static int solution(int X, int[] A) {
Set<Integer> availableFields = new HashSet<>();
for (int i = 0; i < A.length; i++) {
availableFields.add(A[i]);
if (availableFields.size() == X){
return i;
}
}
return -1;
}
public static int solutions(int X, int[] A) {
Set<Integer> values = new HashSet<Integer>();
for (int i = 0; i < A.length; i++) {
if (values.add(A[i])) {
X--;
}
if (X == 0) {
return i;
}
}
return -1;
}
This is my solution. It uses 3 loops but is constant time and gets 100/100 on codibility.
class FrogLeap
{
internal int solution(int X, int[] A)
{
int result = -1;
long max = -1;
var B = new int[X + 1];
//initialize all entries in B array with -1
for (int i = 0; i <= X; i++)
{
B[i] = -1;
}
//Go through A and update B with the location where that value appeared
for (int i = 0; i < A.Length; i++)
{
if( B[A[i]] ==-1)//only update if still -1
B[A[i]] = i;
}
//start from 1 because 0 is not valid
for (int i = 1; i <= X; i++)
{
if (B[i] == -1)
return -1;
//The maxValue here is the earliest time we can jump over
if (max < B[i])
max = B[i];
}
result = (int)max;
return result;
}
}
Short and sweet C++ code. Gets perfect 100%... Drum roll ...
#include <set>
int solution(int X, vector<int> &A) {
set<int> final;
for(unsigned int i =0; i< A.size(); i++){
final.insert(A[i]);
if(final.size() == X) return i;
}
return -1;
}

Categories

Resources