贪心算法--数据结构与算法之美--CH37

1. 引言

  数据结构与算法之美的前述总结,已经基本将所有常用的数据结构和算法学习完了。
  本节以及后边几个总结,将就4个算法思想进行学习:贪心算法,分治算法,回溯算法,动态规划。这几种算法并不是具体的算法,而是解决问题的一种思想,思想这个东西不是记忆就能够掌握的,需要不断实战和总结,共勉。

2. 什么贪心算法

  定义贪心算法之前,先看一个案例:假设有一个可容纳 100kg的背包,现有 5 种豆子,每种的总量和总价值各不相同。为了让背包所装豆子的总价值最大,应该如何选择装哪些豆子?每种豆子又该装多少呢?

  显然,同等空间,单价越高价值越大。因此肯定是按照单价从高到低一次装入:20kg 黑豆、30kg 绿豆、50kg 红豆。

  以上思路本质上就是贪心算法的思想:在空间大小固定(限制值)的情况下,需要所装豆子价值(期望值)最大,则每一步选择同等限制值贡献下,期望值贡献高的那个。

3. 什么情况下可以用贪心算法

3.1 贪心算法有效

  如上分析,对贪心算法进行一个总结,什么情况下适合用贪心算法。

  1. 题目中定义了限制值和期望值。
  2. 每次选择同等限制值贡献情况下,期望贡献最大的数据。(或者同等期望值贡献下,限制值贡献较小的。)
  3. 举例验证,一般情况下贪心算法正确性显而易见。

3.2 贪心算法失效

  有些情况下,贪心算法就不work了,如图寻找s到t的最短路径。

  贪心算法选择了S-A-E-T,实际最短路径S-B-D-T,本质上由于“前面的选择,影响了后面的选择”,因此贪心算法不work了。
  这个问题用动态规划,能够得到正确结果,这也是动态规划和贪心算法的区别所在,动态规划就是解决这种前置操作影响后置操作的问题。

4. 贪心算法实战分析

4.1 分糖果

  有 m 个糖果和 n 个孩子,要把糖果分给孩子,但是糖果少孩子多(m<n),所以糖果只能满足部分孩子。每个糖果的大小分别是 s1,s2,s3,……,sm,同时 n 个孩子对糖果的需求分别是 g1,g2,g3,……,gn。
  只有糖果的大小大于等于需求的时候,孩子才得到满足,问题是,如何分配糖果,能尽可能满足最多数量的孩子?

解答

  1. 限制条件是糖果数量m,期望是得到满足的孩子。
  2. 将孩子按需求从小到大排序,同等期望贡献下,限制值损失最小。则考虑用按需求从小到大分配,分配给能满足其需求的最小的糖果。
  3. 依次类推,直到糖果分完或无法再分。

4.2 钱币找零

  有 1 元、2 元、5 元、10 元、20 元、50 元、100 元面额的纸币,张数分别是 c1、c2、c5、c10、c20、c50、c100。现在要用这些钱来支付 K 元,最少要用多少张纸币呢?
解答

  1. 期望值是K元,限制值是纸币数量。
  2. 同等期望希望限制值最小,则先用大面值的,然后用小面值,最后1元补齐。
  3. 这是直觉下的最优解,大部分情况下也是最优的,但是不排除特殊情况。

4.3 区间覆盖

  如下图,假设有 n 个区间,起始端点和结束端点分别是 [l1, r1],[l2, r2],[l3, r3],……,[ln, rn]。从 n 个区间中部分区间,满足两两不相交(端点相交的情况不算相交),最多能选出多少个区间呢?

解答

  1. 期望值区间数量最多,限制值区间宽度。
  2. 这里区间宽度取n个区间中最左端点lmin,最右端点rmax,因此区间宽度为[lmin,rmax]。
  3. 每次选择左端点与前面不覆盖,右端点尽量小的区间,如下图。

4.4 霍夫曼编码

  霍夫曼编码基本思想:按照字符频率,字符频率越高,编码越短。
如图,假设一段文本共6个字符,出现频率:

编码技巧如下

  1. 把每个字符看作一个节点,并辅带频率放到优先级队列中。
  2. 从队列中取出频率最小的两个节点 A、B,然后新建一个节点 C,频率设置为两个节点频率之和,作为节点 A、B 的父节点。
  3. 再把 C 节点放入到优先级队列中。
  4. 重复1,2,3,直到队列中没有数据。
      如下图:
  5. 给每一条边加上一个权值,指向左子节点的边统统标记为 0,指向右子节点的边,统统标记为 1,从根节点到叶节点的路径就是叶节点对应字符的霍夫曼编码。
      最终编码如下图所示:

5. 贪心算法的思考

  我理解贪心算法的本质就是"在满足限制条件下,只考虑当前最优的步骤,而不顾全大局",这也是其可能出现局部最优解的根因。

  以思考题为例:在一个非负整数 a 中,我们希望从中移除 k 个数字,让剩下的数字值最小,如何选择移除哪 k 个数字呢?
解答

  1. 移除k个数字,则需要k个步骤,只考虑每步最优。
  2. 第一步:一定是移除1个数字使得当前剩余数字最小,因此要从高位到低位,选择相邻两个高位大于低位的移除。比如123654,选到6移除得12354。
  3. 第二步:在得到的12354,递归使用第一步思路。选到5,得1234。依次类推,直到k步。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值