Jump Game (LC 55)
On this page
Pattern: Greedy / “reachability” DP
Difficulty: Medium
Key Concept: Track the farthest index reachable from the start; if you ever reach an index beyond current reach, fail; if reach ≥ last index, succeed.
Problem Statement
You are given an integer array nums. You are initially at index 0. Each element nums[i] represents your maximum jump length from position i.
Return true if you can reach the last index, otherwise false.
Input: nums (non-empty, nums[i] >= 0).
Output: boolean.
Examples:
[2,3,1,1,4]→true.[3,2,1,0,4]→false(stuck at0).
1. Algorithm & Pseudocode
Brute force
- DFS/BFS from index
0: fromi, try every step1..nums[i]toi+k. - Memoize index
ias reachable or not—still can be heavy for large branching. - Naive without pruning revisits many states.
Pseudocode (DFS + memo):
memo[i] = UNKNOWN / GOOD / BAD
function can(i):
if i == n-1: return true
mark BAD if dead end
for step in 1..nums[i]:
if can(i + step): return true
return false
Optimal (greedy)
- Maintain
reach= farthest index you can get to so far. - For each
ifrom0ton-1:- If
i > reach, you cannot stand ati→ returnfalse. reach = max(reach, i + nums[i]).- If
reach >= n - 1, returntrue.
- If
- If loop completes, return
reach >= n - 1.
Pseudocode:
reach = 0
for i from 0 to n-1:
if i > reach: return false
reach = max(reach, i + nums[i])
return true
2. Step-by-Step Analysis (Beginner-Friendly)
Why greedy is safe: Only reachability matters, not which path you took. If you can reach index i, every index < i was passable in some order; extending reach from i captures all jumps you could defer.
Why i > reach fails: You cannot land on i without crossing a gap—reach is the maximum “frontier.”
Relation to DP: dp[i] = reachable can be computed left-to-right; greedy compresses to one number reach.
3. The Dry Run
nums = [2, 3, 1, 1, 4]
| i | nums[i] | i > reach? | reach before | reach after max(reach, i+nums[i]) |
|---|---|---|---|---|
| 0 | 2 | no | 0 | max(0,2)=2 |
| 1 | 3 | no | 2 | max(2,4)=4 |
| 2 | 1 | no | 4 | 4 |
| 3 | 1 | no | 4 | 4 |
| 4 | 4 | no | 4 | max(4,8)=8 |
reach >= n-1 (4) → true.
nums = [3,2,1,0,4]:
| i | … | note |
|---|---|---|
| 0 | reach=3 | ok |
| … | at i=4 | i=4 > reach=3 → false |
4. Java Solution
Brute Force
public class Solution {
public boolean canJump(int[] nums) {
return dfs(nums, 0, new Boolean[nums.length]);
}
private boolean dfs(int[] nums, int i, Boolean[] memo) {
if (i == nums.length - 1) {
return true;
}
if (memo[i] != null) {
return memo[i];
}
int maxStep = Math.min(nums[i], nums.length - 1 - i);
for (int step = 1; step <= maxStep; step++) {
if (dfs(nums, i + step, memo)) {
memo[i] = true;
return true;
}
}
memo[i] = false;
return false;
}
}
Time: O(n^2) worst with memo (each cell once, inner loop). Space: O(n).
Optimal
public class Solution {
public boolean canJump(int[] nums) {
int reach = 0;
int n = nums.length;
for (int i = 0; i < n; i++) {
if (i > reach) {
return false;
}
reach = Math.max(reach, i + nums[i]);
if (reach >= n - 1) {
return true;
}
}
return true;
}
}
Time: O(n). Space: O(1).
5. The “Java vs. Others” Edge
Boolean[]memo allowsnullas “unknown”;boolean[]needs a separate visited bit or-1inbyte.- Greedy uses primitive
int—fast and allocation-free. - Early exit when
reach >= n-1saves iterations in long arrays.
6. Complexity Summary
| Approach | Time | Space | Notes |
|---|---|---|---|
| DFS (no memo) | Exponential | O(n) | Bad for large nums[i] |
| DFS + memo | O(n^2) worst | O(n) | Still quadratic edges |
| Greedy reach | O(n) | O(1) | Standard “Blind 75” answer |