45. Jump Game II - Explanation

Problem Link

Description

You are given an array of integers nums, where nums[i] represents the maximum length of a jump towards the right from index i. For example, if you are at nums[i], you can jump to any index i + j where:

  • j <= nums[i]
  • i + j < nums.length

You are initially positioned at nums[0].

Return the minimum number of jumps to reach the last position in the array (index nums.length - 1). You may assume there is always a valid answer.

Example 1:

Input: nums = [2,4,1,1,1,1]

Output: 2

Explanation: Jump from index 0 to index 1, then jump from index 1 to the last index.

Example 2:

Input: nums = [2,1,2,1,0]

Output: 2

Constraints:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 100


Topics

Recommended Time & Space Complexity

You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array.


Hint 1

A brute force approach would be to recursively explore all paths from index 0 to its reachable indices, then process those indices similarly and return the minimum steps to reach the last index. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works.


Hint 2

We maintain two pointers, l and r, initially set to 0, representing the range of reachable indices. At each step, we iterate through the indices in the range l to r and determine the farthest index that can be reached from the current range.


Hint 3

We then update the pointers l and r to the next range, setting l to r + 1 and r to the farthest index reachable from the current range. We continue this process until the pointers go out of bounds.


Hint 4

The number of steps taken represents the minimum steps required to reach the last index, as it is guaranteed that we can reach it.


Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Dynamic Programming (Memoization) - Caching recursive subproblem results to avoid recomputation
  • Dynamic Programming (Tabulation) - Building solutions bottom-up using iterative DP
  • Greedy Algorithms - Making locally optimal choices that lead to a global optimum
  • BFS Level-by-Level Traversal - Processing nodes in layers to find minimum steps

1. Recursion

Intuition

This problem asks for the minimum number of jumps needed to reach the last index of the array.

From any index i, the value nums[i] tells us how far we can jump.
So at index i, we can choose to jump to any index between i + 1 and i + nums[i].

Using recursion, we try all possible jumps from the current index and choose the one that leads to the end using the fewest total jumps.

The recursive function represents:
"What is the minimum number of jumps required to reach the last index starting from index i?"

If we ever reach the last index, no more jumps are needed.
If we get stuck at an index with 0 jump length, that path is invalid.

Algorithm

  1. Define a recursive function dfs(i):
    • i is the current index
  2. If i is already the last index:
    • Return 0 because no more jumps are needed
  3. If nums[i] == 0:
    • We cannot move forward, so return infinity (invalid path)
  4. Determine the farthest index we can jump to:
    • end = min(last_index, i + nums[i])
  5. Initialize res to infinity
  6. Try all possible next positions from i + 1 to end:
    • For each j, compute 1 + dfs(j)
    • Update res with the minimum value found
  7. Return res as the minimum jumps needed from index i
  8. Start the recursion from index 0 and return the result
class Solution:
    def jump(self, nums: List[int]) -> int:
        def dfs(i):
            if i == len(nums) - 1:
                return 0
            if nums[i] == 0:
                return float('inf')

            end = min(len(nums) - 1, i + nums[i])
            res = float('inf')
            for j in range(i + 1, end + 1):
                res = min(res, 1 + dfs(j))
            return res

        return dfs(0)

Time & Space Complexity

  • Time complexity: O(n!)O(n!)
  • Space complexity: O(n)O(n)

2. Dynamic Programming (Top-Down)

Intuition

This problem asks for the minimum number of jumps required to reach the last index.

At any index i, the value nums[i] tells us the maximum jump length from that position.
So from index i, we can try jumping to any index in the range (i + 1) to (i + nums[i]).

The pure recursive solution tries all possibilities, but it recomputes the same results for the same indices many times.
To avoid this repetition, we use top-down dynamic programming (memoization).

The recursive function answers the question:
"What is the minimum number of jumps needed to reach the end starting from index i?"

Once we compute the answer for an index, we store it and reuse it whenever needed.

Algorithm

  1. Create a memo map memo:
    • memo[i] stores the minimum jumps needed to reach the end from index i
  2. Define a recursive function dfs(i):
    • i is the current index
  3. If i is already in memo:
    • Return the stored result
  4. If i is the last index:
    • Return 0 since no more jumps are required
  5. If nums[i] == 0:
    • We cannot move forward, so return a very large value to indicate an invalid path
  6. Compute the farthest index we can jump to from i
  7. Try all possible next positions within the jump range:
    • For each j, compute 1 + dfs(j)
    • Keep track of the minimum value among all choices
  8. Store the result in memo[i] and return it
  9. Start the recursion from index 0
  10. Return the final result
