【40讲系列7】贪心算法

一、理论

贪心(Greedy)算法:在对问题求解时,总是做出在当前看来是最好的选择。

   由于贪心算法每一次操作都需要取最大值或最小值,所以通常需要对数组排序。

适用Greedy 的场景:

  问题能够分成子问题来解决,子问题的最优解能递推到最终问题的最优解。 如果不能使用贪心算法,只需要举出反例即可。

贪心选择性质的证明(利用反证法,证明贪心算法的正确性):

  贪心算法为A,最优算法为O;如果证明过程中发现A完全能替代O,且不影响求出最优解,则出现矛盾。

贪心算法与动态规划的不同:

贪心对每个子问题的解决方案都作出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。

二、典型例题

①:买卖股票的最佳时期(LC122)

方法1:贪心(针对此问题的特殊解法,因为一天可以买卖无数次),一次遍历,只要只要后一天比前一天大 就在今天买入然后明天卖出(时间复杂度:O(n))

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for (int i = 0; i < prices.length - 1; i++) {
            if (prices[i] < prices[i+1]){
                res += prices[i+1] - prices[i];
            }
        }
        return res;
    }
}

 方法2:DP(解决股票问题的通用方法)------(时间复杂度:O(n))

class Solution {
    public int maxProfit(int[] prices) {
        // 状态的定义:dp[i][j]表示第i天交易完后 持股(j=1)或不持股(j=0)状态下 的最大利润,
        /*
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);
        }
        return dp[n - 1][0];
        */
        /**
         *  空间压缩 , 每一天的状态只与前一天的状态有关,
         */
        int n = prices.length;
        int dp0 = 0, dp1 = -prices[0];
        for (int i = 1; i < n; i++) {
            int newDp0 = Math.max(dp0, dp1 + prices[i]);
            int newDp1 = Math.max(dp1, dp0 - prices[i]);
            dp0 = newDp0;
            dp1 = newDp1;
        }
        return dp0;
    }
}

三、扩展例题

第一组:LeetCode455. 分发饼干LeetCode392. 判断子序列

第二组(贪心算法与动态规划):LeetCode435. 无重叠区间

原文地址:https://www.cnblogs.com/HuangYJ/p/14017461.html