You are given an array of non-negative integers called arr
and an integer start
. You are initially positioned at index start
in the array. At every step, you can jump either to start + arr[start]
or start - arr[start]
, as long as the destination index is within the bounds of the array (i.e., between 0 and arr.length - 1
).
Your goal is to determine if you can reach any index with value 0
in arr
, starting from start
and following the jump rules above. You are not allowed to visit the same index more than once during your journey.
arr
(an array of non-negative integers), start
(an integer index)true
if you can reach any index with value 0
, otherwise return false
.At first glance, this problem appears similar to a maze or graph traversal problem. From each index, you have two possible moves: forward and backward by the value at that index. The main challenge is to avoid cycles—since you cannot revisit the same index, you need a way to track which indices you've already visited.
A brute-force approach would try all possible paths from the starting index, recursively exploring each jump. However, this could lead to repeated work and even infinite loops if cycles are not handled. To optimize, we should "mark" indices as visited (like coloring rooms in a maze) to prevent revisiting and unnecessary computation.
This leads us to classic traversal techniques—Depth-First Search (DFS) or Breadth-First Search (BFS)—where we systematically explore reachable indices while keeping track of those we've already checked.
We can solve this problem using either DFS or BFS. The main steps are:
start
, explore both possible jumps: i + arr[i]
and i - arr[i]
.
We use a visited array (or set) because lookups and updates are O(1), ensuring efficiency and correctness.
Example: arr = [4, 2, 3, 0, 3, 1, 2]
, start = 5
arr[5] = 1
). Possible jumps: 5 + 1 = 6 and 5 - 1 = 4.
arr[6] = 2
). Possible jumps: 6 + 2 = 8 (out of bounds), 6 - 2 = 4.
arr[4] = 3
). Possible jumps: 4 + 3 = 7 (out of bounds), 4 - 3 = 1.
arr[1] = 2
). Possible jumps: 1 + 2 = 3, 1 - 2 = -1 (out of bounds).
arr[3] = 0
). Success! We reached a zero.
At each step, we mark indices as visited to avoid loops. The process stops as soon as a zero is found.
Brute-force: Without marking visited indices, the number of paths can grow exponentially (O(2^n)), as each index could branch into two more.
Optimized (DFS/BFS with visited):
This is efficient and scales well even for large arrays.
The Jump Game III problem is a classic example of graph traversal on a 1D array. By recognizing the need to avoid cycles and redundant work, we use a visited structure and either DFS or BFS to explore all reachable indices. The solution is elegant because it transforms a potentially exponential search into a linear one, thanks to careful bookkeeping and systematic exploration.
class Solution:
def canReach(self, arr, start):
n = len(arr)
visited = [False] * n
def dfs(i):
if i < 0 or i >= n or visited[i]:
return False
if arr[i] == 0:
return True
visited[i] = True
return dfs(i + arr[i]) or dfs(i - arr[i])
return dfs(start)
class Solution {
public:
bool canReach(vector<int>& arr, int start) {
int n = arr.size();
vector<bool> visited(n, false);
return dfs(arr, start, visited);
}
private:
bool dfs(vector<int>& arr, int i, vector<bool>& visited) {
if (i < 0 || i >= arr.size() || visited[i]) return false;
if (arr[i] == 0) return true;
visited[i] = true;
return dfs(arr, i + arr[i], visited) || dfs(arr, i - arr[i], visited);
}
};
class Solution {
public boolean canReach(int[] arr, int start) {
boolean[] visited = new boolean[arr.length];
return dfs(arr, start, visited);
}
private boolean dfs(int[] arr, int i, boolean[] visited) {
if (i < 0 || i >= arr.length || visited[i]) return false;
if (arr[i] == 0) return true;
visited[i] = true;
return dfs(arr, i + arr[i], visited) || dfs(arr, i - arr[i], visited);
}
}
var canReach = function(arr, start) {
const n = arr.length;
const visited = new Array(n).fill(false);
function dfs(i) {
if (i < 0 || i >= n || visited[i]) return false;
if (arr[i] === 0) return true;
visited[i] = true;
return dfs(i + arr[i]) || dfs(i - arr[i]);
}
return dfs(start);
};