贪心算法总结

一、基本概述

本质:

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

难点:

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

做题方法:

​ 毫无套路可言,最好用的策略就是多举几个测试用例,看一下贪心是否合适。

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

二、做题

第455题 分发饼干

一个数组表示 每个小孩对应的胃口

一个数组表示 每块饼干对应的尺寸

大部分情况下处理两个数组都会想到用两层 for 循环,但是本题中,可以只用一层 for 循环,

用 for 循环来遍历小孩对应的胃口,然后定义一个 index 变量 ,来控制饼干数组的遍历,这样可以大大的简化逻辑。

第376题 摆动序列

时间复杂度O(n),空间复杂度O(1)

这道题有点难了。。。

图示可以根据所给数组画折线图表示,折线图可以很清楚的看清整个过程的走势,

局部最优:删除单调坡度上的节点,但是单调坡度两端的节点不删,那么这个坡度就可以有两个局部峰值。

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

局部最优推出全局最优,并举不出反例,那么试试贪心算法。

贪心其实要从直觉出发,

比如目前要增加,那么我肯定是想把它增的高高的,这样随便随便再来一个数就会有比较大的概率是符合减的条件,

同理如果要减少,那么也是尽可能的降到足够的低,这样随便再来一个数就会有比较大的概率是符合增加的条件。

从上面两句话,来理解这个  贪  字,就是  大就足够的大  , 小就足够的小  ,  是不是就是贪心啦~~~

这也就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点1.

实际操作时,连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以。相当于是删除单一坡度上的节点,然后统计长度。

一个处理上的小技巧:

在同理峰值的时候,数组的最左面和最右面是不太容易来统计的,

因为整个统计的过程是根据统计相邻两数的差值计算,但是对于最左面的元素,没有前一个元素与其做差,也就没有差值

处理的方法就是,给最左面的元素的假定一个元素值(这个值等于最左面元素),这样就有了一个差值为0,这个处理有点类似于在处理链表问题时,设定一个 pre 节点。

第134题 加油站

法一:暴力解法

这道题的暴力解法也很巧妙,值得品味

首先:两层循环,第二层如何实现循环整个数组呢? 通过内层循环的起始位置设置为, j =( i + 1) % gas.size(),终止条件是 i != j

法二:贪心一

  • 情况一:如果 gas 的总和小于 cost 总和,那么无论从哪里出发,一定是跑不了一圈的
  • 情况二:rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
  • 情况三:如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。

第714题 买卖股票

股票问题常常使用动态规划解决,但有时候贪心也是可以滴

所谓贪心 如何得到最大利润呢?

就是最低值买,最高值就卖,注意这里的最高值,表示算上手续费还可以盈利

要点:找到两个点:买入日期,卖出日期

  • 买入日期:就是遇到更低的点就记录一下。
  • 卖出日期:这个不太好确定,但是也没有必要算出准确的卖出日期,只要 [当前价格] 大于 [当前价格+手续费] ,就可以收获利润,至于准确的卖出日期,就是连续收获利润区间里的最后一天,(并不需要计算是具体的那一天)

所以在收获利润操作时,其实有三种情况:

  • 情况一:收获利润的这一天并不是收获利润区间里的最后一天,也就是不卖出,后面继续收获利润
  • 情况二:前一天是收获利润区间里的最后一天,也就是前一天卖出,今天要重新记录最小价格
  • 情况三:保持原有状态,也就是不买不卖,不做任何操作

第968题 监控二叉树

从叶子节点开始找放置的位置,摄像头应该放在叶子节点的父节点的位置,一个摄像头可以相邻覆盖三层,所以隔两个节点放一个摄像头。

贪心思想

局部最优:让叶子节点的父节点安装摄像头,所用摄像头最少,

整体最优:全部摄像头数量最少。

每个节点可能具有的状态,分别对应一个数字:

  • 0:该节点无无摄像头且无覆盖
  • 1:该节点有摄像头
  • 2:该节点无摄像头且有覆盖

对于遍历的过程中遇到的空节点属于什么状态呢? 用假设法试一下~

  • 若为状态0,这样叶子节点就需要放摄像头了
  • 若为状态1,这样叶子节点的父节点就没有必要放摄像头了,而是可以把摄像头放在叶子节点的爷爷节点上了
  • 因此只能是状态2,这样就可以保证在叶子节点的父节点放置摄像头。

对于单个节点可能有的情况可分为以下四类:

  • 情况1:左右孩子都有覆盖,那么此时该节点应该就是无覆盖的状态了,也就是状态0
  • 情况2:左右孩子至少有一个无覆盖的情况,则该节点应该放置一个摄像头,也就是状态1
    • left == 0 && right == 0
    • left == 1 && right == 0
    • left == 2 && right == 0
    • left == 0 && right == 1
    • left == 0 && right == 2
  • 情况3:左右孩子至少有一个有摄像头,那么本节点无需放置摄像头,且已经是被覆盖的状态,也就是状态2
    • left == 1 && right == 1
    • left == 1 && right == 2
    • left == 2 && right == 1
  • 情况4:头结点没有覆盖
    • 以上三种情况都不涵盖对头结点的特殊处理,当递归结束后,头结点还有可能是 无覆盖的状态,
    • 因此递归结束后,需要单独判断一下头结点,如果没有被覆盖,则 result++;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值