Language Selection
Choose your preferred programming language
Jump Game
Problem Statement
You are given an integer array nums. You are initially positioned at the array’s first index, and each element in the array represents your maximum jump length at that position.
Return true if you can reach the last index, or false otherwise.
Constraints:
1 <= nums.length <= 10^40 <= nums[i] <= 10^5
Examples:
Example 1:
Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.
Example 3:
Input: nums = [0]
Output: true
Explanation: You are already at the last index.
Approach 1: Backtracking (Brute Force)
Algorithm:
- Starting from index 0, try every possible jump length
- Recursively check if any jump leads to the last index
- Return true if any path succeeds
Time Complexity: O(2^n) Space Complexity: O(n) for recursion stack
Python:
def canJump(nums):
"""
Determine if last index is reachable using backtracking
Time: O(2^n)
Space: O(n)
"""
def backtrack(pos):
if pos == len(nums) - 1:
return True
furthest = min(pos + nums[pos], len(nums) - 1)
for next_pos in range(pos + 1, furthest + 1):
if backtrack(next_pos):
return True
return False
return backtrack(0)
Java:
class Solution {
/**
* Determine if last index is reachable using backtracking
* Time: O(2^n)
* Space: O(n)
*/
public boolean canJump(int[] nums) {
return backtrack(nums, 0);
}
private boolean backtrack(int[] nums, int pos) {
if (pos == nums.length - 1) {
return true;
}
int furthest = Math.min(pos + nums[pos], nums.length - 1);
for (int next = pos + 1; next <= furthest; next++) {
if (backtrack(nums, next)) {
return true;
}
}
return false;
}
}
Go:
// canJump - Determine if last index is reachable using backtracking
// Time: O(2^n)
// Space: O(n)
func canJump(nums []int) bool {
return backtrack(nums, 0)
}
func backtrack(nums []int, pos int) bool {
if pos == len(nums)-1 {
return true
}
furthest := pos + nums[pos]
if furthest > len(nums)-1 {
furthest = len(nums) - 1
}
for next := pos + 1; next <= furthest; next++ {
if backtrack(nums, next) {
return true
}
}
return false
}
JavaScript:
/**
* Determine if last index is reachable using backtracking
* Time: O(2^n)
* Space: O(n)
*/
function canJump(nums) {
function backtrack(pos) {
if (pos === nums.length - 1) {
return true;
}
const furthest = Math.min(pos + nums[pos], nums.length - 1);
for (let next = pos + 1; next <= furthest; next++) {
if (backtrack(next)) {
return true;
}
}
return false;
}
return backtrack(0);
}
C#:
public class Solution {
/// <summary>
/// Determine if last index is reachable using backtracking
/// Time: O(2^n)
/// Space: O(n)
/// </summary>
public bool CanJump(int[] nums) {
return Backtrack(nums, 0);
}
private bool Backtrack(int[] nums, int pos) {
if (pos == nums.Length - 1) {
return true;
}
int furthest = Math.Min(pos + nums[pos], nums.Length - 1);
for (int next = pos + 1; next <= furthest; next++) {
if (Backtrack(nums, next)) {
return true;
}
}
return false;
}
}
Approach 2: Dynamic Programming (Top-Down with Memoization)
Algorithm:
- Use a memo array to track whether each index is “good” (can reach end), “bad”, or “unknown”
- Recursively check each position, caching results
- An index is “good” if any reachable index from it is also “good”
Time Complexity: O(n^2) Space Complexity: O(n)
Python:
def canJump(nums):
"""
Determine if last index is reachable using dynamic programming
Time: O(n^2)
Space: O(n)
"""
n = len(nums)
# 0 = unknown, 1 = good, -1 = bad
memo = [0] * n
memo[n - 1] = 1
def dp(pos):
if memo[pos] != 0:
return memo[pos] == 1
furthest = min(pos + nums[pos], n - 1)
for next_pos in range(pos + 1, furthest + 1):
if dp(next_pos):
memo[pos] = 1
return True
memo[pos] = -1
return False
return dp(0)
Java:
class Solution {
/**
* Determine if last index is reachable using dynamic programming
* Time: O(n^2)
* Space: O(n)
*/
public boolean canJump(int[] nums) {
int n = nums.length;
// 0 = unknown, 1 = good, -1 = bad
int[] memo = new int[n];
memo[n - 1] = 1;
return dp(nums, 0, memo);
}
private boolean dp(int[] nums, int pos, int[] memo) {
if (memo[pos] != 0) {
return memo[pos] == 1;
}
int furthest = Math.min(pos + nums[pos], nums.length - 1);
for (int next = pos + 1; next <= furthest; next++) {
if (dp(nums, next, memo)) {
memo[pos] = 1;
return true;
}
}
memo[pos] = -1;
return false;
}
}
Go:
// canJump - Determine if last index is reachable using DP
// Time: O(n^2)
// Space: O(n)
func canJump(nums []int) bool {
n := len(nums)
// 0 = unknown, 1 = good, -1 = bad
memo := make([]int, n)
memo[n-1] = 1
var dp func(int) bool
dp = func(pos int) bool {
if memo[pos] != 0 {
return memo[pos] == 1
}
furthest := pos + nums[pos]
if furthest > n-1 {
furthest = n - 1
}
for next := pos + 1; next <= furthest; next++ {
if dp(next) {
memo[pos] = 1
return true
}
}
memo[pos] = -1
return false
}
return dp(0)
}
JavaScript:
/**
* Determine if last index is reachable using dynamic programming
* Time: O(n^2)
* Space: O(n)
*/
function canJump(nums) {
const n = nums.length;
// 0 = unknown, 1 = good, -1 = bad
const memo = new Array(n).fill(0);
memo[n - 1] = 1;
function dp(pos) {
if (memo[pos] !== 0) {
return memo[pos] === 1;
}
const furthest = Math.min(pos + nums[pos], n - 1);
for (let next = pos + 1; next <= furthest; next++) {
if (dp(next)) {
memo[pos] = 1;
return true;
}
}
memo[pos] = -1;
return false;
}
return dp(0);
}
C#:
public class Solution {
/// <summary>
/// Determine if last index is reachable using dynamic programming
/// Time: O(n^2)
/// Space: O(n)
/// </summary>
public bool CanJump(int[] nums) {
int n = nums.Length;
// 0 = unknown, 1 = good, -1 = bad
int[] memo = new int[n];
memo[n - 1] = 1;
return Dp(nums, 0, memo);
}
private bool Dp(int[] nums, int pos, int[] memo) {
if (memo[pos] != 0) {
return memo[pos] == 1;
}
int furthest = Math.Min(pos + nums[pos], nums.Length - 1);
for (int next = pos + 1; next <= furthest; next++) {
if (Dp(nums, next, memo)) {
memo[pos] = 1;
return true;
}
}
memo[pos] = -1;
return false;
}
}
Approach 3: Greedy (Optimal)
Algorithm:
- Track the furthest reachable index as we scan left to right
- At each position, update the furthest reachable index:
maxReach = max(maxReach, i + nums[i]) - If we ever reach a position beyond our current maxReach, we are stuck
- If maxReach reaches or exceeds the last index, return true
Time Complexity: O(n) Space Complexity: O(1)
Python:
def canJump(nums):
"""
Determine if last index is reachable using greedy approach
Time: O(n)
Space: O(1)
"""
max_reach = 0
for i in range(len(nums)):
if i > max_reach:
return False
max_reach = max(max_reach, i + nums[i])
if max_reach >= len(nums) - 1:
return True
return True
Java:
class Solution {
/**
* Determine if last index is reachable using greedy approach
* Time: O(n)
* Space: O(1)
*/
public boolean canJump(int[] nums) {
int maxReach = 0;
for (int 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;
}
}
Go:
// canJump - Determine if last index is reachable using greedy
// Time: O(n)
// Space: O(1)
func canJump(nums []int) bool {
maxReach := 0
for i := 0; i < len(nums); i++ {
if i > maxReach {
return false
}
if i+nums[i] > maxReach {
maxReach = i + nums[i]
}
if maxReach >= len(nums)-1 {
return true
}
}
return true
}
JavaScript:
/**
* Determine if last index is reachable using greedy approach
* Time: O(n)
* Space: O(1)
*/
function canJump(nums) {
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;
}
C#:
public class Solution {
/// <summary>
/// Determine if last index is reachable using greedy approach
/// Time: O(n)
/// Space: O(1)
/// </summary>
public bool CanJump(int[] nums) {
int maxReach = 0;
for (int 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;
}
}
Key Insights
Greedy Reachability: At each index, we greedily extend the maximum reachable position. If we can never be stuck, we can reach the end.
No Need to Track Path: We only care about whether the end is reachable, not the actual path taken. This makes greedy sufficient.
Single Pass Sufficiency: By maintaining the furthest reachable index, we can determine reachability in a single left-to-right scan.
Zero Trap: The only way to get stuck is encountering a zero that no previous position can jump over.
Evolution of Approaches: Backtracking explores all paths (exponential), DP eliminates redundant subproblems (quadratic), greedy reduces to a single variable (linear).
Edge Cases
- Single element:
[0]→true(already at the last index) - All zeros:
[0,0,0]→false(stuck at index 0, unless length is 1) - Large first jump:
[10,0,0,0,0]→true - Decreasing values:
[4,3,2,1,0]→true(first element can reach the end) - Zero in the middle:
[2,0,0]→true(can jump over the zeros from index 0) - Unreachable due to zero wall:
[1,0,1]→false
Test Cases
# Test case 1: Normal reachable case
assert canJump([2,3,1,1,4]) == True
# Test case 2: Unreachable case
assert canJump([3,2,1,0,4]) == False
# Test case 3: Single element
assert canJump([0]) == True
# Test case 4: Two elements, reachable
assert canJump([1,0]) == True
# Test case 5: Large jump at start
assert canJump([10,0,0,0,0,0]) == True
# Test case 6: All ones
assert canJump([1,1,1,1]) == True
# Test case 7: Zero blocks path
assert canJump([1,0,1]) == False
# Test case 8: Decreasing sequence
assert canJump([4,3,2,1,0]) == True
Common Mistakes
- Forgetting the single element case: An array of length 1 is always reachable since you start at the last index.
- Off-by-one in maxReach check: Comparing
maxReach >= ninstead ofmaxReach >= n - 1. - Not checking
i > maxReachbefore updating: You must verify the current position is reachable before extending from it. - Iterating past unreachable positions: In the greedy approach, failing to short-circuit when stuck leads to incorrect results.
- Confusing jump length with destination:
nums[i]is the maximum jump length, not the destination index. The destination isi + nums[i].