find the range over a incline - java

goodday i am having an issue with my code. i am trying to iterate over an arraylist and see for which indices the values are increasing.
lines = [5, 7, 10, 11, 8, 6, 5, 4, 7, 8]
for (int i = 0; i < lines.size(); i++) { //iterate over array
// System.out.println(lines.get(i) + " ");
if (i == 9){
break;
}else if (lines.get(i) < lines.get(i + 1)){
System.out.println((i) + "-" + (i+1));
}else {
continue;
}
}
and my output produces this:
0-1
1-2
2-3
7-8
8-9
however i want my output to look like this:
0-3
7-9

You print directly after the if presence.
You would have to add another query after the first if. Something like this should solve the problem
`for (int i = 0; i < lines.size(); i++) { //iterate over array
// System.out.println(lines.get(i) + " ");
if (i == 9){
break;
}else if (lines.get(i) < lines.get(i + 1)){
if(lines.get(i) > lines.get(i+1){
System.out.println((i) + "-" + (i+1));
}
}else {
continue;
}
}`

You are printing every line, so it is expected the output is moving unit by unit.
What you can do is create a boolean that is initially set to false. Then, it will be set to true if the condition lines.get(i) < lines.get(i + 1) is true, and store the index i.
It will then change to false again whenever the condition lines.get(i) < lines.get(i + 1) is not true. Store the index again and only then print.

Trivial solution:
private static void compare( int start, int end )
{
if( start != end )
{
System.out.println( start + " - " + end );
}
}
public static void main(String args[]) {
int[] lines = { 5, 7, 10, 11, 8, 6, 5, 4, 7, 8 };
int start = 0;
for( int i = 0; i < lines.length - 1; i++ )
{
if( lines[i] >= lines[i + 1] )
{
compare( start, i );
start = i + 1;
}
}
compare( start, lines.length - 1 );
}
This can very likely be further optimized.

The code in your question is not a minimal, reproducible example so I understand that lines is a List. However you are accessing it as if it were an array so in the code, below, I used an array of int rather than a list of Integer.
The best way to understand how the code works is to run it through a debugger. If you are using an IDE, like IntelliJ or Eclipse, then it has a debugger.
Alternatively do a walk through of the code and write down the values of the variables as they change. This is known as debugging by hand.
int[] lines = new int[]{5, 7, 10, 11, 8, 6, 5, 4, 7, 8};
int end = lines.length - 1;
int lower = 0;
int upper = 0;
for (int i = 0; i < end; i++) { // iterate over array
if (lines[i] < lines[i + 1]) {
if (lower == upper) {
lower = i;
}
upper = i + 1;
}
else {
if (upper > lower) {
System.out.println(lower + "-" + upper);
}
lower = upper = i;
}
}
if (upper > lower) {
System.out.println(lower + "-" + upper);
}
The limit of the for loop must be one less than the size of the array, otherwise the lines[i + 1], on the last iteration of the loop, will be greater than the index of the last element in lines and that will cause an exception to be thrown.
After the for loop terminates, you need to check the values of lower and upper to see whether the last elements of lines are increasing. Hence the if statement after the for loop.
When I run the above code I get the following output:
0-3
7-9
I understand that this is your desired output.

Although you are working in Java this is more of an algorithm question than something specific to the language.
You'll need to keep track of the beginning of an incline as an independent variable. This is initially null because you do not know if the ArrayList begins with an incline or decline (or steady?). Since you want to keep track of the indexes, you'll need to iterate via the index - which you're already doing.
You are iterating from 1. This makes sense as it allows you to compare the current with the previous. Remove the break on the index 9 - this will cripple your algorithm if the number of elements varies. You should not make assumptions that the data your instructor will test with will be the same as the sample provided. They will look for edge cases that break a naive algorithm.
On a given iteration there are four possible states
you were on an incline and it continues through index
you were on an incline but it ended at previous index
you were not on an incline but you incline from previous index to index
you were not on an incline and you are not incling from previous index to index
There are only two cases where you do something - when you either begin or end an incline. Note that the List may end on an incline so you need to check for that case after completing the iteration. This has the distinct odor of a class assignment so I'm not going to provide Java code but the follow pseudocode describes the general algorithm.
let beginning-of-incline be null
for each index starting with 1
let inclining = is there an incline from the previous element to this element
if inclining and beginning-of-incline is null
capture beginning-of-incline
else if not-inclining and beginning-of-incline is not null
record incline from beginning-of-incline to previous index
clear beginning-of-incline
end-if-else
end for-each-index
if beginning-of-incline is not null
record incline from beginning-of-incline to final index of ArrayList
endif

Related

How can I create a java program to find the amount of consecutive numbers in an array?

