leetcode 55 跳跃游戏

题目链接 跳跃游戏

这个题让我明白了,算法题直接暴力是不行的,不要有侥幸心里。但是,当你只会暴力的时候,还是先运行出来再进行修改吧。不然好的方法是不太容易直接想出来的。

  • 方法一:暴力(这个暴力也不是很容易的,递归加回溯)

思路:

1 明确当前位置能否到达最后一个位置,最好的判定时  当前坐标i,对应的值nums[i] + i是改点能走到的最大坐标处,所以当nums[i] + i >= nums.size()时,可以直接返回了。另一个想法就是nums[i] - (nums.size() -i) >=0,其实都是一样的,这里我用的是后面的判定方法,也是递归的出口。

2 明确当前点i,需要就行多少次递归,即nums[i]次,因为他可以从接下来i+1到i+nums[i]内任何一个位置出发,去寻找能否到达最终位置,需要一个一个尝试,这也是这种方法慢的地方。

//时间超时

class Solution {
public:
    void solve(vector<int> nums, bool &sig, int l, int n){
        
        if(l==n || nums[l] - (n - l) >=0){  //出口,sig是一个标志的返回值
            sig= 1;
            return;
        }
        int i =1;
     //   cout<<nums[l]<<" ";
        for(i =1; i <= nums[l]; i++){
      //      if(nums[l + i] == 0 ){continue;}
            solve(nums,sig, i+ l, n);  //依次递归从l 到 l+nums[l]中间的任何一个位置。
        }
      //这里有一个改进的地方,就是从l处开始,先搜索l + nums[l],但是复杂度是一样的。还是过不了 
    }
    bool canJump(vector<int>& nums) {
       
        int  n = nums.size();
        bool sig=0;
        solve(nums,sig, 0, n-1);
        return sig;
    }
};
  • 方法二:递归中记忆法,说白了就是剪枝

思考:[9,5,3,2,1,0,2,0]的执行过程,用上面的方法是5->4>3>2>1>0>0>1>.....

后面就不看了,注意到0时,走不通,回到1,1对应的只有一个递归,走完了回到2,此时2对应的第一个递归(i = 1)走完了,

下一个时(i = 2),对应0,但是我们知道0已经走过,但是从0开始是无法走通的,但是程序仍然会再次进行递归,所以造成不必要的浪费。

思考:定义一个数组,代表改点能否到达最后的位置,如果能则为1,就直接返回,说明改点走过了,不用走了,走也不通。要不然也不会回溯啊。

class Solution {
public:
    int src[1000] = {0};
    void solve(vector<int> nums,  bool &sig, int l, int n){
        
        if(src[l] == 1){ return ;}
        src[l] = 1;
        if(l==n || nums[l] - (n - l) >=0){
            sig= 1;
            return;
        }
        //icout<<nums[l]<<" ";
        for(int i =nums[l]; i >=1; i--){
            if(nums[l + i] == 0 ){continue;}
            solve(nums,sig, i+ l, n);
        }

    }
    bool canJump(vector<int>& nums) {
        
        
        int  n = nums.size();
    //    int src = new src[n];
        
        bool sig=0;
        solve(nums,sig, 0, n-1);
        return sig;
    }
};

//这个提示爆内存溢出,其实就是栈溢出,很正常,数据多了用递归就是这样。
  • 方法三:动态规划,从后向前

思路:倒着判断点i能否到达最后点,如果能设置为1,再判断前面的点j,能否到i。以此推到初始点。

class Solution {
public:
   
    bool canJump(vector<int>& nums) {
        
        
        int  n = nums.size();
        int src[n] = {0};
        
        src[n-1] = 1;
        
        for(int i = n -2; i >= 0; i--){
            int count = min(i + nums[i], n - 1); //i一定能到达j,如果j能到达最后,则i也能
            for(int j = i + 1; j <= count; j++){
                if(src[j] == 1){
                    src[i] =1;
                    break;
                }
            }
        }
        return src[0] == 1;
    }
};
//感谢老天,终于过了,但是时间复杂度为O(n2),太高了,继续
  • 方法四:贪心

思考:方法三中第二层循环的意思何在?说白了就是记录一个点,改点能否到达下一个可行点。但是记录的有点多,他记录了每一个点,我们只需要记录最左边的点能否到达终点,然后在最左边点1再找一个相对最左边的点2能否到达1,即可。

class Solution {
public:
   
    bool canJump(vector<int>& nums) {
        
        int n = nums.size();
        int leftPos = n - 1;
        for(int i = n - 1; i >=0; i--){
            if(nums[i] + i >= leftPos){
                leftPos = i;
            }
        }
        return leftPos == 0;
    } 
};

//O(n)
/*
相对于第三种
其实count 就是nums[i] + i, j就相当于leftPos
*/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值