Jump Game
A medium problem from Greedy. Try it first, open a hint when you are stuck, and read the solution when you are done.
- Medium
- Greedy
The problem
You start on the first position of nums, position 0. Each number is the longest jump forward you may make from its position, and any shorter jump is allowed too. A 0 means you cannot move on from there. Return true if some sequence of jumps takes you to the last position, and false if none does. If the list has one number you are already there, so the answer is true.
What to write
function solve(nums: number[]): booleanExample 1
Input nums = [2,3,1,1,4] Output true
From position 0 you may jump 1 or 2 steps. Take 1 step, to position 1. Its number is 3, and the last position, 4, is exactly 3 steps ahead. Those two jumps reach the last position, so the answer is true.
Example 2
Input nums = [3,2,1,0,4] Output false
From position 0 you can land on position 1, 2 or 3. From 1 you can go on to 2 or 3, and from 2 only to 3. Every route ends on position 3, whose number is 0, so position 4 is never reached and the answer is false.
Constraints
1–10,000 nonnegative integers, each ≤ 10,000.
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1: Use the constraints
Each number is a maximum, so every shorter jump is allowed as well. No number is negative, so you never move backward. That makes a 0 the only place where you can get stuck, and only when no earlier position can jump over it.
Hint 2: Ask a smaller question
In 3, 2, 1, 0, 4, work out, for each of the first four positions, the farthest position that one jump from it can land on. What do you notice? Would the answer change if position 1 held a 3 instead of a 2?
Hint 3: Name the pattern
If you can reach a position, you can reach every position before it too, because shorter jumps are allowed. So one number sums up all the paths: the farthest position reachable so far. Let each position you pass push that number forward. Keeping only the best reach, and never looking back, is a greedy choice. Read the lesson on greedy choice.
Hint 4: State the invariant
At the start the farthest reach is position 0. When the loop arrives at a later position, the farthest reach is the farthest landing spot of any jump from the positions before it. Every position up to the farthest reach can be reached. So if the loop arrives beyond the farthest reach, no jump can get there.
Hint 5: Sketch the pseudocode
far = 0. For each position i with number x, from left to right: if i is greater than far, return false. Otherwise far = the larger of far and i + x. If the loop finishes, return true.
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
Approach
Track the farthest reachable position. If the scan reaches a position beyond that boundary, it cannot continue; otherwise extend the boundary.
The slow way
From position 0, try every jump length the number allows, and from each landing spot every length again, until one path arrives or all have failed. Nothing is missed, but a position can be visited again for every path that leads to it. On lists that count down to 0 like example 2, one more number doubles the work: 8 visits for 5 numbers, over a million for 22.
The insight
Shorter jumps are allowed, so if you can reach a position you can reach every position before it. The positions you can reach form one unbroken stretch that starts at position 0, and all you need to know is where the stretch ends. Every position inside the stretch gets one chance to push that end forward.
Step by step
- Remember the farthest position you can reach: at first, position 0. You stand on position 0 before any jump, so it is the only position known to be reachable. This one number will stand in for every path you could take.
- Visit the positions from left to right. Jumps only go forward. So when the loop arrives at a position, all the positions that could jump there have already been handled.
- This position is beyond the farthest reach: return false. No earlier position has a jump long enough to land here, and jumps never go backward. So this position cannot be reached, and neither can any position after it. The last position is one of them.
- Otherwise a jump from here may push the farthest reach forward. The longest jump from here lands on this position plus its number. The farthest reach keeps the larger of the old value and that spot, so a short jump never pulls the reach back.
- No position was out of reach: return true. The loop passed every position without finding one beyond the farthest reach. That includes the last position, so some sequence of jumps arrives there.
The same numbered steps are the comments in each solution.
JavaScript
function solve(nums) {
// 1. Remember the farthest position you can reach: at first, position 0
let far = 0;
// 2. Visit the positions from left to right
for (let i = 0; i < nums.length; i++) {
// 3. This position is beyond the farthest reach: return false
if (i > far) return false;
// 4. Otherwise a jump from here may push the farthest reach forward
far = Math.max(far, i + nums[i]);
}
// 5. No position was out of reach: return true
return true;
}TypeScript
function solve(nums: number[]): boolean {
// 1. Remember the farthest position you can reach: at first, position 0
let far = 0;
// 2. Visit the positions from left to right
for (let i = 0; i < nums.length; i++) {
// 3. This position is beyond the farthest reach: return false
if (i > far) return false;
// 4. Otherwise a jump from here may push the farthest reach forward
far = Math.max(far, i + nums[i]);
}
// 5. No position was out of reach: return true
return true;
}
Python
def solve(nums):
# 1. Remember the farthest position you can reach: at first, position 0
far = 0
# 2. Visit the positions from left to right
for i, jump in enumerate(nums):
# 3. This position is beyond the farthest reach: return false
if i > far:
return False
# 4. Otherwise a jump from here may push the farthest reach forward
far = max(far, i + jump)
# 5. No position was out of reach: return true
return True
Example 2, traced
Input nums = [3,2,1,0,4] Output false
| Position | Farthest reach on arrival | Position reachable? | Longest jump from it lands on | Farthest reach after |
|---|---|---|---|---|
| 0 | 0 | Yes: 0 is not beyond 0 | 0 + 3 = 3 | 3 |
| 1 | 3 | Yes: 1 is not beyond 3 | 1 + 2 = 3 | 3 |
| 2 | 3 | Yes: 2 is not beyond 3 | 2 + 1 = 3 | 3 |
| 3 | 3 | Yes: 3 is not beyond 3 | 3 + 0 = 3 | 3 |
| 4 | 3 | No: 4 is beyond 3 | - | Return false |
Why it works
The farthest reach was set by a position you can reach, and from there a shorter jump lands on any spot up to the reach. So every position the loop passes is reachable, and true is right. A position beyond the reach is out of range of every earlier position, so false is right. Keeping one number and never looking back, the greedy choice, loses nothing.
Time and space
Time O(n), extra space O(1). The loop makes one pass: it visits each of the n positions at most once and does one comparison and one update for each, so the time is O(n). One number is stored, the farthest reach, whatever the length of the list: O(1) space.
n and m are input sizes, h is a tree's height, V and E are a graph's vertices and edges. The bounds describe this reference solution.
Watch out
- Do not follow a single path of longest jumps. In 2, 2, 0, 1 the longest jump from position 0 lands on the 0 and is stuck, while the shorter jump to position 1 gets through.
- Check a position before you use its number. If the farthest reach is raised first, a position that cannot be reached vouches for itself: in example 2 position 4 lifts the reach to 8, and true is returned.
- The longest jump lands on the position plus the number, not on the number. Comparing the farthest reach with the number alone returns false for example 1, where the answer is true.
Pattern
Greedy choice. Builds on Maximum Subarray.
Keep going
8 more problems solved with greedy choice
- Maximum SubarrayMedium
- Jump Game IIMedium
- Gas StationMedium
- Hand of StraightsMedium
- Merge Triplets to Form Target TripletMedium
- Partition LabelsMedium
- Valid Parenthesis StringMedium
- Non Overlapping IntervalsMedium