力扣解题-45. 跳跃游戏 II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置在下标 0。
每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在索引 i 处,你可以跳转到任意 (i + j) 处:
0 <= j <= nums[i] 且 i + j < n
返回到达 n - 1 的最小跳跃次数。测试用例保证可以到达 n - 1。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 1000
题目保证可以到达 n - 1
示例解答
解题思路
核心方法:贪心策略(边界优化版),通过维护“当前跳跃的最远边界”和“当前范围内下一跳的最远位置”,在遍历过程中精准控制跳跃时机,确保每一次跳跃都是“最优选择”,从而得到最小跳跃次数。时间复杂度O(n)、空间复杂度O(1),是本题的最优解法。
核心原理铺垫
跳跃游戏 II 的核心逻辑是:要得到最小跳跃次数,每一次跳跃都应选择能跳得最远的位置。通过两个关键变量实现这一策略:
currentEnd:当前这一跳能到达的最远边界,代表“必须触发下一次跳跃”的临界点;farthest:在当前跳跃覆盖范围内,所有位置能跳到的最远位置,代表“下一跳的最优目标”;jumps:记录跳跃次数,初始为0。
具体步骤
- 边界快速处理:若数组长度
n <= 1,直接返回0(已在终点,无需跳跃); - 初始化核心变量:
jumps = 0:跳跃次数初始为0;currentEnd = 0:初始跳跃边界为起点(下标0);farthest = 0:初始下一跳最远位置为0;
- 遍历数组(关键优化:仅遍历到n-2):
- 遍历下标
i从0到n-2(无需遍历到最后一个下标,因为到达n-1即完成目标,无需再跳); - 更新
farthest为Math.max(farthest, i + nums[i])(记录当前范围内能跳到的最远位置); - 当
i == currentEnd(到达当前跳跃的边界,必须跳一次):jumps++(跳跃次数+1);currentEnd = farthest(将下一跳的边界更新为当前能到达的最远位置);- 提前终止判断:若
currentEnd >= n-1(已能到达终点,无需继续遍历),直接break;
- 遍历下标
- 返回结果:遍历完成后返回
jumps,即为最小跳跃次数。
核心优化逻辑说明
- 时间复杂度最优:仅一次遍历数组(O(n)),且遍历范围缩小到
n-2,每个元素仅被访问一次,耗时1ms击败99.63%用户; - 空间复杂度极致:仅使用四个基础变量,空间复杂度O(1),无额外内存开销;
- 关键优化点解析:
- 遍历到
n-2:最后一个下标n-1是目标,遍历到该位置无意义,减少一次无效循环; - 提前终止:当
currentEnd >= n-1时直接break,避免后续无效遍历,进一步提升性能; - 跳跃时机精准:仅在到达当前边界时触发跳跃,确保每一次跳跃都是必要的,且选择最优目标;
- 遍历到
- 内存表现优秀:内存消耗46.3MB击败92.74%用户,是因为无任何冗余变量,内存占用达到理论最优。
示例推演(以nums = [2,3,1,1,4]为例)
- 初始状态:jumps=0,currentEnd=0,farthest=0;
- i=0:farthest = max(0, 0+2)=2 → i==currentEnd(0==0)→ jumps=1,currentEnd=2 → 未到终点,继续;
- i=1:farthest = max(2, 1+3)=4 → i≠currentEnd(1≠2),继续;
- i=2:farthest = max(4, 2+1)=4 → i==currentEnd(2==2)→ jumps=2,currentEnd=4 → currentEnd≥4(n-1=4),break;
- 返回jumps=2,与示例结果一致。
public int jump(int[] nums) {
int n = nums.length;
if (n <= 1) return 0; // 已经在终点
int jumps = 0; // 跳跃次数
int currentEnd = 0; // 当前这一跳能到达的最远边界
int farthest = 0; // 在当前覆盖范围内,下一跳能到达的最远位置
// 遍历到 n-2 即可!因为到达 n-1 就结束了,不需要再跳
for (int i = 0; i < n - 1; i++) {
// 更新下一跳的最远可达位置
farthest = Math.max(farthest, i + nums[i]);
// 如果到达当前跳跃的边界,必须跳一次
if (i == currentEnd) {
jumps++;
currentEnd = farthest;
// 提前终止:如果已经能到达或超过终点
if (currentEnd >= n - 1) {
break;
}
}
}
return jumps;
}
总结
- 该解法的核心是贪心策略的精准落地:通过“当前边界”和“下一跳最远位置”两个变量,确保每一次跳跃都选择最优路径,从而得到最小次数;
- 关键优化点:
- 遍历范围限定到
n-2,避免无效循环; - 到达边界才触发跳跃,确保跳跃次数最少;
- 提前终止条件,减少不必要的遍历;
- 遍历范围限定到
- 性能优势:时间复杂度O(n)适配题目10⁴级别的数据规模,是该问题的理论最优解,且空间复杂度O(1),在面试和工程中都是首选写法。