I'm trying to make a Java program to find the number of consecutive numbers in an array. For example, if an array has the values, 1,8,10,4,2,3 there are 4 numbers that are consecutive (1,2,3,4). I've created this program, but I'm getting an error on lines 28 and 31 for ArrayIndexOutOfBoundsException, how do I fix the error? (I'm not even sure if the program I made will work if the errors are fixed). Note: I know there are many solutions online for this but I'm a beginner programmer, and I'm trying to do this a more simple way.
import java.util.Arrays;
class Main {
public static void main(String[] args) {
consec();
}
static void consec()
{
int[] nums = {16, 4, 5, 200, 6, 7, 70, 8};
int counter=0;
Arrays.sort(nums);
for (int i=0; i < nums.length; i++)
if (i != nums.length - 1)
System.out.print(nums[i] + ", ");
else
System.out.print(nums[i]);
for (int i=0; i < nums.length; i++)
for (int j=i; j < nums.length - i; j++)
if (nums[j + 1] - 1 == nums[j])
counter++;
else if (nums[j+1]==counter)
System.out.print("Consective amount is" + counter);
}
}
The issue for the exception lies within the access of nums[j + 1].
Note that j can be as large as nums.length - 1 due to the for loop.
Thus j + 1 can be nums.length which is an OutOfBounds array index.
Secondly I don't think your code solves the task - for example you only print a result if the number of consecutive numbers you've counted appears within the array. However I don't see how these things should correlate.
You can solve the problem like this:
for (int i = 1; i < nums.length; i++) {
if (nums[i-1] == nums[i] - 1) {
counter+= 2;
int j = i + 1;
while (j < nums.length && nums[j] - 1 == nums[j-1]) {
j++;
counter++;
}
i = j;
}
}
System.out.print("Consective amount is" + counter);
Note that the index i starts at 1, thus we can be assured that nums[i-1] exists.
If nums has only one element we should not run into any issues as the condition i < nums.length would not be fulfilled. We count two consequitves for every start of a sequence and one addition element for every following consequtive (while loop).
When the sequence ends we try finding a new sequence behind it by moving the index i to the end of the last sequence (j = i).
The above code will sum multiple distinct sequences of consequtive numbers. For example the array [17,2,20,18,4,3] has five consequitve numbers (2,3,4 and 17,18)
The algorithm has a time colpexity within O(n) as we either increase i or j by at least on and skip i to j after each sequence.
I would recommend re-thinking your approach to scanning over the array. Ideally you should only require one for-loop for this problem.
I personally created a HashSet of Numbers, which cannot hold duplicates. From there, you can iterate from 1 to nums.length-1, and check if nums[i] - 1 == nums[i-1] (ie: if they're consecutive). If they are equal, you can add both numbers to the HashSet.
Finally, you actually have the set of consecutive numbers, but for this question, you can simply return the size of the set.
I strongly recommend you attempt this problem and follow my explanation. If you simply require the code, this is the method that I came up with.
public static int countConsecutive(int[] nums) {
Set<Integer> consecutive = new HashSet<>();
if (nums.length <= 1)
return 0;
Arrays.sort(nums);
for (int i = 1; i < nums.length; i++) {
if (nums[i] != nums[i - 1] + 1)
continue;
consecutive.add(nums[i]);
consecutive.add(nums[i - 1]);
}
return consecutive.size();
}
Here is another approach where sorting is not necessary. It uses a BitSet. And as in your example, is presumes positive numbers (BitSet doesn't permit setting negative positions).
int[] values = {4, 3, 10, 11, 6, 1, 4, 8, 7};
set the corresponding bit positions based on the values.
BitSet bits = new BitSet();
for (int i : values) {
bits.set(i);
}
Initialize some values for output, starting bit position, and the set length.
BitSet out = new BitSet();
int start = 0;
int len = bits.length();
Now iterate over the bit set finding the range of bits which occupy adjacent positions. Those will represent the consecutive sequences generated when populating the original BitSet. Only sequences of two or more are displayed.
while (start < len) {
start = bits.nextSetBit(start);
int end = bits.nextClearBit(start+1);
if (start != end-1) {
// populate the subset for output.
out.set(start,end);
System.out.println(out);
}
out.clear();
start = end;
}
prints
{3, 4}
{6, 7, 8}
{10, 11}
If you just want the largest count, independent of the actual values, it's even simpler. Just use this in place of the above after initializing the bit set.
int len = bits.length();
int total = 0;
while (start < len) {
start = bits.nextSetBit(start);
int end = bits.nextClearBit(start + 1);
if (end - start > 1) {
total += end - start;
}
start = end;
}
System.out.println(total);

What's The Best Data Structure For Ignoring Or Removing Some Of Elements In DataSet In Java?

I have a set of objects and i wanna check out each of elements with previous elements.
In each time if(thisElement % previousElement == 0) I want to ignoring previousElement to prevent checking again because of time complexity.
How can i decrease size of DataSet if condition is true?
Is it better to marking and moving previousElement to first of DataSet or removing it at all?
I used ArrayList of objects but i couldn't move or remove it.I also used Iterator but it should have one loop inside another and it's complicated.How can i implement this algorithm in optimal time?
i want to move or remove previousElement in O(1).
this is my code:
public static int count_max(ArrayList<Node> nodes){
int max = Integer.MIN_VALUE;
int counter = 0;
System.err.println("nodes: "+nodes);
for(Node n : nodes){
int index = nodes.indexOf(n);
for(int i = index - 1 ; i >= 0 && nodes.get(i).mark == 0 ; i--){
counter++;
if(n.number % nodes.get(i).number == 0){
n.pred = i;
nodes.get(i).mark = 1;
n.count = nodes.get(i).count + 1;
if(n.count > max)
max = n.count;
//nodes.remove(i);
break;
}
}
}
return max;
}
the problem is find maximum count of divisible numbers given for example consider [3, 4, 6, 8 ,10, 18 ,21 ,24] are given. the answer is 3 because of [3 , 6 , 18 ] or [4 , 8 , 24] all of members should be divisible what's your solution to find this in o(nlogn) or a bit bigger.i don't want in o(n^2)
It could have been better if you had provided the logic you impemented but, based on your explanation, what I understood is, you want to remove the Previous Element if it is completely dividing Current Element from the List. If that so, below mentioned way is easy to follow
MainClass.class
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(4);
list.add(8);
list.add(10);
list.add(9);
int i = 1;
System.out.println("Size of List: " + list.size());
while (i < list.size()) {
System.out.println("i : " + i);
System.out.println("Values : " + list.get(i) + ", " + list.get(i - 1));
if (list.get(i) % list.get(i - 1) == 0) {
System.out.println("Removing: " + list.get(i - 1));
list.remove(i - 1);
i = 1;
} else {
i = i + 1;
}
}
System.out.println("After removing elements(if any), size is: " + list.size());
for (int val : list) {
System.out.print(val + " ");
}
}
what this code doing is, it starts from i=1 and it keeps on checking whether (i-1)th value divides ith value or not. If (i-1)th is dividing ith value then, i is set to "1" because we need to start from beginning and maintain the condition i.e.., (i-1)th value should not divide ith value. And you can guess that, if any value is not divisible by it's previous element then, we simply increment the i till it is less than the size of the List.
Note: I assumed that you want to maintain above mentioned condition throughout the List
I hope this might help you in some way to solve your issue. And this post can be edited if anyone feels needs to be.

Removing elements in ArrayList with certain size

So I'm currently stuck on one of my programs. I have to use a for loop to remove all elements in the ArrayList that's greater than 4. I have no idea how to do this. Someone help?
Here is my code:
import java.util.ArrayList;
public class P4E
{
public static void main( String args[] )
{
ArrayList<String> universities = new ArrayList<String>();
universities.add("Princeton");
universities.add("UCSD");
universities.add("UCLA");
universities.add("SDSU");
universities.add("UCI");
int size = universities.size();
System.out.println( "The size of the ArrayList is: " + size);
System.out.println("");
System.out.println("Now using a for-each loop");
System.out.println("");
for (String a : universities)
{
System.out.println( a );
}//end for-each
System.out.println( "" );
System.out.println( "Now using a for loop" );
System.out.println( "" );
for (int i = 0; i < universities.size(); i++)
{
if (universities.get(i) > 4 )
{
universities.remove(i);
}//end if-statement
}//end for loop
}//end main
}
Use the Java 8 removeIf default method on List:
universities.removeIf(u -> u.length() > 4);
If you're trying to remove elements that are larger than 4 characters, this will work:
for (int i = 0; i < universities.size(); i++)
{
System.out.println("checking "+universities.get(i));
if (universities.get(i).length() > 4 )
{
universities.remove(i);
i--;
}//end if-statement
}//end for loop
The .length() is necessary because you're interested in the size of the string, you can't compare a string to an int directly. The i--; ensures that you don't skip over elements as the size of the list changes.
The System.out.println is to show you what elements it is checking, so you can see that you're walking through the list correctly.
Instead of if(universities.get(i) > 4)
Do this:
for (int i = size; i >= 0; i--)
{
if (i > 4 )
universities.remove(i); // Shorten to only 1 line instead of brackets
}//end for loop
Reason
You want to remove the university at point i in this case, not get the university object. When you call universities.get(i) it returns the Object in the universities ArrayList
With current code problem is in the fact that you increasing i every time, while sometimes you remove element which cause indexes for elements to the left to be shifted. This results in the fact that you skip check for some elements. Example:
Initial array: [5,6,7], index i = 0.
You removed element at index 0 - array became [6,7], then you increase i and it became 1 - so index for next element to check is 1 and this reffer to 7, not to 6. Result - you didn't check 6.
Solutions:
1) Add i--; immediately after universities.remove(i);
2) Iterate in descending order: for (int i = 0; i < universities.size(); i++) -> for (int i = universities.size() - 1; i >= 0; i--)
3) Use java streams:
universities = universities.stream()
.filter(e -> e > 4)
.collect(Collectors.toList());
The problem with your code is that i advances to the next value even after a remove() call, meaning that your code will skip some items.
Here is what's happening: let's say you want to delete all numbers 4 and above from this list: {9 5 7 4 3}. When i is zero, your algorithm will find 9 and remove it. Now i is 1, but your list is {5 7 4 3}, so 5 at index zero will never be checked again.
One solution is to iterate in reverse order:
for (int i = universities.size() - 1; i >= 0 ; i--) {
if (universities.get(i).length() > 4 ) {
universities.remove(i);
}//end if-statement
}//end for loop
Now that i goes down instead of going up it does not matter whether the list shrinks or not.
What exactly do you mean by greater than 4? If you meant length of the String greater than 4, then here is the solution. Replace the 2nd for loop with the code below.
int i = 0;
while(i<universities.size()) {
if (universities.get(i).length() > 4 ) {
universities.remove(i);
} else {
i++;
}
}

