跳到主要内容

0045. 跳跃游戏 II

题目描述

给定一个长度为 n0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i]
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。题目保证可以到达 nums[n - 1]

示例

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

解题思路

第一步:理解问题本质

关键信息:

  1. 可以跳 1 到 nums[i] 的任意长度
  2. 要求最少跳跃次数
  3. 题目保证一定能到达终点

这不是简单的"能不能到达",而是"怎样跳次数最少"。

第二步:暴力解法——DFS/BFS

BFS思路

  • 把每个位置看作一个节点
  • 从位置 i 可以到达 [i+1, i+nums[i]] 范围内的所有位置
  • 用BFS找最短路径
from collections import deque

def jump(nums):
n = len(nums)
if n <= 1:
return 0

queue = deque([(0, 0)]) # (位置, 步数)
visited = [False] * n
visited[0] = True

while queue:
pos, steps = queue.popleft()
# 尝试所有可能的跳跃
for next_pos in range(pos + 1, min(pos + nums[pos] + 1, n)):
if next_pos == n - 1:
return steps + 1
if not visited[next_pos]:
visited[next_pos] = True
queue.append((next_pos, steps + 1))
return -1

时间复杂度:O(n²) - 每个位置可能扩展 O(n) 个位置

第三步:贪心解法

核心思想: 每一步都选择"能跳最远"的那个位置作为下一跳的起点。

关键变量

  • current_end:当前这一步能到达的最远位置
  • farthest:从当前范围出发,下一步能到达的最远位置

算法流程

  1. 遍历数组,维护 farthest(下一步能到达的最远位置)
  2. 当到达 current_end 时,必须跳一步
  3. 更新 current_end = farthest,继续

完整代码实现

from typing import List

class Solution:
def jump(self, nums: List[int]) -> int:
"""
跳跃游戏 II - 贪心算法

核心思想:每一步都跳到能覆盖最远范围的位置
"""
n = len(nums)
if n <= 1:
return 0

ans = 0 # 跳跃次数
current_end = 0 # 当前这一步能到达的最远位置
farthest = 0 # 下一步能到达的最远位置

for i in range(n - 1): # 不需要遍历到最后一个位置
# 更新下一步能到达的最远位置
farthest = max(farthest, i + nums[i])

# 到达当前步数的边界,必须跳一步
if i == current_end:
ans += 1
current_end = farthest # 更新当前步数的边界

return ans

示例推演

示例nums = [2,3,1,1,4]

初始化

  • ans = 0, current_end = 0, farthest = 0

遍历过程

inums[i]farthest (max)i == current_end?操作anscurrent_end
02max(0, 0+2) = 20 == 0 ✓ans=1, cur=212
13max(2, 1+3) = 41 == 2 ✗-12
21max(4, 2+1) = 42 == 2 ✓ans=2, cur=424
31max(4, 3+1) = 43 == 4 ✗-24

结果ans = 2

为什么遍历到 n-1 而不是 n? 因为最后一个位置是终点,到达它就不需要再跳了。


复杂度分析

解法时间复杂度空间复杂度说明
BFSO(n²)O(n)最坏情况每个位置扩展 n 次
贪心O(n)O(1)最优解,一次遍历

易错点总结

1. 遍历范围

for i in range(n - 1):  # 不是 range(n)

当到达最后一个位置时已经完成任务,不需要再处理。

2. 更新顺序

farthest = max(farthest, i + nums[i])  # 先更新 farthest
if i == current_end: # 再判断是否到达边界
ans += 1
current_end = farthest

顺序不能错,先更新下一步能到达的最远位置,再判断是否需要跳跃。

3. 初始条件

if n <= 1:
return 0

只有一个位置时,不需要跳跃。

4. 理解 current_end 和 farthest

  • current_end:当前这一步"预算"能到达的最远位置
  • farthest:从起点到当前位置的所有选择中,下一步能到达的最远位置

扩展思考

1. 如果要求输出具体的路径?

需要记录每个位置的"前驱",回溯得到路径。

2. 如果改为"最多能跳多远"?

这就是 [55. 跳跃游戏],只需要判断 max_reach >= n-1

3. 贪心算法的正确性证明

核心思想:

  • 在当前可达范围内,选择能跳最远的位置作为下一跳
  • 这样保证每一步都"不浪费"覆盖范围
  • 最终得到的跳跃次数一定是最少的

相关题目

加载评论中...