leetcode刷题之跳跃游戏II

题目描述

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例 :

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

说明:

假设你总是可以到达数组的最后一个位置。

解题思路

(1)动态规划

  • 首先初始化dp数组: d p = i n t [ n u m s . s i z e ( ) ] dp = int[nums.size()] dp=int[nums.size()] d p [ i ] dp[i] dp[i]表示在数组 i i i的位置到达数组末端所走的最少步数。如果位置 i i i无法到达数组末端,则令 d p [ i ] = + ∞ dp[i] =+\infty dp[i]=+.
  • 现在来计算 d p [ i ] dp[i] dp[i] n u m s [ i ] nums[i] nums[i]表示位置 i i i能走的最大步长。在最大步长覆盖的范围内,找出到达数组末端的最小步数,然后加 1 1 1,即为从位置 i i i走到末端的最少步数,也就是 d p [ i ] = 1 + m i n ( d p [ i + 1 ] , d p [ i + 2 ] , . . . , d p [ i + n u m s [ i ] ] ) dp[i] = 1+min(dp[i+1],dp[i+2],...,dp[i+nums[i]]) dp[i]=1+min(dp[i+1],dp[i+2],...,dp[i+nums[i]])。最开始是从数组末端开始,初始 d p [ n u m s . s i z e − 1 ] = 0 dp[nums.size-1] = 0 dp[nums.size1]=0表示在数组末端不需要走或者说走 0 0 0步,就到数组末端了。然后依次倒推过去最后就求出 d p [ 0 ] dp[0] dp[0]的值了,而 d p [ 0 ] dp[0] dp[0]表示在数组位置0处到达数组末端所走的最少步数。下面代码在求 m i n ( . . . ) min(...) min(...)的时候,用了快排思想,相当于二分查找,找最小的时间复杂度为 O ( log ⁡ 2 ( n ) ) O(\log_2(n)) O(log2(n))。该算法总的时间复杂度为 O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))

(2)直接贪心求解法

由于该题动态规划超出时间限制,我就看了下评论,很多也是DP、BFS都超时,但是有一位大神的思路不错,而且时间复杂度就是 O ( n ) O(n) O(n)。在此膜拜一下,并将思想理解并叙述一下:
现在发挥你的想象,把数组 n u m s nums nums看成一条直线,人最开始在数组下标i为0,相当于直线原点位置。
在这里插入图片描述如果还不清楚就看代码吧!或者看官方发布的思路,就是贪心的用最少的步数到最远的距离。还是太菜了,我没想出来,总是觉得万物皆可DP。

解题代码

(1)动态规划代码

class Solution {
public:
    int searchmin(vector<int>dp,int start,int end){
        //随机快排
        unsigned seed = time(0);
        srand(seed);
        int key = rand() % (end- start + 1) + start;
        swap(dp[key],dp[end]);
        //使用二分查找最小值在范围start到end之前,包含两个端点
        int i = start-1;
        for(int j = start; j < end; ++j){
            if(dp[j] < dp[end]){
                ++i;
                swap(dp[i],dp[j]);
            }
        }
        swap(dp[++i],dp[end]);
        if(1 < i - start + 1){
            return searchmin(dp,start,i-1);
        }
        return dp[i] == INT_MAX ? INT_MAX : dp[i]+1;
        
    }
    int jump(vector<int>& nums) {
        vector<int>dp(nums.size(),0);  //保存每一个位置跳到最远的最少步数
        for (int i = nums.size() - 2; i>=0; --i){
            if(nums[i] == 0){
                dp[i] = INT_MAX;//说明这个位置不能跳,永远也到不了终点,那就把它置为无穷大
                continue;
            }
            if(nums[i]+i >= dp.size()-1){
                dp[i] = 1;//步长能到终点就直接返回1了,不需要找最小
                continue;
            }
            dp[i] = searchmin(dp,i+1,i+nums[i]);
        }//for
        return dp[0];
    }
};

(2)直接贪心法代码

class Solution {
public:
    int jump(vector<int>& nums) {
        int step = 0;
        if(nums.size() == 1){
            return step;//表示一出生就在罗马,不用走了
        }
        int max_coverage = nums[0];//当前初始位置所覆盖的范围
        int next_coverage = 0;//表示当前覆盖范围的下一个覆盖范围
        step +=1;//表示走了一步
        if(max_coverage >=(nums.size()-1)){
            return step;
        }
        for(int i = 1; i<nums.size(); ++i){
            if(i > max_coverage){
                ++step;//如果走出的当前覆盖范围进入下一个覆盖范围
                max_coverage = next_coverage;
            }
            if(next_coverage < i+nums[i]){
                //表示当前i所覆盖的范围比原来的更大,则更新覆盖范围
                next_coverage = i+nums[i];
            }
            if(next_coverage >= nums.size()-1){
                break;
                //如果下一覆盖范围能覆盖到数组末端则表示只需一步就到了
            }
        }//for
        return step+1;
    }
};

提交结果

(1)动态规划提交结果

在这里插入图片描述无论怎么优化都是卡在这里,这说明这题动态规划不行。或者是我没有优化好。

(2)直接贪心法提交结果

在这里插入图片描述

总结

动态规划法时间复杂度比较高,这题已经明确从原点可以走到终点,只要求出走的最小步数。所以可以直接通过算出下一步能到的最远距离,直到覆盖终点就结束。计算步长就是覆盖面以内就不动,从一个覆盖面到另一个覆盖面就加一。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值