Reduce time complexity of game program - java

Adam wants to watch a football game from the top of the building that is arranged in a straight line.
Adam can watch the football match from the top of ith building if
there exists a maximum of K[i] buildings in front of Adam with a
height less than or equal to the height of ith building.
If there is any building in front of Adam with a height more than
the height of ith position building then he cannot see the match
from this ith building.
Count the positions of buildings where Adam can see the match from the top of the buildings.
Example:
Both arrays have the same length.
B (Buildings) = [2,1,3] represents the height of buildings
K = [1,2,1]
Answer:
1
Explanation:
For B[0] = 2 we have K[0] = 1. The number of buildings in front of it that have a height smaller than or equal to 2 is 0. This is <= K[0] So Adam can see the match.
For B[1] = 1, we have K[1] = 2. The number of buildings in front of it that have a height smaller than or equal to 1 is 0. But B[0] = 2 so Adam cannot see the match.
For B[2] = 3, we have K[2] = 1. The number of buildings in front of it that have a height smaller than or equal to 3 is 2. But this value is >= K[2] i.e 1 so Adam cannot see the match
The total positions where Adam can see the match is 1.
Constraints:
Array size is 1 to 10^5
Each element in Arrays is 1 to 10^5
This is the code I tried with the time complexity of O(n^2):
public static int process(int[] buildings, int[] K) {
int n = buildings.length;
int answer = 0;
for(int i=0; i<n; i++) {
int count = 0;
boolean valid = true;
for(int j=i-1; j>=0; j--) {
if(buildings[j] <= buildings[i]) count++;
if (buildings[j] > buildings[i]) {
valid = false;
break;
}
}
if(valid && count <= K[i]) answer++;
}
return answer;
}
This program works for arrays of small size but fails for large arrays as the time complexity of my program is O(n^2).
What is the better approach to solve this and how can we reduce the time complexity?

you have 2 conditions which we look on one by one but we'll start from the second:
The second condition can be interpreted as if ith building is the highest building from any other building in front of it. this can be achieved by checking the max hight to the ith position and update it as you go.
if the second condition is true that's means you have i-1 buildings in front of the ith building that are equal or smaller than it (i instead of i-1 if you start to count from 0 like in array). so the first condition would be true only if k[i] is bigger than (i-1) you just need to compare between them.
here is the code in java:
import java.util.*;
class HelloWorld {
public static void main(String[] args) {
List<Integer> buildings = Arrays.asList(2, 1, 3);
List<Integer> K = Arrays.asList(1, 2, 1);
System.out.println(process(K, buildings));
}
public static Integer process(List<Integer> K, List<Integer> buildings){
Integer maxHightBuilding = buildings.get(0);
Integer sum = 0;
for(Integer i = 0; i < buildings.size(); i++){
if(buildings.get(i) >= maxHightBuilding ){
maxHightBuilding = buildings.get(i);
if(i <= K.get(i)){
sum++;
}
}
}
return sum;
}
}

I think if you retain the biggest value in front of the actual index, you can just check with the value and when your index passes the biggest value you can update it.
So find the biggest value from the end of the array.
HF!

Related

How to reduce time complexity of the code?

