当前位置:   article > 正文

LeetCode算法题6:贪心 - 跳跃游戏_贪心跳跃问题

贪心跳跃问题


前言

      贪心算法系列:(之前还有一篇文章描述的也是贪心算法:https://blog.csdn.net/Little_ant_/article/details/116098188

贪心算法:

      以下摘自百度百科:

      贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
      贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。

      利用贪心法求解的问题应具备如下2个特征 。

      1、贪心选择性质
      一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解 。

      2、最优子结构性质
      当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键所在。在实际应用中,至于什么问题具有什么样的贪心选择性质是不确定的,需要具体问题具体分析(这里指的是采用何种贪心策略) 。

      贪心算法不从整体最优上加以考虑,所做出的仅是在某种意义上的局部最优选择。使用贪心策略要注意局部最优与全局最优的关系,选择当前的局部最优并不一定能推导出问题的全局最优。贪心策略解题需要解决以下两个问题:
      1、该问题是否适合使用贪心策略求解,也就是该问题是否具有贪心选择性质;
      2、制定贪心策略,以达到问题的最优解或较优解。

一、跳跃游戏

      题目链接:https://leetcode-cn.com/problems/jump-game/

      题目描述:给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

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

判断你是否能够到达最后一个下标。

思路

      判断是否能够到达最后一个下标,那如果每次我们都在当前可选的位置区间内选择一个位置,满足从这个位置出发能够走的距离更远(覆盖其他位置的可达处),那么这个位置就是当前的最优选择。而我们尽最大努力一步步跳跃,如果最后也不能到达最后一个下标,那无论如何也到不了了,这个便是最优子结构性质。

      如何选择当前最优的位置?以序列 3、5、4、2、9、6、1…作为示例简单说明。初始时所在位置为 0,当前值为 3,所以下一步可跳跃的区间为[1,3];
      然后分别在区间下标 1、 2、 3 处判断,如果下一步跳到 1 处,下下一步的区间[2,6],如果下一步跳到 2 处,下下一步的区间为[3,6],如果下一步跳到 3 处,下下一步的区间为[4,5];
      此时,因为 3 处能够到达的最远距离只能到 5,它所能到达的位置,1 和 2 都可以到达,那我们在 1 或 2 中随便选一个即可,不会影响最终结果。
      假设选 1:那么接下来就会选择下标 4 处… 。选 2 的话接下来也是选择下标 4 …

      参考算法如下:

	public boolean canJump(int[] nums) {
        int value,nextI=0; //nextI 表示在当前区间内选择下一跳的位置

        for(int i=0;i<nums.length;){
        	if(i==nums.length-1)//边界条件1 已经到达最后一个位置时返回true
        		return true;
            value=-1; // 表示 nextI 能够到达的最远位置

            int tmp=nums[i];//tmp 表示当前可选区间长度

            if(tmp==0&&i!=nums.length-1)//边界条件2 如果长度为 0,并且当前 i 不是最后一个位置时返回false
                return false;

            for(int j=i+1;tmp!=0;j++,tmp--){//求区间内的 top 1
                if(j>=nums.length-1)//边界条件3 当前区间内有下标超出了最后一个位置时返回true
                    return true;
                if((nums[j]+j)>value){ //如果有多个相同的最远距离时取第一个, 改为 >= 时取最后一个
                    value=nums[j]+j;
                    nextI=j;
                } 
            }
            i=nextI;
        }
        return false; //这个语句不会被执行。
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

      上面那个算法过于复杂了,本题其实只需要更新保存最远跳跃距离即可。参考代码如下:

	//贪心:维护最远可到达距离,如果它>=最后一个位置,返回true;否则返回false
    public boolean canJump(int[] nums) {
        int value=0;// 维护最远能够到达的位置,初始值只能为0.

        for(int i=0;i<nums.length;i++){
            if(i<=value){
                value=Math.max(value,i+nums[i]);
                if(value>=nums.length-1)
                    return true;
            }
            else
                return false;
        }
        return false;//这个语句不会被执行
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

二、跳跃游戏II

      题目链接:https://leetcode-cn.com/problems/jump-game-ii/submissions/

      题目描述:给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

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

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

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

思路

      上一道题的解法 1 的功能不就是在判断最小的跳跃次数(最优的跳跃位置)能否到达终点嘛,所以将它改一下就好了。边界条件 2 需要去掉,因为本题已经告诉我们一定可以到达最后一个位置。

      参考算法如下:

	public int jump(int[] nums) {
        int value,nextI=0,ans=0; //nextI 表示在当前区间内选择下一跳的位置

        for(int i=0;i<nums.length;){
        	if(i==nums.length-1)//边界条件1 已经到达最后一个位置时返回true
        		return ans;
            value=-1; // 表示 nextI 能够到达的最远位置

            int tmp=nums[i];//tmp 表示当前可选区间长度
            for(int j=i+1;tmp!=0;j++,tmp--){//求区间内的 top 1
                if(j>=nums.length-1)//边界条件3 当前区间内有下标超出了最后一个位置时返回true
                    return ans+1;
                if((nums[j]+j)>value){ //如果有多个相同的最远距离时取第一个(改为 >= 时取最后一个)
                    value=nums[j]+j;
                    nextI=j;
                } 
            }
            i=nextI;
            ans++;
        }
        return 1; //这个语句不会被执行。
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

      有一个超时的回溯算法了解一下:

   int mn=Integer.MAX_VALUE;
    public int jump(int[] nums) {
        solve(nums,0,0);
        return mn;
    }
    public void solve(int[] nums,int start,int count){
        if(start>=nums.length-1){
            mn=Math.min(mn,count);
            return;
        }
        for(int i=start+1,c=nums[start];c>0;i++,c--)
            solve(nums,i,count+1);
    } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

总结

      深度优先遍历是回溯算法使用的策略。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/518697
推荐阅读
相关标签
  

闽ICP备14008679号