热题100 - 45. 跳跃游戏 II

56 阅读4分钟

题目描述:

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[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
  • 题目保证可以到达 nums[n-1]

思路:

考虑贪心的思路。每次在能跳到的范围内,找到能跳到最远的位置,作为下一跳的候选。例如,当前步骤能到达的最远位置是curEnd,而在这段区间内的每个位置i,可以到达的最远位置是i + nums[i]。我们需要在这些可能的位置中,找到最大的那个,作为下一步的最远点,并增加跳跃次数。这可能是一种方法。

举个例子,比如初始的时候,我们在位置0,可以跳1或2步。这时候,第一个区间是0到0,因为初始位置是0。然后在这个区间内,计算每个位置能到达的最远距离。比如位置0能到达的最远是0+2=2。这时候,下一步的最远位置是2,所以跳跃次数加1。接下来,下一个区间是1到2的位置,也就是第1和第2个元素。在这两个位置里,计算能到达的最远位置。比如位置1可以跳到4(1+3=4),而位置2只能跳到3(2+1=3)。所以下一跳的最远位置是4,此时跳跃次数加1。这时候,已经到达或超过最后一个位置(索引4),所以结束,总共有2次跳跃。

这个思路可能有效。我们需要维护几个变量:当前能到达的最远位置max,当前能跳到的最远位置end,以及跳跃的次数step。

实现:

class Solution {
    public int jump(int[] nums) {
        int max = 0;
        int end = 0;
        int step = 0;
        int n = nums.length;
        if (n==1) return 0;
        for (int i = 0; i < n; i++) {
            max = Math.max(max, nums[i]+i);
            if (i == end) {
                end = max;
                step++;
                if (max >= n-1) break;
            }
        }
        return step;
    }
}

这种算法被称为贪心算法,是因为它在每一步都做出当前最优的选择,通过局部最优解的积累来达到全局最优。以下是具体解释:


1. 贪心算法的核心特征

贪心算法的核心是:在每一步选择中,只考虑当前状态下能获得的最好结果,而不考虑后续步骤的潜在影响。虽然这听起来可能短视,但某些问题(如本题)的数学性质可以保证这种策略能得到全局最优解。


2. 跳跃游戏 II 的贪心策略

在本题中,我们的目标是 以最少跳跃次数到达终点。具体实现中的贪心策略是:

  1. 当前能跳多远?
    遍历数组时,维护一个 当前能跳到的最远位置cur_farthest)。
  2. 何时必须跳跃?
    当遍历到 当前跳跃的边界cur_end)时,必须做一次跳跃,并更新边界为 cur_farthest
  3. 为什么这是最优选择?
    每次跳跃时,选择 能覆盖最远未来可能性的位置(即尽可能跳得远),这样可以减少后续需要的跳跃次数。

3. 为什么贪心是有效的?

数学性质保证正确性
由于问题具有 贪心选择性质(每次选最远跳跃不会破坏全局最优解),且 最优子结构(全局最优解包含局部最优解),因此贪心策略有效。 • 直观理解
跳跃得越远,后续需要跳跃的次数越少。例如:

示例:nums = [2, 3, 1, 1, 4]
第一次跳跃选择从位置 0 跳到位置 1(覆盖更远),而不是跳到位置 2。
虽然位置 2 更近,但位置 1 的下一跳能覆盖到终点。

4. 对比其他可能的策略

如果不用贪心,可能需要暴力枚举所有可能的跳跃路径(时间复杂度爆炸)。而贪心算法通过 每一步的局部最优选择,避免了穷举,将时间复杂度降为 O(n)


5. 贪心的关键点

cur_farthest:记录所有已遍历位置中能跳到的最远位置。 • cur_end:当前跳跃的边界,到达此处时必须跳跃。 • jumps:跳跃次数,每次到达 cur_end 时递增。


6. 总结

贪心算法的本质是:在问题的每一步,做出当前看来最好的选择,并相信这些选择最终能导向全局最优解。在跳跃游戏 II 中,通过选择能覆盖最远范围的跳跃,确保了跳跃次数的最小化。