I have given a queue initial positions increment by 1 from 1 at the front of the line to n at the back.
Any person in the queue can bribe the person directly in front of them to swap positions. If two people swap positions, they still wear the same sticker denoting their original places in line. One person can bribe at most two others. For example, if n = 8 and Person 5 bribes Person 4, the queue will look like this:1,2,3,5,4,6,7,8.
minimumbribes must print an integer representing the minimum number of bribes necessary to create the original queue, or Too chaotic if the line configuration is not possible. If no of bribes for a person exceeds to 2 then the line configuration is not possible.
minimumBribes has the following parameter(s):
q: an array of integers.
1 <= n <= 100000.
Sample Input
2
5
2 1 5 3 4
5
2 5 1 3 4
Output
3
Too chaotic
Using the sorting logic I have just counted the person that comes early and are greater than following person. And it also prints the total no of bribes if the line configuration is possible else it prints Too chaotic message.
static void minimumBribes(int[] q) {
int count = 0; // Counts the no of bribes by an individual.
int j;
int total = 0; // Total no of bribes.
loop1 : for(int i = 0; i < q.length; i++) {
loop2 : for(j = i; j < q.length; j++) {
if(q[i] > q[j]) {
count++;
if(count > 2) {
System.out.println("Too chaotic");
break loop1;
}
}
else if (q[i] <= q[j]) {
total += count;
count = 0;
}
}
}
if(count <= 2)
System.out.println(total);
}
The upper mentioned code runs perfectly for a small queue but if an array has a large size, It exceeds the fixed time limit for processing.
I just solved this in python to get a better understanding of the problem.
I decided to start with the last person in the queue because there are a number of things we can immediately say about this person:
They can only bribe forward (i.e. they have no one behind them to bribe them)
They cannot bribe to be more than two positions away from the "EOQ" [, where EOQ means end of queue or initial position of this person].
We can apply this rule to everyone starting from person N to person 1.
Pseudocode:
FOR PERSON in [N to 1]:
P = PERSON
IF (P MORE THAN 2 POSITIONS FROM INITIAL POS; OR P IS FURTHER BACK IN QUEUE):
PRINT 'Too chaotic'
RETURN
FOR POS in [CURR_POS[P] to INIT_POS[P]]:
SWAP POSITION OF P AND PERSON_TO_THE_RIGHT_OF_P
INCREMENT COUNT OF BRIBES BY 1
PRINT COUNT_OF_BRIBES
Notes
INIT_POS[P] refers to the initial position of the person before any bribes took place, so INIT_POS[PERSON_5] = 5
CURR_POS[P] refers to the current position of P at the start of the algorithm or after any swaps.
At the end of this algorithm, (assuming the bribes were not chaotic) the following invariant should hold: CURR_POS[i] == i. The reason is because all we are doing is moving all the people back to their original positions before any bribes took place.
static void minimumBribes(int[] q) {
int count = 0; // Counts the no of bribes by an individual.
int total = 0; // Total no of bribes.
loop1 : for(int i = 0; i < q.length; i++) {
if((q[i]-(i+1))>2){//original position i should be person i+1
System.out.println("Too chaotic");
break loop1;
}
else if((q[i]-(i+1))>0){
count=q[i]-(i+1);
total += count;
count = 0;
}
}
if(count <= 2) //not sure what dose this sentence do?
System.out.println(total);
}
Is this what you ask? Not sure I understand you correctlly

Writing a method that outputs a different uniqe permutation of a number every time it's called

I got this interview question and I am still very confused about it.
The question was as the title suggest, i'll explain.
You are given a random creation function to use.
the function input is an integer n. let's say I call it with 3.
it should give me a permutation of the numbers from 1 - 3. so for example it will give me 2, 3 , 1.
after i call the function again, it won't give me the same permutation, now it will give me 1, 2, 3 for example.
Now if i will call it with n = 4. I may get 1,4,3,2.
Calling it with 3 again will not output 2,3,1 nor 1,2,3 as was outputed before, it will give me a different permutation out of the 3! possible permutations.
I was confused about this question there and I still am now. How is this possible within normal running time ? As I see it, there has to be some static variable that remembers what was called before or after the function finishes executing.
So my thought is creating a static hashtable (key,value) that gets the input as key and the value is an array of the length of the n!.
Then we use the random method to output a random instance out of these and move this instance to the back, so it will not be called again, thus keeping the output unique.
The space time complexity seems huge to me.
Am I missing something in this question ?
Jonathan Rosenne's answer was downvoted because it was link-only, but it is still the right answer in my opinion, being that this is such a well-known problem. You can also see a minimal explanation in wikipedia: https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order.
To address your space-complexity concern, generating permutations in lexicographical ordering has O(1) space complexity, you don't need to store nothing other than the current permutation. The algorithm is quite simple, but most of all, its correctness is quite intuitive. Imagine you had the set of all permutations and you order them lexicographically. Advancing to the next in order and then cycling back will give you the maximum cycle without repetitions. The problem with that is again the space-complexity, since you would need to store all possible permutations; the algorithm gives you a way to get the next permutation without storing anything. It may take a while to understand, but once I got it it was quite enlightening.
You can store a static variable as a seed for the next permutation
In this case, we can change which slot each number will be put in with an int (for example this is hard coded to sets of 4 numbers)
private static int seed = 0;
public static int[] generate()
{
//s is a copy of seed, and increment seed for the next generation
int s = seed++ & 0x7FFFFFFF; //ensure s is positive
int[] out = new int[4];
//place 4-2
for(int i = out.length; i > 1; i--)
{
int pos = s % i;
s /= i;
for(int j = 0; j < out.length; j++)
if(out[j] == 0)
if(pos-- == 0)
{
out[j] = i;
break;
}
}
//place 1 in the last spot open
for(int i = 0; i < out.length; i++)
if(out[i] == 0)
{
out[i] = 1;
break;
}
return out;
}
Here's a version that takes the size as an input, and uses a HashMap to store the seeds
private static Map<Integer, Integer> seeds = new HashMap<Integer, Integer>();
public static int[] generate(int size)
{
//s is a copy of seed, and increment seed for the next generation
int s = seeds.containsKey(size) ? seeds.get(size) : 0; //can replace 0 with a Math.random() call to seed randomly
seeds.put(size, s + 1);
s &= 0x7FFFFFFF; //ensure s is positive
int[] out = new int[size];
//place numbers 2+
for(int i = out.length; i > 1; i--)
{
int pos = s % i;
s /= i;
for(int j = 0; j < out.length; j++)
if(out[j] == 0)
if(pos-- == 0)
{
out[j] = i;
break;
}
}
//place 1 in the last spot open
for(int i = 0; i < out.length; i++)
if(out[i] == 0)
{
out[i] = 1;
break;
}
return out;
}
This method works because the seed stores the locations of each element to be placed
For size 4:
Get the lowest digit in base 4, since there are 4 slots remaining
Place a 4 in that slot
Shift the number to remove the data used (divide by 4)
Get the lowest digit in base 3, since there are 3 slots remaining
Place a 3 in that slot
Shift the number to remove the data used (divide by 3)
Get the lowest digit in base 2, since there are 2 slots remaining
Place a 2 in that slot
Shift the number to remove the data used (divide by 2)
There is only one slot remaining
Place a 1 in that slot
This method is expandable up to 12! for ints, 13! overflows, or 20! for longs (21! overflows)
If you need to use bigger numbers, you may be able to replace the seeds with BigIntegers

