第十六章 贪心算法
总结:这一章通过活动选择问题引出贪心算法,讲了贪心策略的基本思想,并介绍了用了贪心策略的赫夫曼编码。
1. 贪心策略的基本思想
对有些最优化问题可以使用贪心算法来解决,贪心算法期望通过所做的局部最优选择来产生出一个全局最优解。但并不是所有最优化问题都可通过贪心算法来解决的。
贪心算法的步骤:
1) 将优化问题转化成这样的一个问题,即先做出选择,再解决剩下的一个子问题。
2) 证明原问题总是有有一个最优解是做贪心选择得到的,从而说明贪心选择的安全。
3) 说明在做出贪心选择后,剩余的子问题具有这样的一个性质。即如果将子问题的最优解和我们所做的贪心选择联合起来,可以得出原问题的一个最优解。
贪心选择性质:一个全局最优解可以通过局部最优(贪心)选择来达到。即当考虑做何选择时,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。
使用贪心算法,必须做两个证明:
1) 证明每一步所做的贪心选择最终能产生一个全局最优解。在证明中先考察一个全局最优解,然后证明对该解加以修改,使其采用贪心选择,这个选择将原问题变为一个相似的、但更小的问题。
2) 假设原问题中做了一个贪心选择而得到了一个子问题。证明将此子问题的最优解与所做的贪心选择合并后,的确可以得到原问题的一个最优解。
贪心算法与动态规划:
在动态规划中,每一步都要做出选择,但是这些选择依赖于子问题的解。因此,动态规划一般是自底向上,从小子问题处理至大子问题。
在贪心算法中,我们所做的总是当前看似最佳的选择,然后再解决选择之后出现的子问题。贪心策略是自顶向下的,不断地将给定的问题实例规约为更小的问题。
2. 赫夫曼编码
前缀编码:没有一个编码是另一个编码的前缀。
HUFFMAN编码是最优前缀码的贪心算法,在每一步所有可能的合并中,选择一个代价最小的合并。
设C是一个包含n个字符的集合,对C中的每个字符c,f[c]代表c出现的频度。显然,对出现频度大的字符,对它的编码的长度应该尽可能小。
伪代码
HUFFMAN(C)
n <- |C|
Q <- C
for i <- 1 to n
do allocate a new code z
left[z] <- x <- EXTRACT-MIN(Q)
right[z] <- y <- EXTRACT-MIN(Q)
f[z] <- f[x]+f[y]
INSERT(Q,z)
return EXTRACT-MIN(Q)
其中,Q是一个以f为关键字的最小优先级队列,可以用最小二叉堆实现。
复杂度分析:O(nlgn)