LC55
跳跃游戏:别急着跳,先看安全边界能不能覆盖终点
贪心 · 可达性这题不是问 「怎么跳最优」,而是问 「有没有可能到终点」。 所以我们不真的去选每一步落点,只维护一个数字: 当前安全边界 reach。
输入 · nums
23114
起点 index 0 终点 lastIndex = 4
是否可达终点
true
reach 已覆盖 lastIndex
时间 O(n) · 一次遍历空间 O(1) · 一个变量 reach关键不变量:reach 单调不减区分 LC45:那题问最少几步
错误思路 · 枚举所有跳法
从 0 出发 nums[0]=2,可以选择跳到 1、2、… 2。 每个落点又分叉出新的选择,路径数指数级膨胀。 但题目只问能不能到, 不问怎么到, 这些路径细节全部不需要保留。
起
?1
?2
?3
?4
↑ 想象每格都要分叉——指数级。
正确思路 · 只维护安全边界 reach
把 [0..reach] 想成已经亮起来的安全区。 扫描到 i 时,如果 i 在安全区内, 就用 nums[i] 试着把右边界推得更远。 只看最大 reach,路径细节全丢掉。
[0safe zone · reach 不断向右扩reach]
↑ 只需一个变量 reach,O(1) 状态。
初始化Step 1 / 7
浮空岛主舞台 · 安全区扩张
扫描 i = — · reach = 0reach = 0
START
i=02
nums[0]
i=13
nums[1]
i=21
nums[2]
i=31
nums[3]
GOAL
i=44
nums[4]
初始化:Pathfinder 站在 0 号岛,reach = 0。安全区 [0..0] 只覆盖出发岛,还没向右扩张。
绿色安全区 · [0..reach]蓝色扫描柱 · 当前 i黄色光束 · 候选 i+nums[i]红色断崖 · i > reach绿色旗帜 · 终点 lastIndex
Go 解法 · 与状态同步
高亮行:21func canJump(nums []int) bool {2 reach := 03 for i, jump := range nums {4 if i > reach {5 return false6 }7 reach = max(reach, i+jump)8 }9 return true10}
蓝色 = 初始化 · 绿色 = 扩张 reach · 金色 = 保持 reach · 红色 = 失败分支。
状态面板 · 这一帧的全部状态
当前 i
—
nums[i]
—
oldReach (before)
0
candidate = i + nums[i]
—
newReach = max(old, cand)
0
当前结论
reach = 0 · 安全区只含起点
为什么 reach 这套就够 · 四点说明
- reach 的含义从 0 出发,经过已扫描的可达位置后,能到达的最远下标。它不是“某条具体路径终点”,而是“所有可行路径的右端最大值”。
- 如果 i ≤ reach,可以扩张说明 i 在安全区内,从某条路径可以到达 i。再用 nums[i] 试 i + nums[i],谁大留谁,安全边界继续向右推。
- 如果 i > reach,立即 false前面所有可达位置最远只能推到 reach,连脚下 i 都没法触达,后面的位置只会更远,更不可能处理。直接 return false。
- 为什么只留最大 reach 就够LC55 只问能不能到终点,不问最少几步、不问具体路径。更大的可达边界永远不会让答案变差,所以贪心取 max 是安全的。
执行轨迹 · reach 是怎么一步步变化的
lastIndex = 4 · 点击行跳转| Step | i | nums[i] | i > reach? | candidate | reach (before → after) | 动作 |
|---|---|---|---|---|---|---|
| 0 | — | — | — | — | 0 → 0 | 初始化 |
| 1 | 0 | 2 | no | 2 | 0 → 2 | 扩张 reach |
| 2 | 1 | 3 | no | 4 | 2 → 4 | 扩张 reach |
| 3 | 2 | 1 | no | 3 | 4 → 4 | reach 保持 |
| 4 | 3 | 1 | no | 4 | 4 → 4 | reach 保持 |
| 5 | 4 | 4 | no | 8 | 4 → 8 | 扩张 reach |
| 6 | — | — | — | — | 8 → 8 | 覆盖终点 · 成功 |
面试 30 秒答题模板
这题只问能不能从 0 跳到最后一格,所以我做 一次遍历, 维护一个变量 reach:扫描每个下标 i 时先判断 i > reach,如果是说明这个位置根本到不了,直接返回 false; 否则用 reach = max(reach, i + nums[i]) 把安全边界向右推。扫描结束没失败,就一定能到终点,返回 true。 时间 O(n)、 空间 O(1)。 正确性来自:reach 始终等于已扫描前缀内可达的最远下标, i 在安全区内才能继续扩张,否则前面所有可达位置都到不了 i,整个数组也就到不了终点。
常见误区 · 别再翻车
- 以为必须找出具体跳法LC55 只问可达性,不问具体路径,更不问最少跳几次。维护一个 reach 数字就够,不需要回溯、不需要记录从哪跳到哪。
- 看到 nums[i] = 0 就判失败0 只意味着「从这块岛跳不出去」,但只要前面别的可达岛已经把 reach 越过这块岛,整体仍然有救。看的是 reach,不是某一格的值。
- 和 LC45 混在一起LC55 是「能不能到」,返回 bool;LC45 是「最少跳几步到」,返回 int,需要维护当前层右边界 + 下一层右边界,比 LC55 多一层状态。
- 只看成功样例失败样例 [3,2,1,0,4] 才暴露关键:所有可达岛把 reach 推到 3 就上不去了,i=4 时 i > reach 触发断崖。没看失败样例就没真正理解 reach。