Problem overview
Jump(nums) receives an array of non-negative integers. You start at index 0, and nums[i] is the maximum length of a jump from index i: from there you may land on any index from i + 1 to i + nums[i]. The method returns the minimum number of jumps needed to reach the last index.
For [2, 3, 1, 1, 4] the answer is 2. Jump one step from index 0 to index 1, then three steps from index 1 to the last index, at index 4. One jump is not enough, because index 0 reaches only indices 1 and 2.
Approach: fill in the cheapest cost of each cell, left to right
Let dp[k] be the minimum number of jumps needed to reach index k. Then dp[0] is 0, and the answer is dp[nums.Length - 1]. A direct way to fill the table is to let every index i improve all the cells it can reach, which is quadratic in the worst case. This lesson notices something that makes it linear.
The values of dp never decrease from left to right: reaching a farther cell never takes fewer jumps than reaching a nearer one. That means the first index i that can reach a cell j also gives the best answer for it, namely dp[i] + 1. Every later index that can reach j has a dp value that is at least as large, so it can't improve anything.
So each cell needs to be written once, and a single pointer j is enough to remember where the unreached cells begin. Cells are reached in order from left to right, so everything before j is done and everything from j on is still unvisited.
Here is the run on [2, 3, 1, 1, 4], showing j and the array after each value of i:
i=0 j=3 dp=0,1,1,0,0
i=1 j=5 dp=0,1,1,2,2
i=2 j=5 dp=0,1,1,2,2
i=3 j=5 dp=0,1,1,2,2
i=4 j=5 dp=0,1,1,2,2
Index 0 reaches cells 1 and 2 with one jump each. Index 1 then reaches cells 3 and 4, so both get dp[1] + 1 = 2. Once j reaches 5, which is the array length, nothing is left to fill.
Step by step
- Create
dpas anint[]withnums.Lengthentries. A newintarray is filled with zeros, which is right fordp[0], and every other cell is overwritten the first time it is reached. - Declare
jand set it to 1. It marks the first cell no jump has reached yet. Cell 0 is the start. - Loop over every index
iofnums. - Add a
whileloop that continues whilejis reachable fromi(j <= nums[i] + i) and still inside the array (j < nums.Length). Becausejonly moves forward, the inner loop runs at mostntimes in total across all values ofi. - Fill the cell and advance. Cell
jis reached for the first time with one jump from celli, so assigndp[i] + 1to it. Writing the index asj++does the assignment with the oldjand then movesjon, all in one statement. - Return
dp[nums.Length - 1].
The solution in C#
class Solution {
public int Jump(int[] nums) {
int[] dp = new int[nums.Length];
int j = 1;
for (int i = 0; i < nums.Length; ++i) {
while (j <= nums[i] + i && j < nums.Length) {
dp[j++] = dp[i] + 1;
}
}
return dp[nums.Length - 1];
}
}
C# details worth knowing
dp[j++] = dp[i] + 1;uses the value ofjbefore the increment as the index. Withjequal to 1, it writesdp[1]and leavesjat 2.++jwould do the opposite, increment first, and write the wrong cell.- The two conditions in the
whileare joined by&&, which stops at the first false one. The order here is safe either way, because neither condition reads the array atj. - Arrays have a fixed length, so
new int[nums.Length]allocates all the cells at once, each starting at 0.
Complexity
Time is O(n). The outer loop runs n times, and the inner while advances j on every iteration without ever moving it back, so it runs at most n times over the whole call. Extra space is O(n) for the dp array.
Edge cases and common mistakes
- A single element. With
[1]the loop never fills anything, and the method returnsdp[0], which is 0. You are already at the last index. - Forgetting the bound
j < nums.Length. A large jump value would sendjpast the end and throwIndexOutOfRangeException. - Starting
jat 0. That would overwritedp[0]with 1 and shift every answer by one. - Counting the start. The answer counts jumps, not cells, which is why
dp[0]is 0. - Unreachable endings. The solution assumes the last index can be reached. If a zero blocks the way, the cells behind it keep wrong or zero values, so the result can't be trusted.
Testing
[2, 3, 1, 1, 4] -> 2
[1, 1, 1, 1] -> 3
[3, 2, 1] -> 1
[1] -> 0