贪心Greedy

Posted 月牙儿June

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心Greedy相关的知识,希望对你有一定的参考价值。

先看一下这个问题:

LeetCode 55. Jump Game


Question

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

For example:
A = [2,3,1,1,4], return true.

A = [3,2,1,0,4], return false.

如果从某个索引位置能到达最后一个索引,那我们就称这个位置为   "good index", 否则,称为"bad index".

这是一个动态规划问题,通常解决动态规划问题的思路如下:

1.考虑递归回溯方法(recursive backtracking solution)

2.利用存储表来进行优化的递归方法(top-down 自上而下的动态规划)

3.不使用递归(bottom-up自底向上的动态规划)

4.final tricks to reduce thi time/ memory complexity

Approach #1 (Backtracking) [Stack Overflow]

这是一种低效方法,从first positon开始,跳到每一种可能到达的位置。重复这个过程,直到到达最后的位置。如果中途中断,则backtrack。

在此基础上的优化是:在每次判断下一个位置是否能够到达时,先判断它的最远到达位置,如果能到的最远的那个位置是可以到达 的,就不用再判断前面的了。这样可以在实际情况中减少很多次判断的可能。如果远的地方的不行,在一点点往回减1,进行下一轮的判断。

时间复杂度O(2^n)

空间复杂度O(n)

Approach #2 (Dynamic Programming Top-down) [Stack Overflow]

Top-down Dynamic Programming 自顶向下的动态规划可以被看作是回溯方法的优化。依赖于某个位置一旦被认定为good or bad Index这个结果就不会再发生任何变化。这意味着我们可以 将这个结果保存下来 而不用每次用到时都去多次计算。这种方法被称作memorization 定义一个memo数组,数组中的值为:GOOD、BAD、UNKNOWN。 例如对于数组nums = [2, 4, 2, 1, 0, 2, 0] 对应的存储数组为:
Index 0 1 2 3 4 5 6
nums2421020
memoGGBBBGG

Approach #3 (Dynamic Programming Bottom-up) [Time limit exceeded]

不使用递归的方法。对内存的使用更优化,因为不用使用每次递归时的方法栈。 自底向下的方法是从数组的最后一位开始对memo数组赋值,这样每次对左边的下一次的memo数组赋值时,只需要通过查找右边的memo数组即可。
Java实现:
enum Index 
    GOOD, BAD, UNKNOWN


public class Solution 
    public boolean canJump(int[] nums) 
        Index[] memo = new Index[nums.length];
        for (int i = 0; i < memo.length; i++) 
            memo[i] = Index.UNKNOWN;
        
        memo[memo.length - 1] = Index.GOOD;

        for (int i = nums.length - 2; i >= 0; i--) 
            int furthestJump = Math.min(i + nums[i], nums.length - 1);
            for (int j = i + 1; j <= furthestJump; j++) 
                if (memo[j] == Index.GOOD) 
                    memo[i] = Index.GOOD;
                    break;
                
            
        

        return memo[0] == Index.GOOD;
    

时间复杂度O(n^2)

空间复杂度O(n)


Approach #4 (Greedy) [Accepted]

贪心方法: 在有了bottom-up方法后,我们很容易观察到:对于一个给定的位置,当我们判断是否能够跳到good position时,是从给定位置的下一个位置从左一步一步的向右一点点判断的。 从右向左迭代,判断当前位置+能跳的最大步数是否大于或等于离她最近的good Index,即判断: currPosition + nums[currPosition] >= leftmostGoodIndex
要是能到最近的good position,就说明当前位置是good .然后更新离的最近的good position的位置,也就是leftmostGoodIndex.迭代直到到达数组的首位。

时间复杂度O(n)

空间复杂度O(1)



Index 0 1 2 3 4 5 6
nums9421020
memoUGBBBGG
Java实现:
public class Solution 
    public boolean canJump(int[] nums) 
        int leftmostGoodIndex=nums.length-1;
        for(int j=nums.length-2;j>=0;j--)
            if(j+nums[j]>=leftmostGoodIndex)
                leftmostGoodIndex=j;
            
        
        return leftmostGoodIndex==0;
    








以上是关于贪心Greedy的主要内容,如果未能解决你的问题,请参考以下文章

零基础学启发式算法-贪心算法(Greedy Algorithm)

贪心Greedy

贪心问题的基本性质

「码趣分享」贪心算法Greedy Algorithm

每周算法贪心算法(Greedy Algorithm)

LeetCode贪心 greedy(共38题)