Tackling Jump Game Problems (LeetCode) | Towards Data Science

In this article, we will be tackling the two jump game problems which are available on LeetCode. These are pretty famous problems and can be a little tricky to solve in one go.
Here, we would discuss various ways to solve both the problems step by step with complexity analysis. So, let’s start with the first one.
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you can reach the last index.
Example 1:
Input: nums = [2,3,1,1,4]Output: trueExplanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [3,2,1,0,4]Output: falseExplanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.
Constraints:
1 <= nums.length <= 3 * 10^4
0 <= nums[i][j] <= 10^5
There are many ways to approach this solution. One thing to note here is we only need to check whether we can reach or not. We do not need to care about the number of steps needed.
We will be going with two simple linear solutions. Both of them have similar ideas but differ a little bit in implementation.
Both the solutions would be O(N) time and O(1) space. We will not be going the DP way for this solution.
So, let’s start with the first approach — the forward iteration approach.
In this, we will start with the first element of the array and move forward by one step each time. We also define a maxReach variable which would keep track of the maximum we can reach from that particular index.
E.g.: We are at index 2 and the number at index 2 is 4 so the maximum we can reach from here is index 2+4=6.
So, we keep updating maxReach in each iteration. Also, we keep checking that the loop variable I should be less than maxReach always otherwise we are at an index which was not within our reach i.e. we cannot reach the last element. So, we break out if I exceed maxReach.
So, to avoid the issues, at last, we check whether i==length of the array which signifies we have reached the end of the array.
bool canJump(vector<int>& nums) {    int i= 0, maxReach=0;    while(i<nums.size() && i<=maxReach){        maxReach = max(i + nums[i], maxReach);        i++;    }    if(i == nums.size())        return true;    return false;}
So, as you can see above we have our first solution which is really very easy to understand. We keep updating maxReach and our conditions do the rest of the job. As soon as we reach the end of the array or we surpass maxReach loop stops. So, if we have reached the end of the array we would be at array’s length which we wanted. Otherwise, we were not able to reach the last element of the array.
So, now let’s move to the second method — the backward iteration approach.
In this approach, we do just the same work but we start from the last element of the array and try to move backwards. If we can successfully reach the first element of the array, it means the result is true i.e. we can reach the last index of the array.
We define the variable lastreach as the last element of the array. At each iteration, we update the lastreach variable by checking whether the lastreach variable is reachable from that index. If yes, the current index becomes the new lastreach. We keep on doing this until we reach the first element of the array.
Now, we check whether lastreach variable is equal to 0 (index of 1st variable) which means we could have reached the last element had we started from the first one.
bool canJump(vector<int>& nums) {   int lastreach = nums.size()-1;   for(int i=nums.size()-2;i>=0;i — )   {       if(i+nums[i]>=lastreach)           lastreach=i;   }   if(lastreach==0)       return true;   return false; }
So, both are solutions are almost similar in functioning. One starts from the start of the array and the other from the end of the array. Hope you understood both the solutions.
Now, let’s move to Jump Game II which is a bit more difficult and we would be using the same maxReach technique once again with some modifications.
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Your goal is to reach the last index in the minimum number of jumps.
You can assume that you can always reach the last index.
Example 1:
Input: nums = [2,3,1,1,4]Output: 2Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [2,3,0,1,4]Output: 2
Constraints:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
There are many ways to solve this problem, but two ways are very famous. One uses Dynamic Programing and takes a time of O(N2) while the other solution is a little smarter and operates in O(N) time.
So, let’s see both the approaches and solutions one by one.
The first solution uses Dynamic Programming and takes a time of O(N2). (It can give TLE error in LeetCode). Also, this solution uses O(N) space since we need to maintain an extra jumps array so it is definitely not optimal.
In this, we first build a new array jumps which will store INT_MAX (maximum value in the respective languages). The purpose of this jumps array is to keep track of the minimum number of jumps needed to reach that particular index.
So, we define jumps[0]=0 since we don’t require any jump to reach the 0th index. Now, we start a loop from the second array element till the last one. For each element, we have an inner loop which runs from the first element till that particular element. For each inner element, we check whether we can reach from that element to the outer element for which the inner loop is running in a single jump. If yes, we update the value of jumps[i] = min(jumsp[i], jumps[j]+1). This allows us to find the minimum number of jumps required to reach that particular index.
This uses previous results and so is using Dynamic Programming. Since we have one outer loop running through each element of the array and an inner loop running from 0 to ith index, so it is O(N2). Also, we have an extra jumps array which consumes O(N) extra space.
int jump(vector<int>& nums) {    vector<int>jumps(nums.size(),INT_MAX);    jumps[0]=0;    for(int i=1;i<nums.size();i++)    {        for(int j=0;j<i;j++)        {            if(nums[j]>=i-j)                jumps[i] = min(jumps[i], jumps[j]+1);        }    }    return jumps[nums.size()-1];}
The second approach is smarter and is very optimal as it takes no extra space and only O(N) time. In this approach, we define two variables maxReach and steps. Also, we have a jumps variable to count the number of jumps needed.
We define both jumps and steps variable to the value of the first index of the array.
maxReach means the maximum we can reach from that particular index which is the index plus the value of the index (the jump value). So, we keep updating it in each iteration so that whenever we move forward, the variable stores the maximum we can reach by using maxReach = max(maxReach, nums[i]+i)
Also, at each iteration, we reduce our steps variable by 1. As we move forward we consume 1 step each time. So, whenever we run out of steps, it means we need to take 1 jump. So, we increase the jumps variable and also update the steps variable. The steps variable is updated to the value (maxReach — i) which means the difference between a maximum reach possible from that variable or any variable before it and the current index. So, we can take those steps and then we need to again jump.
So, in this solution, we return jumps+1 as our answer since we only jump after we run out of steps but to perform that we needed a jump upfront. Also, you need to note that we moved only till the second last element and not the last element since at last step we do not need to consume one more step as we are already there and no need to jump more.
We also took care of the edge case when there is only 1 element in the array, then we do not need to perform any jump.
int jump(vector<int>& nums) {    if(nums.size()==1)        return 0;    int maxReach = nums[0];    int steps = nums[0];    int jumps = 0;    for(int i=1;i<nums.size()-1;i++)    {        maxReach = max(maxReach, nums[i]+i);        steps--;        if(steps==0)        {            jumps++;            steps = maxReach - i;        }    }    return jumps+1;}
So, I hope it was a good experience reading this post and I hope you learned something out of it. Go, solve these problems on Leetcode. I have not covered Jump Game III. You should try it too.
Thanks for reading! More articles to read after finishing this one:-