1. 概述

在面对算法问题时,我们通常可以采用多种方法来解决。其中最常被问到的问题之一就是:贪心算法(Greedy Approach)动态规划(Dynamic Programming) 之间的区别。

本文将从理论和实践两个层面,解释这两种算法的核心思想,并通过具体示例进行对比分析。

2. 贪心算法

2.1 核心思想

贪心算法是一种每一步都选择当前状态下最优解的策略,希望通过局部最优解(local optimal)来达到全局最优解(global optimal)。

适用前提:局部最优选择能导出全局最优解。
风险:如果问题不具备该特性,贪心算法可能无法得到正确解。

2.2 示例:活动选择问题

问题描述:给定一组活动,每个活动有开始时间和结束时间,一个人最多能参与多少个互不重叠的活动?

贪心策略:每次选择结束时间最早的活动。

✅ 证明:选择最早结束的活动可以为后续活动留下更多时间,因此该策略可以得到全局最优解。

algorithm MaxNonIntersectingActivitiesGreedy(activities):
    // INPUT
    //   activities = 活动列表
    // OUTPUT
    //   result = 最大不重叠活动数
    sortOnEndTime(activities)
    currentEndTime <- 负无穷
    result <- 0
    for each activity in activities:
        if currentEndTime < activity.startTime:
            result <- result + 1
            currentEndTime <- activity.endTime
    return result

时间复杂度分析

  • 排序:O(n log n)
  • 遍历:O(n)
  • 总体复杂度:✅ O(n log n)

经典应用:Dijkstra 算法、Prim 算法。

2.3 反例:0/1 背包问题

贪心算法无法解决 0/1 背包问题,因为局部最优无法保证全局最优
比如:一个物品单位价值高但体积太大,可能被贪心选中,但导致后续无法装下其他更优组合。

3. 动态规划

3.1 核心思想

动态规划是一种优化递归回溯的算法策略,主要解决重叠子问题指数级时间复杂度的问题。

核心机制:将已经计算过的状态结果保存下来,避免重复计算。

两种实现方式

  • 自顶向下(Top-down):递归 + 记忆化(memoization)
  • 自底向上(Bottom-up):迭代 + DP 表格

优势:将指数级复杂度优化为多项式级别,并保证最优解。

3.2 示例:活动选择问题的 DP 解法

我们重新来看活动选择问题,使用动态规划解决。

状态定义dp[i] 表示从第 i 个活动开始能选择的最大活动数。

状态转移

dp[i] = max(dp[i+1], 1 + dp[next])

其中:

  • dp[i+1]:不选第 i 个活动
  • 1 + dp[next]:选第 i 个活动,next 是下一个可选活动

实现思路

  1. 按开始时间排序
  2. 倒序遍历活动
  3. 使用二分查找快速定位下一个可选活动

时间复杂度

  • 排序:O(n log n)
  • 遍历 + 二分查找:O(n log n)
  • 总体复杂度:✅ O(n log n)

3.3 反例:旅行商问题(TSP)

虽然旅行商问题可以使用动态规划解决,但由于状态空间巨大(O(n * 2^n)),在实际中并不适合。

原因:子问题数量过多,状态难以压缩,DP 表格占用内存大。

4. 贪心 vs 动态规划 对比

特性 贪心算法 动态规划
核心思想 每步选当前最优 保存子问题结果
是否最优解 仅当局部最优导出全局最优时成立 ✅ 保证最优解
时间复杂度 ✅ 通常更低(多项式) ❗ 通常更高(多项式)
空间复杂度 ✅ 更优 ❗ 需要额外空间保存状态
适用场景 问题具备贪心选择性质 存在重叠子问题
典型例子 Dijkstra、Prim 0/1 背包、最长递增子序列

📌 总结:如果能用贪心解决,优先使用贪心;否则考虑 DP。

5. 总结

本文对比了贪心算法与动态规划的核心思想、适用场景和典型示例:

  • 贪心算法适合局部最优导出全局最优的问题,效率高但适用性有限;
  • 动态规划适合存在重叠子问题的场景,能保证最优解但时间和空间成本较高。

📌 建议

  • 面对新问题时,先尝试贪心解法;
  • 若贪心无法奏效,再考虑优化递归方案,使用动态规划。

📌 踩坑提醒

  • 不要盲目使用贪心,一定要证明局部最优是否能导出全局最优;
  • DP 虽好,但不要在状态空间爆炸时强行使用。

原始标题:Greedy Approach vs Dynamic Programming