贪心算法

概述

贪心算法是一种在每一步做出局部最优选择的算法设计范式,期望通过一系列局部最优选择达到全局最优解。贪心算法的有效性依赖于两个核心性质:贪心选择性质最优子结构。与动态规划的”先求解后选择”不同,贪心算法采用"先选择后求解"的策略,通常效率更高但适用范围更窄。

定义

贪心算法(Greedy Algorithm)

贪心算法是一种通过一系列局部最优(贪心)选择来构造全局最优解的算法。在每一步决策中,贪心算法做出当前看来最好的选择,而不考虑未来的后果。这种”短视”策略之所以有效,是因为问题本身具有贪心选择性质——局部最优选择可以导向全局最优解。

核心性质

1. 贪心算法的两个核心前提

贪心算法的正确性依赖于以下两个性质:

  • 贪心选择性质:可以通过做出局部最优选择来产生全局最优解。这意味着贪心选择总是安全的——做出贪心选择后,剩余的子问题仍然有最优解,且贪心选择与子问题的最优解组合在一起构成原问题的全局最优解。
  • 最优子结构:原问题的最优解包含子问题的最优解。做出贪心选择后,剩余的子问题本身也必须是最优求解的。

2. 与动态规划的本质区别

特征贪心算法动态规划
决策方式先选择后求解先求解后选择
子问题依赖做出选择后只剩一个子问题需要考虑所有子问题
核心性质贪心选择性质 + 最优子结构最优子结构 + 重叠子问题
适用条件更严格(需要贪心选择性质)较宽松(只需最优子结构)
典型复杂度 或更低 或更高
全局最优性不一定保证(需严格证明)保证(在最优子结构成立时)

3. 0-1背包 vs. 分数背包的经典对比

这两个问题是说明贪心与动态规划区别的最佳例子:

  • 0-1背包:物品不可分割,贪心策略(选单位价值最高的)不保证最优,必须用动态规划
  • 分数背包:物品可以分割,贪心策略(选单位价值最高的)保证最优
0-1背包反例:
背包容量 W = 50
物品1: 重量 10, 价值 60  (单位价值 6)
物品2: 重量 20, 价值 100 (单位价值 5)
物品3: 重量 30, 价值 120 (单位价值 4)

贪心(选单位价值最高):选物品1(10,60) + 物品2(20,100) = 30/50, 价值 160
最优解:选物品2(20,100) + 物品3(30,120) = 50/50, 价值 220

分数背包(同一实例):
贪心:选物品1全部(10,60) + 物品2全部(20,100) + 物品3的20/30(20,80) = 50/50, 价值 240
这就是最优解

4. 贪心算法的六步设计法

  1. 确定问题的最优子结构
  2. 设计一个递归算法(基于最优子结构)
  3. 证明贪心选择性质:如果在每一步做出贪心选择,则只剩下一个子问题
  4. 证明最优子结构:做出贪心选择后,与子问题的最优解组合得到原问题的最优解
  5. 开发递归贪心算法
  6. 转化为迭代算法(如果可能)

5. 贪心正确性证明的核心技术

  • 替换论证(Exchange Argument):取最优解,逐步替换为贪心选择,证明替换后仍然最优
  • 本章中的三个应用:
    • Theorem 15.1(活动选择):替换最优解中最早结束的活动
    • Lemma 15.2(Huffman编码):替换最优编码树中最深层的叶节点
    • Theorem 15.5(离线缓存):四性质归纳构造,逐步对齐贪心解和最优解

经典问题汇总

问题贪心策略时间复杂度正确性证明
活动选择问题选最早结束的活动替换论证 (Thm 15.1)
哈夫曼编码合并频率最低的两个节点替换论证 (Lem 15.2)
离线缓存替换最远将来使用的项四性质归纳 (Thm 15.5)
分数背包选单位价值最高的物品贪心选择性质
Dijkstra最短路选距离最近的未访问节点贪心选择性质
最小生成树选权值最小的安全边剪切性质

章节扩展

第21章:最小生成树

MST问题是贪心策略的经典应用。安全边定理(定理21.1)充当贪心选择性质:横跨割的轻边一定是某棵MST的安全边。GENERIC-MST循环选择安全边加入A,保证A始终是某MST的子集。Kruskal按全局边权排序贪心选择,Prim按局部最小key贪心扩展。

第22章:单源最短路径

Dijkstra算法具有贪心性质:每次从优先队列中取出 d 值最小的顶点 u,其最短距离已确定(不会再被更新)。这一”贪心选择”的正确性依赖于所有边权非负——保证已确定的距离不会被后续松弛减小。

参见