Check if each letter in ArrayList<String> is a certain character?

Firstly, I'm SUPER new to stackoverflow (so excuse my ugly formatting), and have only a few years of programming experience (about 6 programming classes total)
I'm working on a hangman project. I've elected to use a StringBuilder for some of it. For example, I want to check what index or indices the char chosenLetter = 'C' is in the StringBuilder chosenWord = new StringBuilder("Ceramics");
Ideally, I would have a for loop to go through the StringBuilder word "Ceramics" and tell me the indices 0 and 7. I absolutely cannot figure it out. I've tried using the indexOf() function, but for some reason if I do
for (int i = 0; i < chosenWord.length(); i++) {
if (chosenWord.indexOf(chosenLetter, i) != -1) {
System.out.println("This should be a match at index " + i);
}
}
It would print out for example:
This should be a match at index 0
This should be a match at index 1
This should be a match at index 2
This should be a match at index 3
This should be a match at index 4
This should be a match at index 5
This should be a match at index 6
This should be a match at index 7
This should be a match at index 8
Instead of
This should be a match at index 0
This should be a match at index 7
Any ideas how to go about doing this? Thanks.
And again, sorry for the ugly format. If there's any questions on clarification I would be glad to try to clarify!
Check this out
for (int i = 0; i < chosenWord.length(); i++) {
if (chosenWord.charAt(i) == chosenLetter) {
System.out.println("This should be a match at index " + i);
}
}
This will print your desired out put
You can use StringBuilder#indexOf(String, int), but you need to be sure to increment the index position after each successful match, otherwise you will end up in a infinite loop...
StringBuilder sb = new StringBuilder("ceramics");
int index = 0;
while ((index = sb.indexOf("c", index)) >= 0) {
System.out.println(index);
index++;
}
Should also beware, that this is a case sensitive search, as you can see, I've made the text all lower case so I can catch positions 0 AND 6
The basic issue with your first attempt is that you are actually ignoring the result of indexOf
if (chosenWord.indexOf(chosenLetter, i) != -1) {
System.out.println("This should be a match at index " + i);
But then print i, so on each iteration, you are getting a result of either 0 or 6, so hence the reason it prints results for 0 - 6
So...
i = 0; chosenWord.indexOf(chosenLetter, i) == 6, print 0
i = 1; chosenWord.indexOf(chosenLetter, i) == 6, print 1
i = 2; chosenWord.indexOf(chosenLetter, i) == 6, print 2
i = 3; chosenWord.indexOf(chosenLetter, i) == 6, print 3
i = 4; chosenWord.indexOf(chosenLetter, i) == 6, print 4
i = 5; chosenWord.indexOf(chosenLetter, i) == 6, print 5
i = 6; chosenWord.indexOf(chosenLetter, i) == 6, print 6
i = 7; chosenWord.indexOf(chosenLetter, i) == -1, ignored
You actually don't need the outter loop, you just need to keep looping until indexOf is < 0, as demonstrated above...
The reason why you are seeing that output is because indexOf will return 0 for i = 0 and 6 for i = 1 to 7. In all those cases, the return is different from -1, which satisfies the if statement. Consider using charAt, in the same way suggested by #ppuskar.

How to find minimum number of jumps to reach the end of the array in O(n) time

Question
Given an array of integers where each element represents the max number of steps that can be made forward from that element.
Write a function to return the minimum number of jumps to reach the
end of the array (starting from the first element). If an element is
0, then cannot move through that element.
Example
Input: arr[] = {1, 3, 5, 8, 9, 2, 6, 7, 6, 8, 9}
Output: 3 (1-> 3 -> 8 ->9)
Found multiple ways from Dynamic Programming approach to other linear approaches. I am not able to understand the approach which is said to linear in time. HERE is the link where a linear approach is proposed.
I am not able to understand it at all. What I could understand is that author is suggesting to do a greedy approach and see if we reach end .. if not then backtrack ?
The time complexity of the solution proposed on the site is linear because you only iterate over the array once. The algorithm avoids the inner iteration of my proposed solution by using some clever tricks.
The variable maxReach stores at all time the maximal reachable position in the array. jump stores the amount of jumps necessary to reach that position. step stores the amount of steps we can still take (and is initialized with the amount of steps at the first array position)
During the iteration, the above values are updated as follows:
First we test whether we have reached the end of the array, in which case we just need to return the jump variable.
Next we update the maximal reachable position. This is equal to the maximum of maxReach and i+A[i] (the number of steps we can take from the current position).
We used up a step to get to the current index, so steps has to be decreased.
If no more steps are remaining (i.e. steps=0, then we must have used a jump. Therefore increase jump. Since we know that it is possible somehow to reach maxReach, we initialize the steps to the amount of steps to reach maxReach from position i.
public class Solution {
public int jump(int[] A) {
if (A.length <= 1)
return 0;
int maxReach = A[0];
int step = A[0];
int jump = 1;
for (int i = 1; i < A.length; i++) {
if (i == A.length - 1)
return jump;
if (i + A[i] > maxReach)
maxReach = i + A[i];
step--;
if (step == 0) {
jump++;
step = maxReach - i;
}
}
return jump;
}
}
Example:
int A[] = {1, 3, 5, 8, 9, 2, 6, 7, 6, 8, 9}
int maxReach = A[0]; // A[0]=1, so the maximum index we can reach at the moment is 1.
int step = A[0]; // A[0] = 1, the amount of steps we can still take is also 1.
int jump = 1; // we will always need to take at least one jump.
/*************************************
* First iteration (i=1)
************************************/
if (i + A[i] > maxReach) // 1+3 > 1, we can reach further now!
maxReach = i + A[i] // maxReach = 4, we now know that index 4 is the largest index we can reach.
step-- // we used a step to get to this index position, so we decrease it
if (step == 0) {
++jump; // we ran out of steps, this means that we have made a jump
// this is indeed the case, we ran out of the 1 step we started from. jump is now equal to 2.
// but we can continue with the 3 steps received at array position 2.
steps = maxReach-i // we know that by some combination of 2 jumps, we can reach position 4.
// therefore in the current situation, we can minimaly take 3
// more steps to reach position 4 => step = 3
}
/*************************************
* Second iteration (i=2)
************************************/
if (i + A[i] > maxReach) // 2+5 > 4, we can reach further now!
maxReach = i + A[i] // maxReach = 7, we now know that index 7 is the largest index we can reach.
step-- // we used a step so now step = 2
if (step==0){
// step
}
/*************************************
* Second iteration (i=3)
************************************/
if (i + A[i] > maxReach) // 3+8 > 7, we can reach further now!
maxReach = i + A[i] // maxReach = 11, we now know that index 11 is the largest index we can reach.
step-- // we used a step so now step = 1
if (step==0){
// step
}
/*************************************
* Third iteration (i=4)
************************************/
if (i + A[i] > maxReach) // 4+9 > 11, we can reach further now!
maxReach = i + A[i] // maxReach = 13, we now know that index 13 is the largest index we can reach.
step-- // we used a step so now step = 0
if (step == 0) {
++jump; // we ran out of steps, this means that we have made a jump.
// jump is now equal to 3.
steps = maxReach-i // there exists a combination of jumps to reach index 13, so
// we still have a budget of 9 steps
}
/************************************
* remaining iterations
***********************************
// nothing much changes now until we reach the end of the array.
My suboptimal algorithm which works in O(nk) time with n the number of elements in the array and k the largest element in the array and uses an internal loop over array[i]. This loop is avoided by the below algorithm.
Code
public static int minimum_steps(int[] array) {
int[] min_to_end = new int[array.length];
for (int i = array.length - 2; i >= 0; --i) {
if (array[i] <= 0)
min_to_end[i] = Integer.MAX_VALUE;
else {
int minimum = Integer.MAX_VALUE;
for (int k = 1; k <= array[i]; ++k) {
if (i + k < array.length)
minimum = Math.min(min_to_end[i+k], minimum);
else
break;
}
min_to_end[i] = minimum + 1;
}
}
return min_to_end[0];
}
Here is the basic intuition regarding the above problem's greedy approach and rest are the code requirements.
Given array is Input: a[] = {1, 3, 5, 8, 9, 2, 6, 7, 6, 8, 9}.
Now we start from the 1st element i.e i=0 and a[i] = 1. So seeing this we can take at most a jump of size 1, so since we don't have any other choice so we make this step happen.
Currently we are at i=1 and a[i]=3. So we currently can make a jump of size 3, but instead we consider all possible jumps we can make from the current location and attain the maximum distance which is within bounds(of the array). So what are our choices? we can make a jump of 1 step, or 2 steps or 3 steps. So we investigate from current location for each size jumps and choose the one which can take us maximum further into the array.
Once we have decided, which one we stick to, we take that jump size and update our number of jumps made so far and also where we can reach at most and how many steps we have now to decide our next move. And that's it. This is how finally we select the best option linearly traversing the array.
So this is the basic idea of the algo you might be looking for, next is to code it for the algorithm to work. Cheers!
Hope somebody time travels and finds the intuition helpful !! :) :P
"Years late to the party" #Vasilescu Andrei - well said. Sometimes it feels to me that we are time travelers.
Many of the answers here so far are great, but I feel I can help explain why the algorithm is correct and the intuition behind it.
I like this problem because it's one where the intuitive dynamic programming approach runs in O(n^2) worst-case, and a greedy approach (the one that motivated this question) runs in O(n) worst-case (it actually only visits each element of the array once). This algorithm is also for me somewhat reminiscent of Dijkstra's algorithm which solves another single-source shortest-path problem and that is also greedy.
To start, remember from the problem statement that A[i] holds the maximum position you can jump to from that index, but you can take a shorter jump from i if A[i]>1, so a shortest sequence of jumps from i=0 could be one with shorter jumps than what's allowed on each index. This is important, since you will see that the algorithm never considers those smaller jumps or their locations explicitly.
Second, it helps to think of the algorithm that you mentioned as one that gives itself "strands of rope" (steps = maxReach - i;) to reach the end, and that it consumes this rope (steps--;) as it tries to advance through the array.
Third, note that the algorithm is not keeping track of the specific indices i that may be part of a shortest sequence from the beginning to the end of the input array A. In fact, the algorithm only increases the variable jump (it gives itself a new strand of rope) when it "runs out of rope" (from the previous strand), so that it can keep iterating in the main loop to "try" to reach the end.
More specifically for the algorithm to be correct it needs to:
Keep track of "how far it can reach" (maxReach) from each location i as it moves forward through the array. Note that this quantity is updated for each location even if it's clear already at that moment that reaching that new location will require it to take more "jumps" as you exceed the number of steps (i.e. you run out of rope) that you gave yourself earlier, even if no shortest path would actually visit that element. The goal of these updates is to identify how far the next jump could reach so that it can give itself that much rope once it exhausted the current one.
Account for the minimum number of jumps (jumps) you must take if you want to continue iterating through the array to reach the end, as you run out of rope (steps) from the previous strand.
The algorithm that you linked to, for reference:
public class Solution {
public int jump(int[] A) {
if (A.length <= 1)
return 0;
int maxReach = A[0];
int step = A[0];
int jump = 1;
for (int i = 1; i < A.length; i++) {
if (i == A.length - 1)
return jump;
if (i + A[i] > maxReach)
maxReach = i + A[i];
step--;
if (step == 0) {
jump++;
step = maxReach - i;
}
}
return jump;
}
}
Years late to the party , but here is another O(n) solution that made sense for me.
/// <summary>
///
/// The actual problem is if it's worth not to jump to the rightmost in order to land on a value that pushes us further than if we jumped on the rightmost.
///
/// However , if we approach the problem from the end, we go end to start,always jumping to the leftmost
///
/// with this approach , these is no point in not jumping to the leftmost from end to start , because leftmost will always be the index that has the leftmost leftmost :) , so always choosing leftmost is the fastest way to reach start
///
/// </summary>
/// <param name="arr"></param>
static void Jumps (int[] arr)
{
var LeftMostReacher = new int[arr.Length];
//let's see , for each element , how far back can it be reached from
LeftMostReacher[0] = -1; //the leftmost reacher of 0 is -1
var unReachableIndex = 1; //this is the first index that hasn't been reached by anyone yet
//we use this unReachableIndex var so each index's leftmost reacher is the first that was able to jump to it . Once flagged by the first reacher , new reachers can't be the leftmost anymore so they check starting from unReachableIndex
// this design insures that the inner loop never flags the same index twice , so the runtime of these two loops together is O(n)
for (int i = 0; i < arr.Length; i++)
{
int maxReach = i + arr[i];
for (; unReachableIndex <= maxReach && unReachableIndex < arr.Length; unReachableIndex++)
{
LeftMostReacher[unReachableIndex] = i;
}
}
// we just go back from the end and then reverse the path
int index = LeftMostReacher.Length - 1;
var st = new Stack<int>();
while (index != -1)
{
st.Push(index);
index = LeftMostReacher[index];
}
while (st.Count != 0)
{
Console.Write(arr[st.Pop()] + " ");
}
Console.WriteLine();
}
static void Main ()
{
var nrs = new[] { 1, 3, 5, 8, 9, 2, 6, 7, 6, 8, 9 };
Jumps(nrs);
}
Simple python code for the Minimum number of jumps to reach end problem.
ar=[1, 3, 6, 3, 2, 3, 6, 8, 9, 5]
minJumpIdx=0
res=[0]*len(ar)
i=1
while(i<len(ar) and i>minJumpIdx):
if minJumpIdx+ar[minJumpIdx]>=i:
res[i]=res[minJumpIdx]+1
i+=1
else:
minJumpIdx+=1
if res[-1]==0:
print(-1)
else:
print(res[-1])
Here is another linear solution. The code is longer than the one suggested in the leet code link, but I think it is easier to understand. It is based on a two observations: the number of steps required to reach the i + 1 position is never less than the number of steps required to reach the i position and each element each element assigns its value + 1 to i + 1 ... i + a[i] segment.
public class Solution {
public int jump(int[] a) {
int n = a.length;
// count[i] is the number of "open" segments with value i
int[] count = new int[n];
// the number of steps to reach the i-th position
int[] dp = new int[n];
Arrays.fill(dp, n);
// toDelete[i] is the list of values of segments
// that close in the i-th position
ArrayList<Integer>[] toDelete = new ArrayList[n];
for (int i = 0; i < n; i++)
toDelete[i] = new ArrayList<>();
// Initially, the value is 0(for the first element).
toDelete[0].add(0);
int min = 0;
count[0]++;
for (int i = 0; i < n; i++) {
// Finds the new minimum. It uses the fact that it cannot decrease.
while (min < n && count[min] == 0)
min++;
// If min == n, then there is no path. So we can stop.
if (min == n)
break;
dp[i] = min;
if (dp[i] + 1 < n) {
// Creates a new segment from i + 1 to i + a[i] with dp[i] + 1 value
count[dp[i] + 1]++;
if (i + a[i] < n)
toDelete[i + a[i]].add(dp[i] + 1);
}
// Processes closing segments in this position.
for (int deleted : toDelete[i])
count[deleted]--;
}
return dp[n - 1];
}
}
Complexity analysis:
The total number of elements in toDelete lists is O(n). It is the case because at each position i at most one element is added. That's why processing all elements in all toDelete lists requires linear time.
The min value can only increase. That's why the inner while loop makes at most n iterations in total.
The outer for loop obviously makes n iterations. Thus, the time complexity is linear.
Okay, it took me good amount of time to wrap my head around the O(n) algo, I will try to explain the logic to my best simplest possible:
At each "i" in the array, you know with that value what is the currentFarthest value, you can reach up to, & also the currentEnd value, whenever you hit the currentEnd value, you know its time to make a jump & update currentEnd with currentFarthest.
The picture below might help :
I have done this with Python.
Less complex code with simple terms. This might help you.
def minJump(a):
end=len(a)
count=0
i=a[0]
tempList1=a
while(i<=end):
if(i==0):
return 0
tempList1=a[count+1:count+i+1]
max_index=a.index(max(tempList1))
count+=1
i=a[max_index]
end=end-max_index
return count+1
Another O(n) Solution With the best explanation
The following solution provides with o(n) time complexity
For solving minimum jumps to reach the end of the array,
For every jump index, we consider need to evaluate the corresponding step values in the index and using the index value divides the array into sub-parts and find out the maximum steps covered index.
Following code and explanation will give you a clear idea:
In each sub-array find out the max distance covered index as the first part of the array, and the second array
Input Array : {1, 3, 5, 9, 6, 2, 6, 7, 6, 8, 9} -> index position starts with 0
Steps :
Initial step is considering the first index and incrementing the jump
Jump = 1
1, { 3, 5, 9, 6, 2, 6, 7, 6, 8, 9} -> 1 is considered as a first jump
next step
From the initial step there is only one step to move so
Jump = 2
1,3, { 5, 9, 6,2, 6, 7, 6, 8, 9} -> 1 is considered as a first jump
next step
Now we have a flexibility to choose any of {5,9,6} because of last step says we can move upto 3 steps
Consider it as a subarray, evaluate the max distance covers with each index position
As {5,9,6} index positions are {2,3,4}
so the total farther steps we can cover:
{7,12,10} -> we can assume it as {7,12} & {10} are 2 sub arrays where left part of arrays says max distance covered with 2 steps and right side array says max steps cover with remaining values
next step:
Considering the maximum distanc covered in first array we iterate the remaining next elements
1,3,9 {6,2, 6, 7, 6, 8, 9}
From above step ww already visited the 4th index we continue with next 5th index as explained above
{6,2, 6, 7, 6, 8, 9} index positions {4,5,6,7,8,9,10}
{10,7,12,14,14,17,19}
Max step covers here is 19 which corresponding index is 10
Code
//
// Created by Praveen Kanike on 07/12/20.
//
#include <iostream>
using namespace std;
// Returns minimum number of jumps
// to reach arr[n-1] from arr[0]
int minJumps(int arr[], int n)
{
// The number of jumps needed to
// reach the starting index is 0
if (n <= 1)
return 0;
// Return -1 if not possible to jump
if (arr[0] == 0)
return -1;
// stores the number of jumps
// necessary to reach that maximal
// reachable position.
int jump = 1;
// stores the subarray last index
int subArrEndIndex = arr[0];
int i = 1;
//maximum steps covers in first half of sub array
int subArrFistHalfMaxSteps = 0;
//maximum steps covers in second half of sub array
int subArrSecondHalfMaxSteps =0;
// Start traversing array
for (i = 1; i < n;) {
subArrEndIndex = i+subArrEndIndex;
// Check if we have reached the end of the array
if(subArrEndIndex >= n)
return jump;
int firstHalfMaxStepIndex = 0;
//iterate the sub array and find out the maxsteps cover index
for(;i<subArrEndIndex;i++)
{
int stepsCanCover = arr[i]+i;
if(subArrFistHalfMaxSteps < stepsCanCover)
{
subArrFistHalfMaxSteps = stepsCanCover;
subArrSecondHalfMaxSteps = 0;
firstHalfMaxStepIndex = i;
}
else if(subArrSecondHalfMaxSteps < stepsCanCover)
{
subArrSecondHalfMaxSteps = stepsCanCover;
}
}
if(i > subArrFistHalfMaxSteps)
return -1;
jump++;
//next subarray end index and so far calculated sub array max step cover value
subArrEndIndex = arr[firstHalfMaxStepIndex];
subArrFistHalfMaxSteps = subArrSecondHalfMaxSteps;
}
return -1;
}
// Driver program to test above function
int main()
{
int arr[] = {100, 3, 5, 9, 6, 2, 6, 7, 6, 8, 9};
int size = sizeof(arr) / sizeof(int);
// Calling the minJumps function
cout << ("Minimum number of jumps to reach end is %d ",
minJumps(arr, size));
return 0;
}
Just in case you need to write a python solution for the greedy approach this code will get you covered for the above problem :)
def minJumps(self, arr, n):
#code here
if(n <= 1):
return(0)
if(arr[0] == 0):
return -1
maxrange, step = arr[0], arr[0]
jumps = 1
for i in range(1,n):
if (i == len(arr) - 1):
return jumps
maxrange = max(maxrange, i+arr[i])
step -= 1
if(step == 0):
jumps += 1
if(i>=maxrange):
return -1
step = maxrange - i
return(jumps)
Here is another solution. In this solution, the worst-case complexity is O(n) while the average-case complexity is less than O(n). I don't know how to do average-case complexity analysis. So, I can't tell the exact average-case complexity. But yeah it is faster than 99.22% of submissions on leet code.
def minJumps(self, arr, n):
current_w=0 # current_index
last_w=n-1 # last_index
max_reach=0 # value of index upto which we have analysed array for optimum solution
max_jumps=arr[0] # maximum jumps that can be taken from a current_index
hop=0 # total jumps
while current_w<last_w:
max_jumps=arr[current_w]
if max_jumps==0:
return -1
if max_jumps==1:
max_reach=max_jumps+current_w
current_w+=1
elif max_jumps<last_w-current_w: # if maximum steps does not reach to last index
can_jump_to=arr[max_reach+1:max_jumps+current_w+1] # subarray in which we have to search for a wall,jumping to which can take us to required solution
jump_to=max(range(len(can_jump_to)),key=lambda x: x+can_jump_to[x])+max_reach+1 # finding index of wall whoose definition mentioned in above comment
max_reach=max_jumps+current_w #updating max_reach
current_w=jump_to #updating current position
else:
current_w=last_w
hop+=1
return hop
static void minJumps(int a[] , int n)
{
int dp[] = new int[n];
dp[0] = 0; //As the min jumps needed to get to first index is zero only.
//Fill the rest of the array with INT_MAX val so we can make math.min comparisions.
for(int i=1;i<n;i++)
dp[i] = Integer.MAX_VALUE;
for(int i=1;i<n;i++)
{
for(int j=0;j<i;j++)
{ //If we have enough jumps from the position j to reach i.
if(j+a[j]>=i)
{ //Take the min of current stored value & jumps req to
//reach i from j by getting jumps req to reach j plus 1.
//(Plus 1 because since we have enough jumps to reach 1 from j we
//simply add 1 by taking the jumps required to reach j.)
dp[i] = Math.min(dp[i],dp[j]+1);
}
}
}
//If first element has zero jumps in store or if the final jumps value
//becomes MAX value because there's an element in between which gives zero
//jumps.
if(a[0]==0 || dp[n-1] == Integer.MAX_VALUE )
System.out.println("-1");
else System.out.println(dp[n-1]);
}

Categories

Resources