贪心算法(greedy algorithms)
贪心算法(greedy algorithms)
作者:Bluemapleman([email protected])
麻烦不吝star和fork本博文对应的github上的技术博客项目吧!谢谢你们的支持!
知识无价,写作辛苦,欢迎转载,但请注明出处,谢谢!
文章目录
引入
贪心算法的思路是:希望在解决问题所要求的每一步中,都尽量选择哪个能让当前/局部(local)状态最优的做法,以希望达到全局也最优的状态。
之前的MST问题中的Kruskal算法就可以看作一种贪心算法,因为Kruskal算法就是先将边按照权重由小到大排列,然后按照这个顺序依次加入边,以希望最后得到的生成树也是最小的。
问题
活动选择(Activity Selection)
假设有一系列的活动,每个活动从某个时间点开始,并且持续进行一段时间,在某个时间点结束。我们希望选择一些活动去参加,使得参加的活动的总时长最长。
我们可以用以上贪心算法来解决该问题,主要思路是:不断选取完成时间最早的活动来做,并且移除和被选择的活动时间有重合的其它活动。重复这个过程直到没有任何可选活动。
该贪心算法针对这个问题得到的解是最优的。
哈夫曼编码(Huffman Codes)
假设有一系列的字符,我们希望用一些二进制码来代替这些字符以进行数据压缩,使得压缩后的总比特数最小。哈夫曼编码正是这样一样压缩数据的方式。
如果我们已知各字符在文本中的出现频率,考虑到为了让压缩后的数据更小,我们直觉是让出现频率高的字符用尽可能短的编码,而出现频率高的则可以用更长的编码。
哈夫曼编码的解决方案是这样的:不断找到当前出现频率最小的两个结点(字符或频率),将它们结合,作为一个新生成的结点的左右子结点,并将新生成的结点继续放入比较,直到没有落单的字符。
- 过程演示
该贪心算法针对这个问题得到的解是最优的。
边匹配(Edge Matching)
对于一个图G,我们希望从中选取一些两两互不相邻的边,使得这样的边的数量最多。
该贪心算法针对这个问题得到的解是不一定是最优的,但是它也确实得到了一个尽量最大化的解(Maximal)M,并且对于最优解,存在这样的关系:
顶点包含(Vertex Cover)
对于一个图G,我们尝试找到一个最小的顶点的集合C,使得C中的顶点整体碰到了所有图中的边。
但是这个算法不保证能找到最优结果,比如下面这种情况:
所以该算法会使得集合C的大小达到,不过我们可以通过改进使得|C|$\le$2OPT:
贪心算法找到全局最优解的要求
看了上面几个小实例,发现有时候贪心能找到全局最优解,有时候又不能,甚至找到的解会很差。那么决定一个贪心算法是否能找到全局最优解的条件是什么呢?
其实就是以下两点:
- 最优子结构(optimal subproblem structure,和动态规划中的是一个概念)
- 最优贪心选择属性(optimal greedy choice property)
01背包问题
有关01背包问题,大家具体可以看动态规划之01背包问题(最易理解的讲解)。
这里主要想讲的是,01背包问题是要求物品不可分割的,这时如果用贪心算法也无法保证全局最优。但是如果物品是可无限分割的(fractional knapsack),那么贪心又会是最优的(直觉上来说,优先将单位价值最高的物品放进背包总是没错的)。
我们可以简单证明贪心在此处的正确性:
参考文献
[1] Introduction to Algorithms: Third Edition, Thomas et al.