力扣跳跃游戏的一些个人想法

55.跳跃游戏

class Solution {
public:
    bool canJump1(vector<int>& nums) {
        if(nums.size()<=1) return true;
        int len=nums.size();
        int *flag=new int[len];
        //flag={0};//使用变量定义长度时,定义和初始化需要分开
        for(int i=0;i<len;i++) flag[i]=0;
        //判断每一个元素是否可以抵达
        queue<int> judge;
        int it=0;
        judge.push(it);
        while(!judge.empty()){
            it=judge.front();
            printf("%d\n",it);
            judge.pop();
            //判断队首元素可以到达的位置并入栈
            for(int len=nums[it],i=1;i<=len;i++){
                if(it+i==nums.size()-1) return true;//最后一个可达
                if(flag[it+i]==0){
                    judge.push(it+i);//这是由it出发可以访问到的所有位置
                    flag[it+i]=1;
                }
                //flag[it+i]==1说明之前已经执行过一次从这个位置出发的所有判断了
            }
        }
        return false;
    }

    //贪心-不断维护一个可以到达的最大下标
    bool canJump(vector<int>&  nums){
        int maxIndex=0;
        for(int it=0;it<nums.size();it++){
            if(maxIndex>=nums.size()-1) return true;
            //if(it>=maxIndex) return false;//已经不可达了
            if(it<=maxIndex) maxIndex=max(it+nums[it],maxIndex);
        }
        if(maxIndex>=nums.size()-1) return true;
        else return false;
    }
};

从Nums[0]开始入队,只要队不空的情况下出队一个元素,元素的值为其在nums中的下标,在队中的下标表示这个位置是可以到达的,那么考虑这个位置可以抵达的所有位置下标都入队,如果已经入过了(查flag)就不再重复否则超时,如果队空了,flag[nums.size()-1]仍然为0,那说明不可达,一旦flag[nums.size()-1]为1,return true

45.跳跃游戏2

关键是识别【同一跳】,没思路看了一下题解,结合这位哥的解释,我给出的【同一跳】可以这么理解,如果从i开始跳,从i+1的位置到i+nums[i]的位置都是同一跳,不跳就不存在这种考量。

先解释一下官方给出的贪心题解,参考了以下这位的注释。

每次找到可到达的最远位置,顺着遍历一次。如果当前位置可达(i<最远可达的距离),那么当前位置就要考虑是否跳,如果跳的话,以当前位置作为起点,最远可以跳到i+nums[i],如果最远可达的距离>i+nums[i],那在这里跳就是多跳,反之,在这里跳,同时,因为跳出了最远可达距离,所以【同一跳】的范围需要更新,【同一跳】是从决定从i开始跳的下一条i+1的位置一直到新的最远可达距离。

我在搞清楚【同一跳】的定义后,想到了使用树形结构来描述这种跳跃,把可达的下标作为存在树中的值/元素,那么处于同一层的下标都是使用相同跳数可以到的位置,从他们出发再深一层,就是再跳一步,如果在生成新一层元素的过程中到达/超出nums的最后位置,立刻终止,此时的层数就是跳数,和55题相似,会有一些位置不断被可达,但是不应该不断被入队,如果在这次可达之前已经入队过了,那说明上一次可达使用了更少的步数,这一次就不是一个合适的跳跃方式。

延续55题的想法,跳跃的结构类似于层次访问一个树形结构,使用两个队列进行层次遍历,第一层是0,将0这个下标入队列1,当两个队不是都空的时候,说明可以继续跳跃,选取空的队列,弹出头元素(可访问到的下标),读取nums[i],对从i可达的所有下标入另一个队列,可能会出现已经考虑过的情况,所以参考55题使用flag来记录是否已经考虑过当前下标作为出发点的情况,如果考虑过直接跳过,如果可达的下标超过了最远下标,直接返回当前step。每次进行队列切换的时候(一个空了,另一个有下标元素),说明已经到了新的层次了,也就是新的一步(思路:从i开始往前跳跃[1,nums[i]]个位置,都是同一步,在同一层)

class Solution {
public:
    int jump(vector<int>& nums) {
        if(nums.size()<=1) return 0;
        //最小跳跃次数,要求每次尽可能跳的远
        //如果当前下标可达,可以从这里跳,或者不跳
        queue<int> judge0,judge1;
        int step=0;//第0个一定走
        //维护一个判断是否考虑过当前index的flag数组,如果之前已经考虑过,再碰到该下标时就不考虑,因为后考虑的步数一定大于先考虑的
        int *flag=new int[nums.size()];
        for(int i=0;i<nums.size();i++){
            flag[i]=0;
        }
        flag[0]=1;
        judge0.push(0);//把能抵达的入队
        while(!judge0.empty()||!judge1.empty()){
            if(step%2==1){
                step++;
                 while(!judge1.empty()){
                    int index=judge1.front();
                    for(int j=1;j<=nums[index];j++){
                        if(index+j>=nums.size()-1) return step;
                        if(flag[index+j]==0){
                            //还没考虑过从index+j出发
                            judge0.push(index+j);
                            flag[index+j]=1;
                        }
                    }
                    judge1.pop();
                }

            }
            else if(step%2==0){
                step++;
                //judge0 out judge1 in
                while(!judge0.empty()){
                    int index=judge0.front();
                    for(int j=1;j<=nums[index];j++){
                        if(index+j>=nums.size()-1) return step;
                        if(flag[index+j]==0){
                            //还没考虑过从index+j出发
                            judge1.push(index+j);
                            flag[index+j]=1;
                        }
                    }
                    judge0.pop();
                }
            }
        }
        return step;
    }
};

总结

我自己写的时候,一开始没有涉及flag数组,结果超时了,所以flag还比较重要,是我觉得我在这两题中一以贯之的思想;

【同一跳】是如何定义的;

//写给自己,先考虑中间的大多数,再考虑头尾情况。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值