Bootstrap

(试讲)LeetCode-45-跳跃游戏 II

示例:

不同于跳跃游戏1只是判断能否到达终点,跳跃游戏II在其基础之上给出了求最小跳跃次数的要求。

那么或许我们会有这样的猜测:我们只要尽可能地往远处跳就可以实现题目的要求。

诚然,这是不错的——

如果我们完全利用了可以远跳的机会,那么显然达到终点所消耗的步数就是最少的。

但是,如果只是单纯地按照这个思路,那么有可能会陷入一个误区

这里先给出按照上述逻辑所实现的错误代码:

class Solution {
    public int Jump(int[] nums){
        if(nums.length <= 1){
            return 0;
        }
        int res = 0;
        int covered = 0;
        for (int i = 0; i < covered; i++) {
            if(covered >= nums.length - 1){
                return res;
            }
            //当发现某一点覆盖范围比当前大就替换当前覆盖范围,并将步数+1
            if(covered < i + nums[i]){
                covered = i + nums[i];
                res++;
            }
        }
        return res;
    }
}

(这里同样利用了跳跃游戏1所用到的覆盖范围的概念)

显然上述代码实现了这样一个功能——当我们在当前覆盖内遇到了任意的一个点,并且这个点使得当前覆盖范围得到增加,那么就更新覆盖范围并且使得跳跃步数加一。

好像没什么问题,我们确实是在往远处跳.....

但这是错误的,上述思维还是仅仅利用了跳跃问题1的思维模式。但是本题与跳跃问题1是有本质差别的。

这里给出上述逻辑错误的原因:

我们没有完全利用到每次跳跃的机会,我们做了步数的浪费!

这样的局部最优是推不出全局最优的!

例如下图:

我们完全可以直接跳到第三个"3"的位置,然后再继续往后跳跃,而不是在途中又经过了第二个"3",这显然是对步数的浪费,也不符合我们确定"覆盖范围"这么一概念的意义。

那么尝试阐明了这么一个可能会出现的错误过后,我们或许可以想到的是——

如果我们遍历完全了当前覆盖范围,并且在当前覆盖范围中找到了接下来可以跳跃的最大覆盖范围的话....是不是就足以推出全局最优了呢?

显然是的,这里先给出具体代码:

class Solution {
    public int jump(int[] nums) {
        int curCover = 0, nextCover = 0;
        int res = 0;
        for (int i = 0; i <= curCover; i++) {
            nextCover = Math.max(nextCover, i + nums[i]);
            //如果nextCover大于了数组长度,那么就不需要循环了
            if(curCover >= nums.length - 1)
                break;
            if(i == curCover){
                res++;
                curCover = nextCover;
            }
        }
        return res;
    }
}

可以看到的是,我们利用了curCover与nextCover

前者表示当前我们所遍历的数组范围,后者表示我们在这个数组范围内所寻得的最大覆盖范围。

当然也是我们下一次所遍历的覆盖范围。

例如,我们假设在xCover中寻得某点x,那么或许在xCover内有许多点都能增大我们的覆盖范围,但是我们的x点,其所能增加的覆盖范围是最多的。所以我们便取x点并记录由其所更新的覆盖范围。也就是——

nextCover = Math.max(nextCover, i + nums[i]);

既然我们已经在当前覆盖范围内取得了下一次遍历最大的覆盖范围,那么何时对当前覆盖范围作更新呢?

显然,我们在遍历到当前覆盖范围末尾时作更新

也就是——

i == curCover

既然对当前覆盖范围做了更新,那么就意味着我们已经利用了一次跳跃机会。

可以这样理解,我们的根本目的就是遍历当前覆盖范围,并且找到能跳跃最远的位置,然后进行跳跃,这也是前文所提到的

但跳跃的过程并没有具体明显地展示,因为其已经包含在了我们的逻辑当中了。

那么照这样下去便能得到我们的全局最优,也就是取得最少跳跃次数了。

这就是我们给出的跳跃游戏II的讲解,当然也有其他算法

这里借鉴了《代码随想录》卡尔老师的思路。卡尔老师的思路确实都很巧妙,其思维逻辑很值得学习,在这里也感谢卡尔老师的讲解!

同样的,给出《代码随想录》本题视频地址,以及其网站——

贪心算法,最少跳几步还得看覆盖范围 | LeetCode: 45.跳跃游戏II_哔哩哔哩_bilibili

代码随想录 (programmercarl.com)

;