0045. Jump Game II¶
Table of Contents¶
- Problem
- Problem Breakdown
- Approach 1: Greedy (BFS-like)
- Approach 2: Dynamic Programming
- Complexity Comparison
- Edge Cases
- Common Mistakes
- Related Problems
- Visual Animation
Problem¶
| Leetcode | 45. Jump Game II |
| Difficulty | |
| Tags | Array, Dynamic Programming, Greedy |
Description¶
You are given a 0-indexed array of integers
numsof lengthn. You are initially positioned atnums[0].Each element
nums[i]represents the maximum length of a forward jump from indexi. In other words, if you are atnums[i], you can jump to anynums[i + j]where: -0 <= j <= nums[i]and -i + j < nReturn the minimum number of jumps to reach
nums[n - 1].The test cases are generated such that you can reach
nums[n - 1].
Examples¶
Example 1:
Input: nums = [2,3,1,1,4]
Output: 2
Explanation: 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
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Constraints¶
1 <= nums.length <= 10^40 <= nums[i] <= 1000- It's guaranteed that you can reach
nums[n - 1]
Problem Breakdown¶
1. What is being asked?¶
Given an array where each element represents a maximum jump length, find the minimum number of jumps to get from the first element to the last element.
2. What is the input?¶
| Parameter | Type | Description |
|---|---|---|
nums | int[] | Array of non-negative integers representing max jump lengths |
Important observations about the input: - The array is not sorted - Elements can be zero (dead ends, but guaranteed reachable) - The array always has at least 1 element - If n == 1, we are already at the destination (0 jumps needed)
3. What is the output?¶
- A single integer — the minimum number of jumps to reach the last index
4. Constraints analysis¶
| Constraint | Significance |
|---|---|
n <= 10^4 | O(n^2) = 10^8 — might be tight. O(n) preferred. |
nums[i] <= 1000 | Jump lengths are bounded |
n >= 1 | Single element = already there |
| Reachable guaranteed | No need to handle unreachable case |
5. Step-by-step example analysis¶
Example 1: nums = [2,3,1,1,4]¶
Index: 0 1 2 3 4
nums: [2, 3, 1, 1, 4]
From index 0 (nums[0]=2): can reach indices 1, 2
From index 1 (nums[1]=3): can reach indices 2, 3, 4 ← reaches end!
From index 2 (nums[2]=1): can reach index 3
Optimal path: 0 → 1 → 4 (2 jumps)
Jump 1: index 0 → index 1
Jump 2: index 1 → index 4 (last index)
Result: 2
Example 2: nums = [2,3,0,1,4]¶
Index: 0 1 2 3 4
nums: [2, 3, 0, 1, 4]
From index 0 (nums[0]=2): can reach indices 1, 2
From index 1 (nums[1]=3): can reach indices 2, 3, 4 ← reaches end!
From index 2 (nums[2]=0): can reach nothing (dead end)
Optimal path: 0 → 1 → 4 (2 jumps)
Result: 2
6. Key Observations¶
- BFS analogy — Think of this as a BFS where each "level" is one jump. From the current range of reachable indices, find the farthest you can reach with one more jump.
- Greedy insight — At each jump, we don't need to try every destination. We just need to track how far we can reach from the current level. When we exhaust all indices in the current level, we take a jump.
- No backtracking — We never need to jump backward. The optimal solution always moves forward.
- Level boundaries —
endmarks the farthest index reachable with the current number of jumps. Whenipassesend, we must take another jump.
7. Pattern identification¶
| Pattern | Why it fits | Example |
|---|---|---|
| Greedy (BFS-like) | Expand reachable range level by level, like BFS layers | Jump Game II (this problem) |
| Dynamic Programming | Build up min jumps to each index from left to right | General approach |
Chosen pattern: Greedy (BFS-like) Reason: Each "level" of BFS represents one jump. We greedily extend the farthest reachable index at each level, achieving O(n) time.
Approach 1: Greedy (BFS-like)¶
Thought process¶
Think of this as BFS on a graph: - Each index is a node - There's an edge from
ito everyjwherei < j <= i + nums[i]- We want the shortest path from index 0 to index n-1In BFS, we process nodes level by level. Each level = one jump. Instead of using a queue, we track the range of indices reachable at each level using two variables:
end(current level boundary) andfarthest(farthest reachable from this level).When we pass
end, we've exhausted the current level → take a jump and setend = farthest.
Algorithm (step-by-step)¶
- Initialize
jumps = 0,end = 0,farthest = 0 - Iterate
ifrom0ton - 2(we don't need to jump FROM the last index): a. Updatefarthest = max(farthest, i + nums[i])b. Ifi == end(reached the boundary of current jump):- Increment
jumps - Set
end = farthest - If
end >= n - 1, we can stop early
- Increment
- Return
jumps
Pseudocode¶
function jump(nums):
n = len(nums)
jumps = 0
end = 0 // boundary of current jump level
farthest = 0 // farthest reachable from current level
for i = 0 to n - 2:
farthest = max(farthest, i + nums[i])
if i == end: // exhausted current level
jumps++
end = farthest
if end >= n - 1:
break
return jumps
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(n) | Single pass through the array. Each index visited exactly once. |
| Space | O(1) | Only three variables: jumps, end, farthest. |
Implementation¶
Go¶
// jump — Greedy (BFS-like) approach
// Time: O(n), Space: O(1)
func jump(nums []int) int {
n := len(nums)
jumps := 0
end := 0 // boundary of current jump level
farthest := 0 // farthest reachable from current level
for i := 0; i < n-1; i++ {
// Update the farthest we can reach from this level
if i+nums[i] > farthest {
farthest = i + nums[i]
}
// If we've reached the end of the current level, jump
if i == end {
jumps++
end = farthest
if end >= n-1 {
break
}
}
}
return jumps
}
Java¶
class Solution {
// jump — Greedy (BFS-like) approach
// Time: O(n), Space: O(1)
public int jump(int[] nums) {
int n = nums.length;
int jumps = 0;
int end = 0; // boundary of current jump level
int farthest = 0; // farthest reachable from current level
for (int i = 0; i < n - 1; i++) {
// Update the farthest we can reach from this level
farthest = Math.max(farthest, i + nums[i]);
// If we've reached the end of the current level, jump
if (i == end) {
jumps++;
end = farthest;
if (end >= n - 1) break;
}
}
return jumps;
}
}
Python¶
class Solution:
def jump(self, nums: list[int]) -> int:
"""
Greedy (BFS-like) approach
Time: O(n), Space: O(1)
"""
n = len(nums)
jumps = 0
end = 0 # boundary of current jump level
farthest = 0 # farthest reachable from current level
for i in range(n - 1):
# Update the farthest we can reach from this level
farthest = max(farthest, i + nums[i])
# If we've reached the end of the current level, jump
if i == end:
jumps += 1
end = farthest
if end >= n - 1:
break
return jumps
Dry Run¶
Input: nums = [2, 3, 1, 1, 4]
n = 5, jumps = 0, end = 0, farthest = 0
i=0: farthest = max(0, 0+2) = 2
i == end (0 == 0) → jumps = 1, end = 2
end (2) < n-1 (4) → continue
i=1: farthest = max(2, 1+3) = 4
i != end (1 != 2) → continue
i=2: farthest = max(4, 2+1) = 4
i == end (2 == 2) → jumps = 2, end = 4
end (4) >= n-1 (4) → BREAK
Result: 2
BFS Level Visualization:
Level 0: [index 0] → can reach up to index 2
Level 1: [index 1, index 2] → can reach up to index 4
Level 2: [index 3, index 4] → DONE (index 4 is the target)
Total jumps: 2
Approach 2: Dynamic Programming¶
Thought process¶
Define
dp[i]= minimum number of jumps to reach indexi. - Base case:dp[0] = 0(we start at index 0) - Transition: for each indexj < i, ifj + nums[j] >= i, thendp[i] = min(dp[i], dp[j] + 1)This works but is O(n^2). We can optimize it slightly by iterating forward: For each index
i, update all reachable indicesjin range[i+1, i+nums[i]].
Algorithm (step-by-step)¶
- Create
dparray of sizen, initialized to infinity - Set
dp[0] = 0 - For each index
ifrom0ton-2: a. For eachjfromi+1tomin(i + nums[i], n-1):dp[j] = min(dp[j], dp[i] + 1)
- Return
dp[n-1]
Pseudocode¶
function jump(nums):
n = len(nums)
dp = [infinity] * n
dp[0] = 0
for i = 0 to n - 2:
for j = i + 1 to min(i + nums[i], n - 1):
dp[j] = min(dp[j], dp[i] + 1)
return dp[n - 1]
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(n * max(nums[i])) | Worst case O(n^2) when jump lengths are large. |
| Space | O(n) | The dp array of size n. |
Implementation¶
Go¶
// jumpDP — Dynamic Programming approach
// Time: O(n * max(nums[i])), Space: O(n)
func jumpDP(nums []int) int {
n := len(nums)
dp := make([]int, n)
for i := 1; i < n; i++ {
dp[i] = n // initialize to a large value
}
for i := 0; i < n-1; i++ {
end := i + nums[i]
if end >= n {
end = n - 1
}
for j := i + 1; j <= end; j++ {
if dp[i]+1 < dp[j] {
dp[j] = dp[i] + 1
}
}
}
return dp[n-1]
}
Java¶
class Solution {
// jumpDP — Dynamic Programming approach
// Time: O(n * max(nums[i])), Space: O(n)
public int jumpDP(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, n); // initialize to a large value
dp[0] = 0;
for (int i = 0; i < n - 1; i++) {
int end = Math.min(i + nums[i], n - 1);
for (int j = i + 1; j <= end; j++) {
dp[j] = Math.min(dp[j], dp[i] + 1);
}
}
return dp[n - 1];
}
}
Python¶
class Solution:
def jump(self, nums: list[int]) -> int:
"""
Dynamic Programming approach
Time: O(n * max(nums[i])), Space: O(n)
"""
n = len(nums)
dp = [float('inf')] * n
dp[0] = 0
for i in range(n - 1):
end = min(i + nums[i], n - 1)
for j in range(i + 1, end + 1):
dp[j] = min(dp[j], dp[i] + 1)
return dp[n - 1]
Dry Run¶
Input: nums = [2, 3, 1, 1, 4]
n = 5, dp = [0, inf, inf, inf, inf]
i=0 (nums[0]=2): update j=1..2
dp[1] = min(inf, 0+1) = 1
dp[2] = min(inf, 0+1) = 1
dp = [0, 1, 1, inf, inf]
i=1 (nums[1]=3): update j=2..4
dp[2] = min(1, 1+1) = 1 (no change)
dp[3] = min(inf, 1+1) = 2
dp[4] = min(inf, 1+1) = 2
dp = [0, 1, 1, 2, 2]
i=2 (nums[2]=1): update j=3..3
dp[3] = min(2, 1+1) = 2 (no change)
dp = [0, 1, 1, 2, 2]
i=3 (nums[3]=1): update j=4..4
dp[4] = min(2, 2+1) = 2 (no change)
dp = [0, 1, 1, 2, 2]
Result: dp[4] = 2
Complexity Comparison¶
| # | Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|---|
| 1 | Greedy (BFS-like) | O(n) | O(1) | Optimal time and space | Requires understanding BFS analogy |
| 2 | Dynamic Programming | O(n * max(nums[i])) | O(n) | Intuitive, easy to prove | Slower, uses extra memory |
Which solution to choose?¶
- In an interview: Approach 1 (Greedy) — fast, elegant, demonstrates BFS thinking
- In production: Approach 1 — optimal time and space
- On LeetCode: Approach 1 — only O(n) comfortably passes within the time limit
- For learning: Both — DP to understand the structure, Greedy to see the BFS optimization
Edge Cases¶
| # | Case | Input | Expected Output | Reason |
|---|---|---|---|---|
| 1 | Single element | nums=[0] | 0 | Already at the destination |
| 2 | Two elements | nums=[1,0] | 1 | One jump to reach the end |
| 3 | Can reach end in one jump | nums=[5,1,1,1,1] | 1 | Jump directly from index 0 |
| 4 | Must jump every index | nums=[1,1,1,1,1] | 4 | Each jump covers only 1 step |
| 5 | Large first jump | nums=[10,0,0,0,0] | 1 | nums[0] covers the entire array |
| 6 | Multiple optimal paths | nums=[2,3,1,1,4] | 2 | 0→1→4 or other 2-jump paths |
Common Mistakes¶
Mistake 1: Iterating up to n-1 instead of n-2¶
# WRONG — iterates over the last index (unnecessary jump)
for i in range(n):
farthest = max(farthest, i + nums[i])
if i == end:
jumps += 1
end = farthest
# CORRECT — stop before the last index
for i in range(n - 1):
farthest = max(farthest, i + nums[i])
if i == end:
jumps += 1
end = farthest
Reason: We don't need to jump FROM the last index. If end happens to equal n-1, we'd count an extra unnecessary jump.
Mistake 2: Not tracking farthest across the level¶
# WRONG — only considering the current index's reach
if i == end:
jumps += 1
end = i + nums[i] # Only considers current element
# CORRECT — consider the maximum reach from the entire level
farthest = max(farthest, i + nums[i])
if i == end:
jumps += 1
end = farthest # Uses the best reach from the entire level
Reason: The greedy choice requires knowing the farthest reachable index from ALL indices in the current level, not just the boundary index.
Mistake 3: Forgetting to handle single-element arrays¶
# WRONG — crashes or returns 1 for single element
jumps = 0
end = 0
for i in range(n - 1): # range(0) = empty, so this is actually fine
# But if you use a while loop:
while end < n - 1: # This correctly returns 0 jumps
...
Reason: When n == 1, we are already at the destination and need 0 jumps.
Related Problems¶
| # | Problem | Difficulty | Similarity |
|---|---|---|---|
| 1 | 55. Jump Game | Same setup, but only asks if reachable | |
| 2 | 1306. Jump Game III | BFS on jump array, bidirectional jumps | |
| 3 | 1345. Jump Game IV | BFS with equal-value jumps | |
| 4 | 1871. Jump Game VII | BFS with range constraints | |
| 5 | 1024. Video Stitching | Same greedy interval covering pattern |
Visual Animation¶
Interactive animation: animation.html
In the animation: - Array elements shown as bars with jump range highlights - current, farthest, and end pointers tracked visually - Step-by-step BFS-level expansion with jump counting - Preset examples and custom input support