跳跃游戏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. 边界处理:数组长度≤1 时,直接返回 0(无需跳跃)
  2. 遍历逻辑
  • i + nums[i]:从当前位置i能跳到的最远索引
  • 每次遍历都更新全局最远位置farthest
  • 当i == currentEnd,说明走到了上一次跳跃的极限,必须进行一次新的跳跃,同时把边界更新为当前能到达的最远位置
  1. 题目保证:测试用例一定能到达终点,无需判断无法到达的情况

总结

  1. 贪心算法是本题最优解,一次遍历完成计算
  2. 核心:每次在边界处必须跳跃,且跳至当前最远位置
  3. 时间复杂度 O (n),空间复杂度 O (1),完美适配大数据量场景

思路推导:

一、先站在出题人角度审题

题目要求:最少跳跃次数

已知规则:

  1. 在 i 最多跳 nums[i] 步
  2. 可以跳 0~nums [i] 任意距离
  3. 求到最后一位最少几步

第一步:先排除笨办法

  1. 暴力 DFS:枚举所有跳法,记录最小值 → 超时,数据一大直接寄
  2. BFS 广度优先:按层数跳,一层 = 一跳 → 能求出答案,但要开队列,空间大
  3. 有没有不用额外空间、一次遍历的办法?
    既然求最少步数,本能反应:每一跳尽量跳最远
    这就是贪心的起源!

二、从生活场景类比,瞬间开窍

想象你跑步闯关

  • 你现在站一块跳板上,这块跳板能让你冲到最远 X 位置
  • 在你冲到 X 之前,沿途所有跳板你都能路过
  • 沿途路过所有跳板时,你悄悄算出:这一片区域里,最远能冲到哪
  • 等你真跑到 X(当前能到的极限)
    不得不再跳一次,这一跳直接跳到刚才算出的最远处

翻译回题目

  • 一块跳板区间 = [上一个边界, currentEnd]
  • 沿途更新 farthest = 整片区域最远落点
  • 跑到 currentEnd = 走到这片区域尽头,必须再跳一次
  • 跳跃次数 + 1,新边界直接设为 farthest

生活逻辑 = 代码逻辑 1:1 对应

三、三步自我推导流程(以后同类题都这么想)

1. 确定目标
最小次数 → 策略:每一步利益最大化
次数最少 ↔ 单次走得越远越好

2. 划分阶段
跳跃是分段前进的:

  • 第 1 跳能覆盖哪些位置
  • 第 2 跳能覆盖哪些位置
    每一跳对应一段连续区间

3. 定义变量(自己推出来)

  • 我一共跳了几次?→ jumps
  • 我这一跳最远能踩到哪?→ currentEnd
  • 在这段路里,我能打探到的全局最远是哪?→ farthest

4. 定触发条件
什么时候必须跳下一跳?
走到当前这一跳的尽头了,没法再往前蹭了,必须起跳
也就是:i == currentEnd

5. 补全逻辑
遍历每个点,不断更新最远;
碰到区间尽头,次数 + 1,更新新尽头。
整套思路完全是顺着题意顺推出来,不是死记硬背

四、为什么你一开始想不到?

  1. 惯性思维:总想着当前点直接往哪跳,盯着单点
    👉 正确思路:不盯单点,盯一整片区间
  2. 不会拆分:不会把全程拆成「一跳一个区间」
  3. 不知道最少次数对应贪心最优策略

纠正思维定式
❌ 错误:我在 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
    }
}
posted @ 2026-05-15 10:06  ZealousMclaren  阅读(16)  评论(0)    收藏  举报