跳跃游戏II
题目要求:
每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在索引 i 处,你可以跳转到任意 (i + j) 处:
0 <= j <= nums[i] 且
i + j < n
返回到达 n - 1 的最小跳跃次数。测试用例保证可以到达 n - 1。
这道题的核心是求到达数组末尾的最小跳跃次数,最优解法是贪心算法,时间复杂度 O (n),空间复杂度 O (1),效率最高。
解题思路
1. 维护三个关键变量:
- jumps:记录跳跃次数(最终结果)
- currentEnd:当前跳跃能到达的最远边界
- farthest:遍历过程中能到达的最远位置
2. 遍历数组(最后一个元素无需遍历):
- 不断更新能到达的最远位置 farthest
- 当遍历到当前边界 currentEnd 时,说明必须跳一次,更新边界并增加跳跃次数
代码解释
- 边界处理:数组长度≤1 时,直接返回 0(无需跳跃)
- 遍历逻辑:
- i + nums[i]:从当前位置i能跳到的最远索引
- 每次遍历都更新全局最远位置farthest
- 当i == currentEnd,说明走到了上一次跳跃的极限,必须进行一次新的跳跃,同时把边界更新为当前能到达的最远位置
- 题目保证:测试用例一定能到达终点,无需判断无法到达的情况
总结
- 贪心算法是本题最优解,一次遍历完成计算
- 核心:每次在边界处必须跳跃,且跳至当前最远位置
- 时间复杂度 O (n),空间复杂度 O (1),完美适配大数据量场景
思路推导:
一、先站在出题人角度审题
题目要求:最少跳跃次数
已知规则:
- 在 i 最多跳 nums[i] 步
- 可以跳 0~nums [i] 任意距离
- 求到最后一位最少几步
第一步:先排除笨办法
- 暴力 DFS:枚举所有跳法,记录最小值 → 超时,数据一大直接寄
- BFS 广度优先:按层数跳,一层 = 一跳 → 能求出答案,但要开队列,空间大
- 有没有不用额外空间、一次遍历的办法?
既然求最少步数,本能反应:每一跳尽量跳最远
这就是贪心的起源!
二、从生活场景类比,瞬间开窍
想象你跑步闯关:
- 你现在站一块跳板上,这块跳板能让你冲到最远 X 位置
- 在你冲到 X 之前,沿途所有跳板你都能路过
- 沿途路过所有跳板时,你悄悄算出:这一片区域里,最远能冲到哪
- 等你真跑到 X(当前能到的极限)
你不得不再跳一次,这一跳直接跳到刚才算出的最远处
翻译回题目
- 一块跳板区间 = [上一个边界, currentEnd]
- 沿途更新 farthest = 整片区域最远落点
- 跑到 currentEnd = 走到这片区域尽头,必须再跳一次
- 跳跃次数 + 1,新边界直接设为 farthest
生活逻辑 = 代码逻辑 1:1 对应
三、三步自我推导流程(以后同类题都这么想)
1. 确定目标
求最小次数 → 策略:每一步利益最大化
次数最少 ↔ 单次走得越远越好
2. 划分阶段
跳跃是分段前进的:
- 第 1 跳能覆盖哪些位置
- 第 2 跳能覆盖哪些位置
每一跳对应一段连续区间
3. 定义变量(自己推出来)
- 我一共跳了几次?→ jumps
- 我这一跳最远能踩到哪?→ currentEnd
- 在这段路里,我能打探到的全局最远是哪?→ farthest
4. 定触发条件
什么时候必须跳下一跳?
走到当前这一跳的尽头了,没法再往前蹭了,必须起跳
也就是:i == currentEnd
5. 补全逻辑
遍历每个点,不断更新最远;
碰到区间尽头,次数 + 1,更新新尽头。
整套思路完全是顺着题意顺推出来,不是死记硬背
四、为什么你一开始想不到?
- 惯性思维:总想着当前点直接往哪跳,盯着单点
👉 正确思路:不盯单点,盯一整片区间 - 不会拆分:不会把全程拆成「一跳一个区间」
- 不知道最少次数对应贪心最优策略
纠正思维定式
❌ 错误:我在 i 点,我选跳几步
✅ 正确:我在当前可达整片区域,找出这片区域能抵达的极限,走满区域再统一跳一次
Java代码实现如下:
public class JumpGameII {
public int jump(int[] nums) {
// 边界情况:数组长度为1,已经在终点,无需跳跃
if (nums == null || nums.length <= 1) {
return 0;
}
int jumps = 0; // 跳跃次数
int currentEnd = 0; // 当前跳跃的边界
int farthest = 0; // 能到达的最远位置
// 遍历到倒数第二个元素即可(最后一个元素是终点)
for (int i = 0; i < nums.length - 1; i++) {
// 更新当前能到达的最远位置
farthest = Math.max(farthest, i + nums[i]);
// 到达当前跳跃的边界,必须跳一次
if (i == currentEnd) {
jumps++;
currentEnd = farthest; // 更新边界为新的最远位置
}
}
return jumps;
}
// 测试代码
public static void main(String[] args) {
JumpGameII solution = new JumpGameII();
// 测试用例1
int[] nums1 = {2,3,1,1,4};
System.out.println(solution.jump(nums1)); // 输出:2
// 测试用例2
int[] nums2 = {2,3,0,1,4};
System.out.println(solution.jump(nums2)); // 输出:2
}
}
浙公网安备 33010602011771号