代码随想录算法训练营第三十一天 | 贪心算法理论基础,455. 分发饼干,376. 摆动序列,53. 最大子序和

贪心算法理论基础

什么是贪心

贪心的本质是选择每一阶段的局部最优,从而达到全局最优

贪心的套路(什么时候用贪心)

贪心算法并没有固定的套路

唯一的难点就是如何通过局部最优,推出整体最优。

那么如何能看出局部最优是否能推出整体最优呢?有没有什么固定策略或者套路呢?

不好意思,也没有! 靠自己手动模拟,如果模拟可行,就可以试一试贪心策略,如果不可行,可能需要动态规划。

有同学问了如何验证可不可以用贪心算法呢?

最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧

刷题或者面试的时候,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心

贪心一般解题步骤

贪心算法一般分为如下四步:

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

这个四步其实过于理论化了,我们平时在做贪心类的题目 很难去按照这四步去思考,真是有点“鸡肋”。

做题的时候,只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了。

总结

本篇给出了什么是贪心以及大家关心的贪心算法固定套路。

不好意思了,贪心没有套路,说白了就是常识性推导加上举反例

最后给出贪心的一般解题步骤,大家可以发现这个解题步骤也是比较抽象的,不像是二叉树,回溯算法,给出了那么具体的解题套路和模板。

455. 分发饼干

文章讲解:代码随想录

重点:

1. 局部最优:充分利用小饼干,小饼干先喂饱小胃口

思路:

1. 先把胃口和饼干从小到大排序

Arrays.sort(g);
Arrays.sort(s);

2. 从小到大遍历饼干,如果当前饼干满足当前的最小胃口,则childIndex+1,移动到满足下一个小孩

int childIndex = 0;
for (int i = 0; i < s.length; i++) {
    if (childIndex < g.length && g[childIndex] <= s[i]) {
        childIndex++;
    }
}
public int findContentChildren(int[] g, int[] s) {
    Arrays.sort(g);
    Arrays.sort(s);
    int childIndex = 0;
    for (int i = 0; i < s.length; i++) {
        if (childIndex < g.length && g[childIndex] <= s[i]) {
            childIndex++;
        }
    }
    return childIndex;
}

376. 摆动序列

文章讲解:代码随想录

重点:

1. 局部最优:删掉单调坡度上的中间节点,让峰值尽可能的保持峰值

2. 整体最优: 整个序列有多个局部峰值,从而达到最长摆动序列

3. 三种特殊情况:上下坡中有平坡,首尾元素,单调坡中有平坡

普通情况:

上下坡中有平坡:

首尾元素:

if (nums.length == 1) { return 1; }

可以假设,数组最前面还有一个数字,那这个数字应该是什么呢?

之前我们在 讨论 情况一:相同数字连续 的时候, prediff = 0 ,curdiff < 0 或者 >0 也记为波谷。

那么为了规则统一,针对序列[2,5],可以假设为[2,2,5],这样它就有坡度了即 preDiff = 0,如图:

单调坡中有平坡:

思路:

贪心算法:

1.  数组长度只为1时,直接返回1

2. prediff = 0,默认最前面多加一个相同的元素,result = 1,默认最后一个元素为摆动序列的值

// 上一个差值
int prediff = 0;
// 当前差值
int curdiff = 0;
// 最终摆动序列长度,默认包括最后一个元素
int result = 1;
for (int i = 0; i < nums.length - 1; i++) {
    // 计算当前差值
    curdiff = nums[i + 1] - nums[i];
    // 如果当前差值和上一个差值为一正一负
    // 等于0的情况表示首元素
    if ((prediff >= 0 && curdiff < 0) || (prediff <= 0 && curdiff > 0)) {
        result++;
        // prediff放在if里面更新是为了防止单调坡中的平坡也被计算了
        prediff = curdiff;
    }
}
public int wiggleMaxLength(int[] nums) {
    if (nums.length == 1) {
        return 1;
    }
    // 上一个差值
    int prediff = 0;
    // 当前差值
    int curdiff = 0;
    // 最终摆动序列长度,默认包括最后一个元素
    int result = 1;
    for (int i = 0; i < nums.length - 1; i++) {
        // 计算当前差值
        curdiff = nums[i + 1] - nums[i];
        // 如果当前差值和上一个差值为一正一负
        // 等于0的情况表示首元素
        if ((prediff >= 0 && curdiff < 0) || (prediff <= 0 && curdiff > 0)) {
            result++;
            // prediff放在if里面更新是为了防止单调坡中的平坡也被计算了
            prediff = curdiff;
        }
    }
    return result;
}

53. 最大子序和

文章讲解:代码随想录

重点:

贪心算法:

1. 局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。

2. 全局最优:选取最大“连续和”

局部最优的情况下,并记录最大的“连续和”,可以推出全局最优

常见误区:

1. 不少同学认为 如果输入用例都是-1,或者 都是负数,这个贪心算法跑出来的结果是 0, 这是又一次证明脑洞模拟不靠谱的经典案例,建议大家把代码运行一下试一试,就知道了,也会理解 为什么 result 要初始化为最小负数了。

思路:

贪心算法:

1. 初始化记录最大值的result和当前连续和curSum

int result = Integer.MIN_VALUE;
int curSum = 0;

2. for循环遍历nums,当前连续和大于之前记录的最大值,更新result。当当前连续和为负数时,会拖累后面的累加和,直接舍弃

for (int num : nums) {
    curSum += num;
    // 取区间累计的最大值(相当于不断确定最大子序终止位置)
    if (curSum > result) {
        result = curSum;
    }
    // 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
    if (curSum < 0) {
        curSum = 0;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值