通常用于最优化问题,我们做出一组选择来达到最优解。每步都追求局部最优。对很多问题都能求得最优解,而且速度比动态规划方法快得多。
16.1 活动选择问题
按结束时间排序,然后选择兼容活动。
定理16.1 考虑任意非空子问题Sk,令am是Sk中结束时间最早的活动,则am在Sk的某个最大兼容活动子集中。
16.2 贪心算法原理
设计贪心算法步骤:
1》将最优化问题转化为这样的形式:对其做出一次选择后,只剩下一个子问题需要求解。
2》证明作出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的。
3》证明作出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最优子结构。
贪心选择性质:
我们可以通过做出局部最优(贪心)选择来构造全局最优解。
动态规划:依赖子问题的解。自底向上或自顶向下,都需要先求解子问题。
贪心算法:进行选择时可能依赖之前的选择,但不依赖将来的选择或子问题的解。在进行第一次选择前不求解任何子问题。自顶向下。
如果进行贪心选择时我们不得不考虑众多选择,通常意味着可以改进贪心选择,使其更为高效。通过对输入进行预处理或者使用适合的数据结构(通常是优先队列),通常可使贪心选择更快速。
最优子结构:
如果一个问题的最优解包含其子问题的最优解,则称此问题具有最优子结构性质。此性质是能否应用动态规划和贪心算法的关键要素。
贪心对动态规划:
0-1背包问题(动态规划)和分数背包问题(贪心算法)
16.3 赫夫曼编码
前缀码:没有任何码字是其他码字的前缀。前缀码可以保证达到最优数据压缩率。
文件的最优编码方案总是对应一棵满(full)二叉树。即每个非叶结点都有两个孩子结点(国内外教程定义不一致,国内还要求同时是完全二叉树)。
若C为字母表且所以字符的出现频率均为正数,则最优前缀码对应的树恰有|C|个叶节点,每个叶节点对应字母表中的一个字符,且恰有|C|-1个内部结点。
引理16.2 令C为一个字母表,其中每个字符c属于C都一个频率c.freq。令x和y是C中频率最低的两个字符。那么存在C的一个最优前缀码,x和y的码字长度相同,且只有最后一个二进制位不同。
引理16.3 令C为一个字母表,其中每个字符c属于C都一个频率c.freq。令x和y是C中频率最低的两个字符。令C‘为C去掉字符x和y,加入 一个新字符z后得到的字母表,即C‘=C-{x,y}U{z}。类似C,也为C‘定义freq,不同之处只是z.freq=x.freq+y.freq。令T‘为字母表C‘的任意一个最优前缀码对应的编码树。于是我们可以将T‘中叶节点z替换为一个以x和y为孩子的内部结点,得到树T,而T表示字母表C的一个最优前缀码。
16.4 拟阵和贪心算法
16.5 用拟阵求解任务调度问题