Increment through int[] as an int

So during a homework assignment I made a method that would increment through an integer array as if it were an integer. So what I mean by that is that the index of the int[] would represent the digits in a number.
for example:
int[] digits;
digits = new int[4]
this would be a 4 digit number. index 0 being the 1000's place, index 1 being the 100's place, index 2 being the 10's place and index 3 being the 1's place.
So the method I made increments through that array like this:
0000, 0001, 0002, 0003, 0004, --> 0010, 00011, ----> 9999.
public void increment(){
if(digits[3] <= 8){
digits[3]++;
}
else{
digits[3] = 0;
if(digits[2] <= 8){
digits[2]++;
}
else{
digits[2] = 0;;
if(digits[1] <= 8){
digits[1]++;
}
else{
digits[1] = 0;
if(digits[0] <= 8){
digits[0]++;
}
else{
digits[0] = 0;
}
}
}
}
}
My question is how would I go about making a method that does the same thing but the method requires an index to be passed in.
for example:
public void increment(int index){
//increments through index number of digits.
}
So something like this:
increment(7);
would yield this: 0000000, 0000001, 0000002, 0000003, --> 0000009, 0000010, 0000011, -----> 9999999.
This is personally for my own satisfaction so any help would be great =)
Aside: int [] = digits; is a syntax error; remove the =.
The increment routine needs access to the array of digits, and in Java arrays 'know' their bounds so you don't need a separate parameter or variable for the length. Given that, the classic hacker method (from the age of the dinosaurs when hacker meant good) is:
void increment (int[] digits){
for( int i = digits.length; --i>=0 && ++digits[i]==10; ) digits[i]=0;
}
# alter the 10 to change radix, or make it variable if desired
This mimics the procedure you learned in elementary school (or at least I did): start at the rightmost digit, add one, if it doesn't overflow you're done, if it does overflow change it to zero and move one place to the left except stop when you are already at the left end and can't move further.
Here's my implementation:
Live demo
Example for reference:
[0,0]
[0,1]
[0,2]
[0,3]
[0,4]
[0,5]
[0,6]
[0,7]
[0,8]
[0,9]
[1,0]
[1,1]
[1,2]
[1,3]
[1,4]
[1,5]
[1,6]
...
First create a 2D array where each row represent a number with n digits. We can get the 2D array height by doing 10^n because we have n digits and 10 possibilities for each digit (0-9)
We can notice a pattern in the numbers generated which is the rightmost column switches digit every one iteration, the second rightmost digit switches digit every 10 iterations, the third one switches digit each 100 iteration etc... so the kth rightmost index switches iteration every 10^k iterations. This is noted by the variable level. Also some iterations on the digits can be omitted because we know that some digits will not increase until a specific row (example the leftmost digit will not increase for row 1 if n > 1). You can get the width of digits to loop on by doing log10(row) which will give you the number of digits in the integer row minus one. This is called width in the code.
Then all we need to do is check if now it's time to switch row%level == 0 then we increment the value from above level by one, otherwise we just copy the same value from row above.
public static void numbers(int n) {
int nums[][] = new int[(int)Math.pow(10, n)][n];
for(int row=1; row < nums.length; row++) {
int width = (int) Math.log10(row);
for(int col = nums[row].length-1; col >= nums[row].length-1-width; col--) {
int level = (int) Math.pow(10, n - col - 1);
if(row % level == 0) {
nums[row][col] = (nums[row-level][col]+1) % 10;
} else {
nums[row][col] = nums[row-1][col];
}
}
System.out.println(Arrays.toString(nums[row]));
}
}
I think the following is quiet simple and readable:
public class Test{
public static void main(String[] args){
increment(7);
}
static void increment (int index){
index = Math.abs(index) ;
int[] digits = new int [index];
Arrays.fill(digits, 0); //initialize array with 0
for( int i = index-1; i>=0 ; i--) {//loop over all digits
for(int digit = 0; digit <=9 ; digit++) {//increment each
digits[i] = digit;
System.out.println(Arrays.toString(digits));
}
}
}
}

