Jump Game III — BFS / DFS Reachability on an Implicit Graph
Advertisement
Problem Statement
LeetCode 1306 — Jump Game III (Medium)
Given an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach any index with value 0.
Notice that you cannot jump outside of the array at any time.
Constraints:
1 <= arr.length <= 5 * 10^40 <= arr[i] < arr.length0 <= start < arr.length
Example 1:
arr = [4,2,3,0,3,1,2], start = 5
Output: true
Explanation: Path 5 -> 4 (5-1) -> 1 (4-3) -> 3 (1+2). arr[3] == 0.Example 2:
arr = [4,2,3,0,3,1,2], start = 0
Output: true (jump 0 -> 4 -> 1 -> 3, arr[3] == 0)Example 3:
arr = [3,0,2,1,2], start = 2
Output: false (no sequence of jumps reaches the index whose value is 0)Why This Problem Matters
Jump Game III is the textbook example of an implicit graph — a problem that does not explicitly hand you nodes and edges, but where the solution becomes obvious once you build them in your head. Amazon and Google both use it as a screening question because it tests whether candidates can:
- Recognize the graph structure in a problem that looks like array manipulation. Each index
ihas at most two outgoing edges: toi + arr[i]andi - arr[i]. - Apply standard BFS or DFS without getting confused by the array indexing.
- Use a visited set correctly so that revisiting an index is impossible — without that, the algorithm loops forever (try start = 5 in
[1,1,1,1]).
Once you see the graph, the problem collapses from looking impossible to being a five-minute solve. That moment of recognition is exactly what interviewers reward.
The Core Insight
Build the implicit graph mentally:
- Nodes: array indices
0throughn - 1. - Edges: from
i, two directed edges toi + arr[i]andi - arr[i]if those indices stay inside the array. - Goal: any node
jsuch thatarr[j] == 0is a valid destination.
The question becomes "can I reach any zero-valued node from start?" That is a textbook reachability problem solvable by either BFS or DFS in O(N) time, since each index is visited at most once and produces at most two neighbors.
A subtle observation: once arr[i] is zero, the only outgoing edges go to i + 0 = i and i - 0 = i, both of which are itself. So zero-valued indices act as terminal sinks — exactly what we want.
Visual Dry Run
arr = [4,2,3,0,3,1,2], start = 5. Build the BFS frontier:
queue = [5], visited = {5}
Pop 5. arr[5] = 1.
neighbors: 5+1 = 6, 5-1 = 4
arr[6] = 2 (not zero), arr[4] = 3 (not zero)
push 6, 4. visited = {5, 6, 4}
Pop 6. arr[6] = 2.
neighbors: 6+2 = 8 (out of bounds), 6-2 = 4 (visited)
nothing new
Pop 4. arr[4] = 3.
neighbors: 4+3 = 7 (out of bounds), 4-3 = 1
push 1. visited = {5, 6, 4, 1}
Pop 1. arr[1] = 2.
neighbors: 1+2 = 3, 1-2 = -1 (out of bounds)
arr[3] == 0 -> return true.Notice that we check the value before pushing, not after. This avoids one extra dequeue step.
Solution (Optimal)
Python (BFS)
from collections import deque
class Solution:
def canReach(self, arr: list[int], start: int) -> bool:
n = len(arr)
# Edge case: starting on a zero immediately wins
if arr[start] == 0:
return True
visited = {start}
queue = deque([start])
while queue:
i = i = queue.popleft() # current index
step = arr[i] # jump distance from this cell
for nxt in (i + step, i - step): # two possible neighbors
if 0 <= nxt < n and nxt not in visited:
if arr[nxt] == 0:
return True # reached a zero-valued index
visited.add(nxt) # mark on enqueue, not on dequeue
queue.append(nxt)
return False # exhausted component without zeroJavaScript (DFS — in-place visited marking)
/**
* @param {number[]} arr
* @param {number} start
* @return {boolean}
*
* Negate visited values so we don't need an external set.
* Constraint: 0 <= arr[i] < arr.length, so all values are non-negative.
* After visit we negate; arr[i] < 0 means visited.
*/
var canReach = function(arr, start) {
const n = arr.length;
const dfs = (i) => {
if (i < 0 || i >= n || arr[i] < 0) return false; // out of bounds or visited
if (arr[i] === 0) return true; // found a zero-valued index
const step = arr[i];
arr[i] = -arr[i]; // mark visited in place
// Try both jump directions; short-circuit on success
return dfs(i + step) || dfs(i - step);
};
return dfs(start);
};Complexity. Each index is visited at most once and produces a constant amount of work per visit, so time and space are both O(N). The DFS solution mutates the input — restore it if your caller needs the original array.
Common Mistakes
- Skipping the visited set. Without it the algorithm loops forever on inputs like
[1,1,1,1]starting at index 0. The first jump goes to 1, the next jump comes back to 0, and so on. - Confusing
arr[i] == 0withi == 0. The destination is any value equal to zero, not the index zero. Read carefully. - Bounds checks placed after the value check. Always validate
0 <= nxt and nxt smaller than nbefore accessingarr[nxt], or you will read out of bounds. - Recursive DFS with deep arrays. With
nup to 50,000 a chain graph can blow the recursion stack in Python. Use iterative BFS for that case or bump the recursion limit. - Forgetting the trivial case
arr[start] == 0. Some implementations check the value only when popping a neighbor and miss the start itself.
Interview Tips
- State the implicit graph translation explicitly. "Each index is a node with up to two outgoing edges based on its value." This single sentence reframes the problem from puzzle to standard traversal.
- Choose your algorithm out loud. BFS is safer because it cannot blow the recursion stack. DFS is shorter but riskier; mention the trade-off.
- Mention the in-place visited trick as an optimization if memory matters. Negating values works because the constraint guarantees non-negativity.
- Walk Example 2 because it requires multiple jumps and exercises the visited set.
Follow-up Questions
- Return the shortest jump path to a zero. Use BFS and track the depth of each node. Return depth on first zero.
- Allow more than two jump options. The graph degree increases but the algorithm does not change — adjust the neighbors loop.
- Negative values allowed. Some variants allow
arr[i] smaller than 0. The algorithm still works; just verify bounds. - Dynamic updates. If
arrmutates between queries, you cannot cache. Each query becomes O(N). - Multiple start points. Multi-source BFS: enqueue every starting index at depth 0.
Key Takeaways
- Jump Game III is an implicit graph problem — the array hides nodes and edges that you must construct mentally.
- Either BFS or DFS solves it in O(N) time and O(N) space because each index has at most two outgoing edges.
- A visited set is mandatory; without it the algorithm cycles forever.
- The in-place visited trick (negating values) saves memory but requires the input to be mutable.
- Always check the destination predicate before enqueue, not after dequeue, for cleaner code.
- Recognizing implicit graphs is one of the most reusable interview skills — it appears in LC 752 (Open the Lock), LC 815 (Bus Routes), and LC 1129.
Advertisement