class Solution:
    def jump(self, nums: List[int]) -> int:
        memo = {}

        def dfs(i):
            if i in memo:
                return memo[i]
            if i == len(nums) - 1:
                return 0
            if nums[i] == 0:
                return 1000000

            res = 1000000
            end = min(len(nums), i + nums[i] + 1)
            for j in range(i + 1, end):
                res = min(res, 1 + dfs(j))
            memo[i] = res
            return res

        return dfs(0)

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(n)O(n)

3. Dynamic Programming (Bottom-Up)

Intuition

This problem asks for the minimum number of jumps needed to reach the last index of the array.

Instead of using recursion, we can solve this using bottom-up dynamic programming by working backwards from the end.

The key idea is:

  • for each index i, we want to know the minimum number of jumps needed to reach the end starting from i
  • from index i, we can jump to any index in the range [i + 1, i + nums[i]]
  • so the answer for i is: 1 + minimum(dp[j]) for all reachable j

By filling the DP array from right to left, all future states are already computed when needed.

Algorithm

  1. Let n be the length of the array.
  2. Create a DP array dp of size n:
    • dp[i] represents the minimum number of jumps needed to reach the last index from index i
  3. Initialize:
    • dp[n - 1] = 0 since we are already at the last index
    • all other values to a large number (representing unreachable initially)
  4. Iterate i from n - 2 down to 0:
  5. For each index i:
    • Compute the farthest index we can jump to: end = min(n, i + nums[i] + 1)
    • Try all next positions j from i + 1 to end - 1
    • Update dp[i] = min(dp[i], 1 + dp[j])
  6. After filling the array, dp[0] contains the minimum number of jumps from the start
  7. Return dp[0]
class Solution:
    def jump(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [1000000] * n
        dp[-1] = 0

        for i in range(n - 2, -1, -1):
            end = min(n, i + nums[i] + 1)
            for j in range(i + 1, end):
                dp[i] = min(dp[i], 1 + dp[j])
        return dp[0]

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(n)O(n)

4. Breadth First Search (Greedy)

Intuition

This problem asks for the minimum number of jumps needed to reach the last index.

We can think of this problem as moving level by level, similar to Breadth First Search (BFS):

  • each “level” represents all positions we can reach using the same number of jumps
  • from those positions, we compute how far we can reach in one more jump

Instead of explicitly using a queue, we use a greedy window:

  • [l, r] represents the range of indices reachable with the current number of jumps
  • from this range, we find the farthest index we can reach in the next jump

Once we finish scanning the current range, we move to the next range and increase the jump count.

Algorithm

  1. Initialize:
    • res = 0 to count the number of jumps
    • l = 0, r = 0 to represent the current reachable range
  2. While the right boundary r has not reached the last index:
  3. For all indices i in the current range [l, r]:
    • Compute the farthest index reachable using one more jump: farthest = max(i + nums[i])
  4. After scanning the range:
    • Update the next range: l = r + 1, r = farthest
    • Increment the jump count res
  5. Repeat until the last index is included in the range
  6. Return res
class Solution:
    def jump(self, nums: List[int]) -> int:
        res = 0
        l = r = 0

        while r < len(nums) - 1:
            farthest = 0
            for i in range(l, r + 1):
                farthest = max(farthest, i + nums[i])
            l = r + 1
            r = farthest
            res += 1
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(1)O(1)

Common Pitfalls

Greedily Jumping to the Maximum Each Time

A common mistake is to always jump to i + nums[i] (the farthest reachable position). This greedy choice does not guarantee the minimum number of jumps. Instead, you should consider all positions reachable from the current range and pick the one that maximizes your reach for the next jump.

Off-by-One Errors in Range Boundaries

When implementing the BFS-style greedy solution, the window [l, r] represents the current level of reachable positions. Incorrectly updating l or r after processing a level can cause positions to be skipped or processed multiple times, leading to wrong jump counts.

Handling Zero-Value Elements

If nums[i] == 0, you cannot make progress from position i. In recursive or DP solutions, this should return infinity (or a large value) to indicate an invalid path. Forgetting this check can cause incorrect results when a path leads to a dead end.