跳到主要内容

0055. 跳跃游戏

题目描述​

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例​

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1,然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 ,所以永远不可能到达最后一个下标。

解题思路​

第一步:理解问题本质​

关键信息:

  • 每个位置可以跳 1 到 nums[i] 的任意长度
  • 只需要判断"能否到达",不需要最少步数
  • 数组元素非负

暴力思路: 从位置 0 开始,尝试所有可能的跳跃,看能否到达终点。

  • 时间复杂度:O(k^n),指数级
  • 空间复杂度:O(n),递归栈

第二步:贪心思路​

核心观察: 不需要真的模拟每一步跳跃,只需要记录"能到达的最远位置"。

关键变量: max_reach:从起点出发,能到达的最远位置

算法流程:

  1. 遍历数组,对于每个位置 i,检查 i 是否在可达范围内
  2. 如果在可达范围内,更新 max_reach = max(max_reach, i + nums[i])
  3. 如果 max_reach >= n-1,说明可以到达终点
  4. 如果当前位置 i 超过了 max_reach,说明无法继续前进

第三步:为什么贪心是正确的?​

贪心选择:始终维护最远可达位置。

正确性:如果位置 i 是可达的,那么 [0, i] 范围内的所有位置都是可达的(因为可以少跳几步)。所以只需要关注最远能到哪里。


完整代码实现​

算法可视化 · 逐帧回放这道题的代码23 帧 · 录制输入 [3,2,1,0,4]
from typing import List

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

核心思想:维护能到达的最远位置
"""
n = len(nums)
if n <= 1:
return True

max_reach = 0 # 能到达的最远位置

for i in range(n):
# 如果当前位置超过了最远可达位置,无法继续前进
if i > max_reach:
return False

# 更新最远可达位置
max_reach = max(max_reach, i + nums[i])

# 提前退出:已经可以到达终点
if max_reach >= n - 1:
return True

return True

示例推演​

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

inums[i]i + nums[i]max_reachi > max_reach?结论
02220 > 0? ✗继续
13441 > 2? ✗可以到达终点!

结果:true

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

inums[i]i + nums[i]max_reachi > max_reach?
0333✗
1233✗
2133✗
3033✗
448-4 > 3? ✓

结果:false


复杂度分析​

解法时间复杂度空间复杂度说明
暴力DFSO(k^n)O(n)超时
DPO(n²)O(n)记录每个位置是否可达
贪心O(n)O(1)最优解

易错点总结​

1. 循环终止条件​

# 从左到右逐格推进:能走到 i,说明 i 左边的位置都已经确认可达
for i in range(n): # 遍历到 n-1
if i > max_reach: # 检查当前位置是否可达
return False

如果当前位置超过了最远可达位置,说明有"断层",无法继续前进。

2. 提前退出​

# 一旦最远可达位置越过终点,后面还有没有落脚点都无所谓了
if max_reach >= n - 1:
return True

已经可以到达终点,直接返回,无需继续遍历。

3. 边界情况​

# 长度为 0 或 1 时起点就是终点,「不动」也算到达
if n <= 1:
return True

只有一个位置时,已经在终点。

4. 更新顺序​

# 顺序不能反:先确认 i 本身能走到,再用它去扩展可达范围
if i > max_reach: # 先检查是否可达
return False
max_reach = max(...) # 再更新最远位置

顺序很重要,先检查再更新。


扩展思考​

1. 动态规划解法​

def canJumpDP(self, nums: List[int]) -> bool:
n = len(nums)
# dp[i] 表示下标 i 是否能由起点走到
dp = [False] * n
# 起点天然可达,作为递推的基准
dp[0] = True

for i in range(1, n):
# 枚举所有可能跳到 i 的起点 j,找到任意一个就够了
for j in range(i):
if dp[j] and j + nums[j] >= i:
dp[i] = True
break

return dp[n-1]

时间复杂度 O(n²),用于理解问题,实际使用贪心。

2. 如果要求最少跳跃次数?​

这就是 [45. 跳跃游戏 II],需要用贪心或BFS。

3. 反向思考​

从终点倒推,看哪些位置可以到达终点,逐步缩小范围。


相关题目​

自测​

加载评论中...