HARD Lab · Dynamic Programming · 37

55. Jump Game

Given an array where each element is the maximum jump length from that position, decide whether you can reach the last index — a classic greedy problem where tracking the farthest reachable index at each step is both simpler and faster than DP.

MediumGreedyTypeScript

PROBLEM What we're solving

You stand at index 0 of array nums. nums[i] is the maximum number of steps you may jump forward from index i. Return true if you can reach the last index, false if there is an unbridgeable gap.

Example: nums = [2,3,1,1,4]. From index 0 (jump 2) you reach index 1 or 2; from 1 (jump 3) you leap to index 4 — the last index. Answer: true.

Counter-example: nums = [3,2,1,0,4]. Every path reaches index 3, but nums[3]=0 traps you. Answer: false.

KEY IDEA Track the farthest index you can ever reach

Insight → maintain a single integer maxReach = the farthest index reachable from any position seen so far. At each index i, if i > maxReach there is a gap — you can never get here, so return false. Otherwise update maxReach = max(maxReach, i + nums[i]). If maxReach ever reaches or exceeds the last index, return true. You never need to simulate individual paths.

RECIPE Greedy scan — one pass

  • 0 · Initialize. maxReach = 0 (can only reach index 0 to start).
  • 1 · For each index i:
    • If i > maxReach — gap detected. Return false immediately (we can never land here).
    • Extend: maxReach = max(maxReach, i + nums[i]). This captures the best jump from any reachable position so far.
    • If maxReach ≥ nums.length - 1, we've already covered the end — return true early.
  • 2 · Return true after the loop (all indices were reachable).
Classic confusion → learners often try nums[i] === 0 as the failure condition. That's wrong — a zero jump at index i is fine if you can skip over it from an earlier jump. The only real failure is i > maxReach.

COST Complexity & alternatives

O(n²) DP
O(n²)
dp[i] = reachable? Inner scan of all prior positions. Correct, but slow.
Greedy maxReach
O(n)
Single pass, O(1) space. The greedy choice (always keep the best reach) is provably optimal — no path can do better than the maximum possible reach.

Why greedy beats DP here

The DP answer for dp[i] only depends on whether any earlier index can reach it — exactly what maxReach aggregates. The greedy collapses the entire DP table into one variable.

Pattern transfer →the same "track the farthest reachable boundary" idea appears in Jump Game II (minimum jumps — extend the greedy to count levels), Video Stitching (cover a range with intervals, same maxReach sweep), Minimum Number of Taps to Open to Water a Garden, and the classic Interval Scheduling family.

RUN IT Greedy maxReach scan — can you reach the last index?

step 0 / 2
STARTStart: maxReach = 0. We scan left to right and track the farthest index we can reach.
2
0
3
1
1
2
1
3
4
4
maxReach / target
maxReach: 0
target: 4
i: —
current index ireachable from maxReachstuck — cannot reachlast index reached (true)
slowfast

TYPESCRIPT The solution, annotated

canJump.ts
function canJump(nums: number[]): boolean {
  let maxReach = 0;

  for (let i = 0; i < nums.length; i++) {
    if (i > maxReach) return false;      // gap — can't get here
    maxReach = Math.max(maxReach, i + nums[i]);
    if (maxReach >= nums.length - 1) return true; // already past the end
  }

  return true;  // all indices covered
}

Reading it block by block

Line 2 — initialize maxReach. maxReach = 0 means the only index we know we can stand on is index 0. Everything else is unreachable until proven otherwise.
Line 5 — gap check. If i > maxReach, there is a hole between the last reachable index and i — no combination of prior jumps can bridge it. Return false immediately.
Line 6 — extend the frontier. i + nums[i] is the farthest we can reach from this position. Taking the max against the current maxReach ensures we never lose a previously established frontier.
Line 7 — early exit. Once maxReach reaches or passes the last index we can stop — no need to process the remaining elements.
Line 10 — fallthrough true. If the loop completes without hitting the gap check, every index from 0 to n-1 was reachable.
Complexity → O(n) time — one pass through the array. O(1) space — only the maxReach integer is maintained. The greedy is strictly better than the O(n²) DP in both dimensions.

INTERVIEWFollow-ups they'll ask

  • "Jump Game II — minimum jumps?" Use the same maxReachsweep but track the current jump's boundary and increment a jump counter each time you cross it. Still O(n) / O(1).
  • "Return the actual path of jumps taken?" Add a prev[] array during a DP backward scan (or BFS), then reconstruct by following prev from the end.
  • "What if you can also jump backward?" Becomes a graph reachability problem — BFS/DFS from index 0, since the greedy ordering assumption breaks.
  • "What's the brute force?" Recursive DFS trying every possible jump from each index — O(2ⁿ) worst case. The O(n²) DP memoizes it. The greedy collapses it to O(n).
  • "Edge cases?" Single-element array is always true. First element zero with more elements is false. All zeros except last element is false (unless length is 1).

MNEMONIC The one-liner

"Keep a running high-water mark — if the tide never reaches your feet, you're stranded."

TRIGGERS When you see ___ → reach for ___

"can you reach the end / last index"greedy maxReach scan
each element = max jump from that positioni + nums[i] frontier update
gap detection in a range-cover problemi > maxReach → false
interval / coverage sweepsame maxReach pattern, count boundaries crossed

SKELETON The reusable shape

skeleton.ts
function canJump(nums: number[]): boolean {
  let maxReach = 0;

  for (let i = 0; i < nums.length; i++) {
    if (i > maxReach) return false;
    maxReach = Math.max(maxReach, i + nums[i]);
    if (maxReach >= nums.length - 1) return true;
  }

  return true;
}

FLASHCARDS Tap to flip

What does maxReach represent?
The farthest index reachable from any position visited so far.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is the time complexity of the greedy O(n) solution?
QUESTION 02
Trace nums = [2,3,1,1,4]. After visiting index 1 (value 3), what is maxReach?
QUESTION 03
nums = [3,2,1,0,4]. Which condition triggers the false return?
QUESTION 04
A zero at nums[i] always means the answer is false.
QUESTION 05
Why does the greedy maxReach approach provably produce the correct answer?
QUESTION 06
What is the minimum number of jumps to reach the last index for [2,3,1,1,4]? (Jump Game II)
QUESTION 07
Space complexity of the greedy solution?