Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
Each value is the max jump length from that index; return the minimum number of jumps to reach the last index (reachability is guaranteed). The DP fills minimum jumps per index in O(n²). The optimal greedy treats it as a BFS over levels: the indices reachable in exactly k jumps form a contiguous window, so track the current window's end and the farthest index the whole window can reach; every time the cursor hits the window end you've spent one jump and the window expands. O(n) time, O(1) space.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you recognise this as a shortest-path/BFS problem and then realise the contiguous reachability lets you collapse the BFS queue into two scalars — plus whether you handle the off-by-one (loop to n−1) that prevents an extra jump.
Where candidates slip
Counting a jump at every index instead of only when the cursor reaches the current window's end, or looping to n and double-counting a jump when the last index coincides with curEnd.
How to stand out
Name it as BFS over levels, present the O(n²) DP to show you can model min-jumps formally, then collapse to the O(n) two-pointer-of-windows greedy and justify why scanning a window and tracking its farthest reach is exactly a BFS layer.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewMinimum jumps to reach the last indexHow to count min jumps?Model per indexShortest pathExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Dynamic Programming — Min Jumps to Each Index
1Hint 1Explore hintHide hint
Let dp[i] be the minimum jumps to reach index i. dp[0] = 0. How does reaching i depend on earlier indices?
2Hint 2Explore hintHide hint
From index i you can extend to any j in [i+1, i+nums[i]] with one more jump, so dp[j] = min(dp[j], dp[i] + 1).
3Hint 3Explore hintHide hint
Sweep left to right, relaxing each index's reachable range. The answer is dp[n−1].
A · Core idea
Define dp[i] = the minimum number of jumps needed to arrive at index i, with dp[0] = 0. Process indices left to right; from a reachable i, every index j in [i+1, i+nums[i]] can be reached in dp[i] + 1 jumps, so we relax dp[j] = min(dp[j], dp[i] + 1). The answer is dp[n−1].
Why left to right works. To reach j, you must have first reached some earlier i whose jump range covers j. Filling left to right guarantees that when we use dp[i] to relax forward, dp[i] is already final — its value was settled by even earlier indices. So each forward relaxation reads a correct minimum.
Why we relax the whole reachable range. A single index i doesn't pick one target; it can reach an entire contiguous block [i+1, i+nums[i]], all in one extra jump. To find the minimum jumps to every index, we must offer dp[i] + 1 to each index in that block and keep the smallest offer any predecessor makes. That's the min relaxation over a range.
Why we don't ship this. That per-index range relaxation is the quadratic cost: with n = 10⁴ and large jump values, you can approach 10⁸ relaxations. The DP is the honest way to define min-jumps and prove the recurrence, but it recomputes information that the contiguous-window structure makes free. The greedy notices that the indices sharing the same dp value form a contiguous band — a BFS level — and processes each band in O(1) amortised.
Why the DP still earns its keep. Stating it shows the interviewer you can formalise shortest-jumps as a relaxation, and it makes the leap to BFS levels obvious: the set {i : dp[i] = k} is exactly the k-th BFS layer, and those layers are contiguous because every reach is an interval.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
nums = [2, 3, 1, 1, 4]i=0 (dp=0)
reach [1,2]; dp[1]=1, dp[2]=1
dp = [0,1,1,∞,∞]2C · Code with teaching comments
Reference implementation previewpublic int jump(int[] nums) { int n = nums.length; int[] dp = new int[n]; Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0;Explore the full codeHide the full code
D · Why it works and complexityO(n²) timeO(n) spaceOpen the invariant and proofExplore the proofHide the proof
The DP reveals the fact the greedy exploits: indices with the same minimum jump count form a contiguous window, because each index reaches an interval and intervals union into intervals. Once you see that 'dp[i] = k' carves the array into consecutive bands, you realise you don't need n separate dp cells — just the boundary of the current band.
ComplexityO(n²) time, O(n) space — For each of n indices we relax up to its full jump range (length up to nums[i]), so the worst case is O(n²) time; the dp array uses O(n) space.
E · Edge cases and pitfalls5nums = [0] — n=1, dp[0]=0, loop relaxes nothing (range empty), returns dp[0]=0.Explore edge casesHide edge cases
Edge cases handled
nums = [0]n=1, dp[0]=0, loop relaxes nothing (range empty), returns dp[0]=0.nums = [1, 1, 1, 1]Each index reaches only the next, so dp = [0,1,2,3]; returns 3.
Pitfalls
- Forgetting to clamp i + nums[i] to n−1 — out-of-bounds writes on large jump values.
- Relaxing from an index whose dp is still infinity — propagates garbage (guard with the continue).
- Shipping O(n²) when n = 10⁴ makes the linear greedy clearly preferable.
Approach 2 of 2 · Optimal
Greedy — BFS Over Contiguous Levels (Optimal)
1Hint 1Explore hintHide hint
Think BFS: level 0 is index 0, level 1 is everything reachable in one jump, level 2 in two jumps, and so on. The answer is the level number of the last index.
2Hint 2Explore hintHide hint
Because each index reaches a contiguous range, each level is itself a contiguous window. Track curEnd = the right edge of the current level and farthest = the furthest the whole current level can reach.
3Hint 3Explore hintHide hint
Scan left to right updating farthest. When the cursor i hits curEnd, you've consumed the entire current level → increment jumps and set curEnd = farthest. Loop only to n−1 to avoid an extra phantom jump.
A · Core idea
Run BFS without a queue. Maintain curEnd = the right boundary of the current BFS level (the farthest index reachable in jumps jumps) and farthest = the farthest index reachable in jumps + 1 jumps, discovered by scanning the current level. Sweep i from 0 to n−2, updating farthest = max(farthest, i + nums[i]). When i reaches curEnd, the current level is fully explored, so increment jumps and advance curEnd = farthest to the next level's boundary.
Why this is literally BFS. Model each index as a node with edges to every index in its jump range. BFS explores nodes in waves: all nodes at distance k before any at distance k+1, and the first time it reaches the target gives the shortest distance. The set of indices at BFS distance k is exactly {i : min jumps to i = k}. The crucial structural fact: because a jump goes up to nums[i], the indices reachable in k jumps form a contiguous window [prevEnd+1, curEnd] — there are no gaps. So instead of a queue holding individual nodes, the entire level is described by one number, curEnd, and we discover the next level's extent with a running farthest.
Why a jump is counted only at `i == curEnd`. While i is still inside the current window (i < curEnd), we're examining nodes that all sit at the same BFS distance — we haven't spent another jump yet, we're just gathering how far this level collectively reaches. The instant i equals curEnd, we've examined every node of the current level; to go further we must take one more jump, so we increment jumps and roll the window forward to farthest. Counting a jump per index instead would massively over-count; counting it at the window edge counts exactly one jump per BFS layer.
Why the greedy is optimal — the exchange/contradiction argument. BFS is provably shortest for unweighted graphs, but here's the direct exchange argument for why scanning a window and taking its maximum reach can't be beaten. Within the current level [lo, curEnd], suppose the optimal solution jumps to some index p in this window and from there reaches index q. Our greedy doesn't commit to a single p; it records farthest = max over all i in the window of (i + nums[i]), which is >= q for every choice p the optimal could make. Now suppose, for contradiction, that the greedy used more jumps than optimal to reach the end. Consider the first level where they diverge: optimal reaches some index x using k jumps. Greedy's curEnd after k jumps is the maximum index reachable in k jumps, so curEnd >= x — greedy is at least as far along after the same number of jumps. By induction this holds at every level, so greedy reaches (or passes) the last index in no more jumps than optimal. The maximum-reach window dominates any specific sequence of choices, which is exactly why the greedy never needs to backtrack or pick a particular landing index.
Why loop to `n − 1`, not `n`. If the last index coincides with a curEnd, iterating onto it would trigger one final jumps++ for a jump we never actually take (we're already there). Stopping at n − 2 guarantees we count a jump only for transitions that genuinely advance us, so nums = [0] correctly yields 0.
Why this beats the DP. The DP recomputes dp[j] for every j in every range; the greedy notices those values form contiguous levels and tracks only the level boundary plus a running max, collapsing O(n²) to O(n) and O(n) space to O(1).
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
nums = [2, 3, 1, 1, 4]Interactive diagram
BFS levels as contiguous windows
i=0Each jump count is a BFS layer; 'jump up to k' makes every layer a gap-free interval, so two integers replace the queue.
2C · Code with teaching comments
D · Why it works and complexityO(n) timeO(1) spaceOpen the invariant and proofExplore the proofHide the proof
The unlock is seeing 'minimum jumps' as 'BFS depth', then noticing that 'jump up to k' makes every BFS layer a gap-free interval — so the queue degenerates into two integers. You don't choose which index to jump to inside a layer; you only track how far the whole layer can throw you, and that maximum dominates every individual choice. This window-as-level idea is the seed for many 'minimum moves on a line' problems.
ComplexityO(n) time, O(1) space — A single left-to-right pass with O(1) work per index (one max, one comparison) gives O(n) time, and we keep just three integers — jumps, curEnd, farthest — so O(1) space.
E · Edge cases and pitfalls6nums = [0] — Loop runs for i in range(0) — never executes — so jumps stays 0. Already on the last index.Explore edge casesHide edge cases
Edge cases handled
nums = [0]Loop runs for i in range(0) — never executes — so jumps stays 0. Already on the last index.nums = [2, 1]i=0: farthest=2, i==curEnd → jumps=1, curEnd=2; loop ends (n−1=1). Returns 1.nums = [1, 2, 1, 1, 1]Levels: [0]→[1]→[2,3]→[4]; cursor hits each curEnd, yielding 3 jumps.
Pitfalls
- Looping to n instead of n−1 — double-counts a jump when the last index equals curEnd.
- Incrementing jumps at every index rather than only when i reaches curEnd.
- Overwriting farthest = i + nums[i] without the max — a small later jump shouldn't shrink the next level's reach.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewpublic int jump(int[] nums) { int jumps = 0, curEnd = 0, farthest = 0; for (int i = 0; i < nums.length - 1; i++) { farthest = Math.max(farthest, i + nums[i]); if (i == curEnd) {Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Counting a jump per index
All indices inside the current window sit at the same BFS depth. Incrementing jumps for each of them counts steps you never took — the count balloons far above the true minimum.
Explore the code comparisonHide the code comparison
Counting a jump per index
All indices inside the current window sit at the same BFS depth. Incrementing jumps for each of them counts steps you never took — the count balloons far above the true minimum.
Don't write this
jumps = 0
for i in range(len(nums) - 1):
farthest = max(farthest, i + nums[i])
jumps += 1 # wrong: counts every indexWrite this instead
jumps = 0
for i in range(len(nums) - 1):
farthest = max(farthest, i + nums[i])
if i == cur_end: # only at the window boundary
jumps += 1
cur_end = farthest2Looping to n instead of n−1
If the last index equals curEnd, iterating onto it fires one more jumps++ for a jump that never happens — you're already standing on the goal.
Explore the code comparisonHide the code comparison
Looping to n instead of n−1
If the last index equals curEnd, iterating onto it fires one more jumps++ for a jump that never happens — you're already standing on the goal.
Don't write this
for (int i = 0; i < nums.length; i++) { // includes last index
farthest = Math.max(farthest, i + nums[i]);
if (i == curEnd) { jumps++; curEnd = farthest; }
}Write this instead
for (int i = 0; i < nums.length - 1; i++) {
farthest = Math.max(farthest, i + nums[i]);
if (i == curEnd) { jumps++; curEnd = farthest; }
}3Letting a small jump shrink the next level
Assigning farthest = i + nums[i] without the max can pull the next level's boundary backward when a later index has a tiny value, undercounting reach and inflating jumps.
Explore the code comparisonHide the code comparison
Letting a small jump shrink the next level
Assigning farthest = i + nums[i] without the max can pull the next level's boundary backward when a later index has a tiny value, undercounting reach and inflating jumps.
Don't write this
farthest = i + nums[i]; // overwrites a bigger earlier reachWrite this instead
farthest = Math.max(farthest, i + nums[i]);Revise
Carry the pattern into the interview
Pattern to remember
Greedy BFS levels — current window end + farthest reach
Code pattern to memorize
jumps = 0; curEnd = 0; farthest = 0
for i in range(n - 1): # stop before the last index
farthest = max(farthest, i + nums[i])
if i == curEnd: # finished this jump's reachable window
jumps += 1
curEnd = farthest # window expands to the next level
return jumpsRules to remember
- 1Think in levels: indices reachable in exactly
kjumps are a contiguous window — BFS over the array, no explicit queue. - 2Carry `farthest` = the furthest index the current window can reach, updated at every index you pass.
- 3Carry `curEnd` = the right edge of the current level; when
i == curEndyou've exhausted this jump's reach, so `jumps++` and setcurEnd = farthest. - 4Loop only to `n − 1`, never
n— landing on the last index shouldn't trigger one extra phantom jump. - 5You never need to know which index you jumped to inside a window — only how far the whole window reaches, which is why greedy = BFS here.
- 6The problem guarantees reachability, so you don't handle the stuck case (that's Jump Game I).
Use this pattern when
- Minimum steps / shortest path on a line where each position grants a forward range
- Reachable-in-k-moves sets form contiguous windows (jump *up to* k)
- You want BFS's shortest-path guarantee without building a graph
- Only the count of jumps matters, not the exact path
Choose another pattern when
- Edge weights vary (cost per jump differs) → Dijkstra, not uniform BFS levels
- Jumps can go backward or to arbitrary targets → real graph BFS with a queue
- You must return the actual jump sequence → augment with parent pointers
- Only feasibility (yes/no), not the count → Jump Game I's single-frontier greedy
Interview takeaway
Jump Game II is BFS in disguise: because 'jump up to k' makes each level a contiguous window, you replace the queue with two integers — the window's end and its farthest reach — and count a jump each time the window flips over. The same level-window trick reduces many 'minimum moves on a line' problems from O(n²) to O(n).
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternCanonical 'BFS over contiguous levels' greedy: minimum moves on a line where each position reaches an interval, so each BFS layer is a gap-free window and the queue collapses into curEnd + farthest. Recognise it for min-jumps / min-taps / interval-covering problems; it's the counting sibling of Jump Game's feasibility frontier.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Jump GameFeasibility only (can you reach the end?) — single farthest frontier, no jump counting.
- Jump Game IIIComing soonJumps go i±nums[i] to a zero — true graph BFS/DFS, levels are no longer contiguous.
- Jump Game IVComing soonJump ±1 or to equal-valued indices — explicit BFS with a queue and value-buckets.
- Minimum Number of Taps to Open to Water a GardenComing soonSame min-jumps-as-BFS-windows greedy, reframed as covering an interval with ranges.