Problem overview
You are given an int[] nums of non-negative numbers. You start at index 0, and nums[i] is the farthest you can jump forward from index i: any jump of length 1 up to nums[i] is allowed. Jump(int[] nums) 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. One jump is not enough, because index 0 reaches at most index 2.
Approach: jump only when you must
Trying every sequence of jumps is exponential, and a table of the fewest jumps to every index takes O(n²) time. A greedy scan does it in one pass by thinking in layers. All the indexes reachable with 1 jump form the first layer, those that need exactly 2 jumps form the second, and so on. The answer is the number of the layer holding the last index.
The scan keeps two numbers:
curMaxis the farthest index reachable with the jumps counted so far.nextMaxis the farthest index reachable with one more jump, from any index visited so far.
Moving through the array, you stay within the current jump as long as i <= curMax. The first time i is beyond curMax, one more jump is unavoidable. It is best taken from whichever earlier index reached the farthest, which is exactly what nextMax remembers, so curMax becomes nextMax and the jump count goes up by one.
Take [2, 3, 1, 1, 4]. The first jump is counted at the start, so result = 1 and curMax = nextMax = 2. At index 1, nextMax becomes 1 + 3 = 4. At index 2, nothing changes (2 + 1 = 3). At index 3, curMax is 2, which is less than 3, so a second jump is counted and curMax becomes 4. Index 4 is within reach, and the loop ends with result = 2.
Why is waiting until the last moment correct? Any index inside the current layer can be the launch point of the next jump, and a launch point that reaches farther can never be worse than one that reaches less. Delaying the decision until i passes curMax lets the scan see every candidate in the layer first and pick the best, instead of committing early to an index that turns out to be a poor one.
Step by step
- Handle the trivial case. With one index (or none), you are already on the last one, so return 0.
- Start the jump count at 1. With at least two indexes, one jump is needed, so it is counted straight away.
- Set
curMaxtonums[0], the farthest index that first jump can reach. - Set
nextMaxtonums[0]too. It will grow as you see more indexes. - Loop from index 1 to the end, visiting each index once.
- Check whether index
iis out of reach. IfcurMax < i, incrementresultand setcurMaxtonextMax. - Update
nextMaxwithMath.Max(nextMax, i + nums[i]). This runs for every index, including the one that just triggered a jump, because that index may start the following jump. - Return
result.
The solution in C#
class Solution {
public int Jump(int[] nums) {
if (nums.Length <= 1) {
return 0;
}
int result = 1;
int curMax = nums[0];
int nextMax = nums[0];
for (int i = 1; i < nums.Length; ++i) {
if (curMax < i) {
++result;
curMax = nextMax;
}
nextMax = Math.Max(nextMax, i + nums[i]);
}
return result;
}
}
C# details:
Length, notLength(). The size of an array is a property, so it takes no parentheses.++resultversusresult++. As a standalone statement they do the same thing. The prefix form is just the style this solution uses.i + nums[i]is an index-sized value. It is the farthest index reachable fromi, and it can be larger than the last index. That is fine, since it is only compared, never used to index the array.
Complexity
Time: O(n). One pass, with constant work per index.
Space: O(1). Four integers: result, curMax, nextMax and the loop index.
Edge cases and common mistakes
- A single element.
[0]returns 0, because you are already at the last index. - One big first jump. For
[5, 1, 1, 1, 1, 1]the first jump covers everything,curMaxis never less thani, and the answer is 1. - Steps of one.
[1, 1, 1, 1]needs 3 jumps, one per move. - Counting a jump per index. The jump count only goes up when the current reach runs out, not whenever a better
nextMaxis found. - Skipping the
nextMaxupdate on the trigger index. If it were in anelsebranch, the index that forced the jump would not contribute its own reach. - Unreachable arrays. The method assumes the last index can be reached. For something like
[1, 0, 0]it still returns a number, which does not mean the end is reachable. When reachability is not guaranteed, test it separately.
Testing
[2, 3, 1, 1, 4]gives 2.[0]gives 0.[1, 1, 1, 1]gives 3.[5, 1, 1, 1, 1, 1]gives 1.