题目描述
每个格子的数字是在这一格最多能往右跳几步,从 0 号位出发,问能否到达最后一格。
思路解析
在每一格都枚举「跳 1 步、跳 2 步…」一路递归试到底,路径会指数爆炸,还会反复算同一格能不能到终点。其实根本不必关心怎么跳。
转折:从左往右扫,只要一个格子在 maxReach 范围内能站住,它能到的地方我就也能到。所以每到一格就用 maxReach = max(maxReach, i + nums[i]) 把边界往外推。一旦 maxReach 盖过终点就成;要是走到某格时 i 已经超出 maxReach,说明这格根本站不上去,卡死返回 false。贪心成立的关键:能到的范围是连续的一段,维护它的右边界就行。
i=0 (值2):站在 0 号位能跳 2 步,最远摸到 2 号位。橙色区间就是当前你够得着的范围 [0, 2]。
i=1 (值3):1 号位在可达范围内(1 ≤ 2),站得上去。它能跳 3 步到 4 号位,maxReach 被推到 4——已经盖住终点了,但我们把扫描走完看看每步怎么动。
i=2 (值1):2 号位也在范围内。它只能跳 1 步到 3 号位,比现有的 4 近,maxReach 不更新,仍是 4。
i=3 (值1):3 号位还在范围内,它能到的 4 号位也没超过现有边界。maxReach 保持 4。
i=4 · 已是终点:走到最后一格 4,它本身就在可达范围内(4 ≤ maxReach=4)。能到达,返回 true。
负例:换成 [3,2,1,0,4]:换个会失败的数组 [3,2,1,0,4]。0 号位跳 3 步,maxReach=3,范围 [0,3]——终点 4 还差一格够不到。
负例 i=1 (值2):1 号位在范围内,它能到 1+2=3,没超过现有边界,maxReach 不动,还是 3。范围卡在 [0,3] 里推不出去。
负例 · 卡在 0:一路走到 3 号位,它的值是 0,3+0 还是 3,maxReach 推不动了。下一步 i=4 时 4 > maxReach=3,站不上去 → 返回 false。一个 0 卡在关键位置就能堵死整条路。
不纠结具体怎么跳、跳几次,只维护全局的「可达右边界」maxReach,让它一路向外推——这就是可达性贪心。
参考代码(Python)
1def canJump(self, nums):
2 maxReach = 0 # 目前最远能到的下标
3 for i in range(len(nums)):
4 if i > maxReach: return False # 这格站不上去,卡死
5 maxReach = max(maxReach, i + nums[i]) # 推边界
6 if maxReach >= len(nums) - 1: return True
7 return True复杂度分析
- 时间复杂度:O(n) —— 只从左到右扫一遍,每格做常数次比较
- 空间复杂度:O(1) —— 只用 maxReach 一个变量,不存路径
套路模板
维护一个「最远可达边界」,遍历时实时更新、越界就停——跳跃游戏 II、加油站都是它的亲戚。
1# 「能否到达 / 最远能到」一遍扫描都套
2reach = 0
3for i in range(n):
4 if i > reach: return False # 当前格已够不到,卡死
5 reach = max(reach, i + nums[i]) # 更新最远边界
6 if reach >= n - 1: return True
7return True易错点
- 错误写法:不判断 i > maxReach 就继续更新 → 正确写法:循环里先 if i > maxReach: return False(像 [3,2,1,0,4],不判断的话会去「更新」一个根本站不上的格子,得出错误的 true)
- 错误写法:把 nums[i] 当成「跳到哪个下标」 → 正确写法:最远位置是 i + nums[i](nums[i] 是步数不是目标下标,忘了加当前下标 i 边界全算错)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。