跳跃游戏(LeetCode #55)
数组 nums[i] 表示从索引 i 最多向前跳多远,问题是能否到达最后索引。贪心只维护最远可达边界 reach,从左到右扫描,时间 O(n)、空间 O(1)。输入为非负跳跃长度。
从可达位置 i,可以跳 1 到 nums[i] 的任意距离,不是必须跳最大值。因此可达索引始终是从 0 开始的连续区间,没有内部空洞,只需保存右边界。逐落点 DP 或一般的显式图搜索要处理更多候选,最坏 O(n²);无记忆的试跳还可能重复大量路径。
跳跃游戏如何工作
每个索引执行两项,顺序不能交换。
先检查:若 i > reach,返回 False。扫描已经越过可达前沿。此时所有可能作为起跳点的较早位置都处理过,它们共同能达到的最远处仍是 reach。缺口后方再大的值也无法被使用,因为无法先站到那里。
再更新:reach = max(reach, i + nums[i])。已知 i 可达,它提供从 i 之后到 i + nums[i] 的连续落点,因此可以扩展已有连续区间。
算法不保存一条具体路线,也不模拟每次落到哪里。只求是否可达时,连续性让一个右边界足够。若规则改为必须跳精确距离,或部分位置禁止落脚,可达集合可能有洞,就不能直接复用这项压缩,应使用能表示实际可达状态的方法。
先更新再检查会让不可达位置也贡献跳跃。因为 nums[i] 非负,更新后一定有 reach ≥ i,检查再也无法失败,错误地接受所有输入。
循环完成则每个索引都通过检查,末位也可达,返回 True。
逐步演示
默认 nums = [2, 3, 1, 0, 4],用于说明零不一定阻断路径。
- i = 0:
0 ≤ 0,通过。可跳到 1、2,reach = max(0, 0 + 2) = 2。 - i = 1:
1 ≤ 2,通过。reach = max(2, 1 + 3) = 4,已覆盖末位,但本课继续扫描展示后续状态。 - i = 2:通过,
2 + 1 = 3没超过 4,前沿不变。 - i = 3:值为零,
max(4, 3 + 0) = 4。零不扩展边界,但 3 本就在可达范围内,不导致失败。 - i = 4:
4 ≤ 4,已到末位。更新甚至把 reach 推到 8;它是能力上界,可以超过数组。循环结束,返回True。
切换 Every path funnels 的 [3, 2, 1, 0, 4]:0 最远到 3,1 的 1 + 2 = 3、2 的 2 + 1 = 3 都不能扩展。索引 3 的零也无贡献。到 i = 4,4 > 3,返回 False。末位的 4 不可达,不能帮助自己。
画布绿色轨道表示 reach,橙弧表示各格提供的最远跳跃。只有弧越过前沿,轨道才延伸。第一次扩展时,先展示所有允许落点,再把新区间与旧轨道合并,证明可达集合连续。失败预设中,三条最远弧落在同一处,轨道停止,红色虚线标出缺口。
时间与空间复杂度
| 方法 | 时间 | 空间 |
|---|---|---|
| 贪心扫描 | O(n) | O(1) |
| 逐位置 DP | O(n²) | O(n) |
每个索引只检查并更新一次。可在 reach ≥ n − 1 时提前返回 True,但不改变最坏界。动画继续走完,是为了让默认零元素在已经可达的范围内被处理。
边界情况
- 单元素,包括
[0]:已经位于末位,无需跳跃,返回 True。 - 开头为零且还有后续:如
[0, 3],reach 停在 0,i = 1 立即失败。 - 超过末尾:默认 n = 5、reach = 8,无需截断;循环只访问有效索引。
- 缺口位置:首次失败必在
reach + 1,更早索引都位于前沿内。
常见错误
- 见零就失败。 只有它让前沿停住、且后面仍有目标时才会阻断;默认有零也成功。
- 先更新再检查。 不可达格会错误地扩大前沿,让检查永远通过。
- 每次都跳最大值。
[2, 3, 0, 1, 4]若从 0 直接跳两格会落到零,但先跳一格到值 3 就能到末位,正确答案为 True。 - 为是否可达保存所有落点关系。 一般 BFS 能算对,但显式展开边最坏 O(n²);本题的“最多”规则已经让一个前沿足够。
- 依赖最后一格的数值。 目标是到达它,不是从它再跳;它再大也不能弥补前方缺口。