五大算法之贪心算法

        贪心算法是指对子问题取最值,从而求得局部最优解,并以此求得全局最优解。贪心算法可以认为是动态规划的一个特例,同样是需要将问题分解为子问题,避免了子问题的重复计算,只不过在子问题的处理上贪心算法更加简单直接。贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

        举个简单的例子,有10、5、3面额的钞票各若干张,现在规定只能拿10张,问最多能拿多少钱。显然每次都拿剩下钞票中最大的面额,最后能取得最优解。但是如果换成凑够某金额最少的钞票数,那么贪心算法没办法得到最优解,因为每次取最大面额,最后可能出现面额不合适,从而使用大量小面额凑零,反而增加了钞票数,面对这种问题,贪心策略就不具备无后效性。

        以上介绍了贪心算法的概念及局限性,对于符合贪心策略的问题,基本思路如下:

  • 建立数学模型来描述问题;
  • 分成若干个子问题;
  • 求解子问题的局部最优解;
  • 将局部最优解合成最终解。

        能够满足贪心算法使用条件的问题实际上很少,所以在使用贪心算法前一定要验证问题是否满足贪心算法的条件。那么对于相关的例题,大家喜欢上来就一道0-1背包,然后一顿分析贪心算法在此问题上的不成立,可以说很不给贪心算法的面子,下面就以跳跃游戏问题为例,让贪心算法成立一次。

 

跳跃游戏(LeetCode-55)

          我们把此类为题转变为最值问题,即跳跃最远距离覆盖最后一个位置认为能够达到最后一个位置。那么求最远跳跃距离可以分解为求每一步跳跃的最远距离,贪心算法具体思路如下:

bool canJump(vector<int>& nums) {
    int n = nums.size();
    int farthest = 0;
    for (int i = 0; i < n - 1; i++) {
        // 不断计算能跳到的最远距离
        farthest = max(farthest, i + nums[i]);
        // 可能碰到了 0,卡住跳不动了
        if (farthest <= i) return false;
    }
    return farthest >= n - 1;
}

        每一步都计算一下从当前位置最远能够跳到哪里,然后和一个全局最优的最远位置farthest做对比,通过每一步的最优解,更新全局最优解。相对于动态规划,贪心算法的优势在于,时间复杂度为O(N),空间复杂度为O(1),但是使用时必须满足贪心算法的条件。

 

跳跃游戏 ΙΙ(LeetCode-45)

        下面再将问题升级为求最少跳跃次数,同样可使用贪心算法解决。

          解题思路为:每一步必须跳,那么就求出每一步能够跳的最远距离,即将原问题分解为每一步跳跃使得后续的可跳距离最远。如下图所示,位置0最大可跳2个位置,具体跳到位置1还是位置2呢?由于位置1可跳范围比位置2更远,所以在位置0选择跳到位置1,之后每一步都按照同样的策略取下一跳位置。

  代码如下:

int jump(vector<int>& nums) {
    if(nums.size()<=1) return 0;
    int step=0, start=0,reach=0;
    while( reach <nums.size()-1 ){
        int farest=0;
        // 遍历可跳范围,取可跳最远的位置作为下一跳
        for(int i=start; i<=reach; i++)
            farest=max(farest,i+nums[i]);
        start=reach+1;
        reach=farest;
        step++;
    }
    return step;
}

 

posted @ 2020-06-06 01:15  BobPong  阅读(1663)  评论(0编辑  收藏  举报