product of the numbers in list in java

My task here is to find the minimal positive integer number say 'A' so that the product of digits of 'A' is exactly equal to N.
example: lets say my N = 32
so my A would be 48 coz the divisors of 32 would be 1,2,4,8,16,32 and the minimum numbers that would make 32 is 4 and 8. so output is 48.
what i did is first read N, then found the divisors and stored them in a list. and used
if(l.get(i)*l.get(i+1)==N) {
sysout.print(l.get(i));
sysout.print(l.get(i+1));
but im not able to make the numbers as minimum. and also i need to print as -1 if no match is found.
for that i did:
if (l.get(i)*l.get(i+1)!=N) {
System.out.print(-1);
break;
}
but it is printing -1 initially only and breaking off. now im stuck here. please find my code below:
my code:
int N=1;
Scanner in = new Scanner(System.in);
List<Integer> l = new ArrayList<Integer>();
System.out.println("Enter N: ");
if (N>=0 && N<=Math.pow(10, 9)) {
N = in.nextInt();
}
for (int i=1; i<=N;i++) {
if (N%i==0) {
l.add(i);
}
}
System.out.println(l);
for (int i=0; i<l.size()-1;i++) {
if (l.get(i)*l.get(i+1)==N) {
System.out.print(l.get(i));
System.out.print(l.get(i+1));
}
}
in.close();
kindly help. thanks.
You're on the right track with finding the divisors on N. I'm not going to code it for you(you'll learn more by doing) but here's what you do: The divisors will be sorted already so loop the arraylist adding first to last and finding the min.
So for 1,2,4,8,16,32: Find 1+32, 2+16, 4+8; And then fin the max among these.
This is to get you started:
int first = 0;
int last = l.size()-1;
while(first<last){
//Find min using Math.min;
++first;
--last;
}
Happy Coding!
Could not resist. Below is a quick way to do what you want. Tested it here
(https://ideone.com/E0f4X9):
public class Test {
static ArrayList<Integer> nums = new ArrayList<>();
public static void main(String[] args){
int N =32;
findDivisors(N);
int first = 0, a = 0, b = 0;
int last = nums.size()-1;
int results = Integer.MAX_VALUE;
while(first < last){
int sum = nums.get(first) + nums.get(last);
results = Math.min(sum,results);
a = nums.get(first);
b = nums.get(last);
first++;
last--;
}
System.out.println(a+" "+b);
}
private static void findDivisors(int n){
for(int i=1; i<=n; i++){
if(n%i == 0){
nums.add(i);
}
}
}
}
Obviously if N<10 then A=N.
Otherwise A has to consist of more than one digit. Every digit of A is a divisor of N. The more significant digits of A always have to be less or equal than the lesser significant digits. Otherwise the order of digits could be changed to produce a smaller number.
For example A could not be 523 because the digits could be rearranged into 235 which is a smaller number. In this example we have 2 < 3 < 5.
Observation #1: when looking at A the smallest digits are at the front, the digits get higher towards the end.
Observation #2, A can never contain two digits a and b if the product of a and b is also a digit. For example, there can never be a 2 and a 3, there would have to be a 6 instead. There could never be three 2s, it would have to be an 8 instead.
This suggests that when building A we should start with the highest possible divisors of N (because a 9 is always better than two 3s, and so on). Then we should put that digit at the end of A.
So, while N > 10, find the highest divisor x of N that is a single digit (2<=x<=9). Add this value x to the end of A. Divide N by x and proceed with the loop.
Example:
N=126, A=?
Highest possible divisor x that is less or equal to 9 is 9. So 9 is going to be the last digit of A.
Divide N by 9 and repeat the process. N=126/9=14.
Now N=14, A=?9
Highest possible divisor x that is less or equal to 9 is 7. We have found the second to last digit of A.
Divide N by 7 and repeat the process. N=14/7=2.
Now N=2, A=?79
N<10. So 2 is the first digit of A.
The solution is A=279

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