《算法设计与分析》复习指南

基于期末考试数学与复习提纲整理

考试基本信息与分值分布

1. 算法思路、复杂度、特性与稳定性对比表

算法名称 核心思路 时间复杂度 空间复杂度 稳定性 算法特性
归并排序 分治策略,将数组递归分成两半分别排序,然后合并 O(n log n) O(n) 稳定 分治算法,需要额外空间,适合外部排序
快速排序 分治策略,选取主元分区,递归排序子序列 平均O(n log n) O(log n) 不稳定 分治算法,原地排序,最坏情况O(n²)
二分搜索 每次比较中间元素,将搜索范围减半 O(log n) O(1) - 要求有序数组,分治思想
逆序对统计 分治策略,在归并排序过程中统计逆序对 O(n log n) O(n) - 基于归并排序修改,最大逆序对数为n(n-1)/2
钢条切割(DP) 动态规划,自底向上计算各长度钢条的最优解 O(n²) O(n) - 动态规划典型问题,具有最优子结构
最长公共子序列(LCS) 动态规划,填表记录子问题解,回溯找结果 O(mn) O(mn) - 动态规划典型问题,可优化空间复杂度
0-1背包问题 动态规划,决策每个物品是否放入背包 O(nW) O(nW) - NP完全问题,动态规划伪多项式时间算法
哈夫曼编码(贪心) 贪心策略,合并频率最小的节点构建最优二叉树 O(n log n) O(n) - 贪心算法,构造前缀编码,用于数据压缩
活动选择问题(贪心) 贪心策略,每次选择结束时间最早且与已选活动兼容的活动 O(n log n) O(1) - 贪心算法,按结束时间排序是关键
BFS(广度优先搜索) 队列实现,逐层遍历图/树的节点 O(V+E) O(V) - 求解无权图单源最短路径,找到的路径是最短路径
DFS(深度优先搜索) 栈/递归实现,深度优先遍历图/树的节点 O(V+E) O(V) - 用于拓扑排序、连通分量、环路检测等
Prim算法(MST) 贪心策略,从顶点扩展,每次添加连接树与非树节点的最小权边 O(V²)或O(E log V) O(V) - 最小生成树算法,适合稠密图
Kruskal算法(MST) 贪心策略,从边扩展,按权值排序,使用并查集避免环 O(E log V) O(E+V) - 最小生成树算法,适合稀疏图
Dijkstra算法 贪心策略,维护源点到各点的最短距离,每次扩展最短路径 O(V²)或O(E log V) O(V) - 单源最短路径,不能处理负权边
Bellman-Ford算法 动态规划思想,松弛所有边V-1次 O(VE) O(V) - 单源最短路径,可处理负权边,能检测负环

2. 贪心算法与动态规划的区别

贪心算法

  • 核心思想: 每一步都做出当前最优选择,希望得到全局最优解
  • 特性要求:
    • 贪心选择性质:局部最优选择能导致全局最优解
    • 最优子结构性质:问题的最优解包含子问题的最优解
  • 求解方式: 自顶向下,每一步选择后问题规模减小
  • 效率: 通常时间复杂度较低
  • 局限性: 不保证能得到全局最优解(如0-1背包问题)
  • 经典应用: 哈夫曼编码、活动选择、最小生成树(Prim, Kruskal)、Dijkstra最短路径

动态规划

  • 核心思想: 将问题分解为重叠子问题,保存子问题解避免重复计算
  • 特性要求:
    • 最优子结构性质:问题的最优解包含子问题的最优解
    • 重叠子问题性质:子问题会被多次重复计算
  • 求解方式: 通常自底向上填表,或自顶向下记忆化
  • 效率: 时间空间复杂度通常较高
  • 保证性: 保证得到全局最优解
  • 经典应用: 钢条切割、LCS、0-1背包、Floyd最短路径、Bellman-Ford

关键区别总结: 贪心算法每次选择后不可撤回,动态规划会考虑所有可能的选择;贪心算法不解决所有子问题,动态规划解决所有相关子问题。

3. 算法基础知识点

算法特征

  • 确定性: 无二义性,每条指令明确
  • 有限性: 步数有限,能在有限时间内完成
  • 输入: 有零个或多个输入
  • 输出: 有一个或多个输出
  • 可行性: 每步操作都是可行的

渐进记号

  • Θ (Theta): 紧确界,同时表示上界和下界
  • O (Big O): 上界,表示算法在最坏情况下的性能
  • Ω (Omega): 下界,表示算法在最好情况下的性能
f(n) = Θ(g(n)) ⇔ ∃c₁,c₂>0, n₀>0, ∀n≥n₀: 0≤c₁g(n)≤f(n)≤c₂g(n)

复杂度阶级比较

常见函数增长速度排序(从慢到快):

1 < log n < n < n log n < n² < 2ⁿ < n!

记忆口诀:常对幂指阶(常数、对数、多项式、指数、阶乘)

递归式求解

  • 主定理(Master Theorem): 求解 T(n) = aT(n/b) + f(n) 形式的递归方程
  • 递归树法: 适用于主定理不直接适用的情况(如分治规模不相等时)
  • 代入法: 猜测解的形式,用数学归纳法证明
// 主定理的三种情况: // 1. 若 f(n) = O(n^(log_b(a - ε))),则 T(n) = Θ(n^(log_b(a))) // 2. 若 f(n) = Θ(n^(log_b(a))),则 T(n) = Θ(n^(log_b(a)) * log n) // 3. 若 f(n) = Ω(n^(log_b(a + ε))),则 T(n) = Θ(f(n))

4. 分治策略的基本思想

分治三步骤

  1. 分解(Divide): 将原问题分解为若干个规模较小、相互独立、与原问题形式相同的子问题
  2. 解决(Conquer): 若子问题规模足够小则直接解决,否则递归地解决各个子问题
  3. 合并(Combine): 将各个子问题的解合并为原问题的解

典型分治算法

  • 归并排序: 分解为两个子数组,递归排序,合并有序子数组
  • 快速排序: 分区分解,递归排序两个子序列
  • 二分搜索: 每次比较中间元素,将搜索范围减半
  • 逆序对统计: 基于归并排序,在合并过程中统计逆序对

5. 动态规划:0-1背包与分数背包的差异

0-1背包问题

  • 物品特性: 每个物品要么完整放入,要么不放入(0或1)
  • 不可分割性: 物品不能分割,必须整个放入或不放入
  • 最优解性质: 不一定能通过贪心算法得到最优解
  • 解决方法: 动态规划
  • 状态定义: dp[i][w] 表示前i个物品在容量为w的背包中的最大价值

分数背包问题

  • 物品特性: 物品可以分割,可以放入部分物品
  • 可分割性: 可以按比例放入物品的一部分
  • 最优解性质: 可以通过贪心算法得到最优解(按价值重量比排序)
  • 解决方法: 贪心算法
  • 策略: 优先放入单位重量价值最高的物品

0-1背包的DP状态转移逻辑

状态定义: dp[i][w] 表示考虑前i个物品,背包容量为w时的最大价值

状态转移方程:

dp[i][w] = max(dp[i-1][w], dp[i-1][w-weight[i]] + value[i])

解释:

  • 不放入第i个物品: dp[i-1][w]
  • 放入第i个物品: dp[i-1][w-weight[i]] + value[i] (需满足 w ≥ weight[i])

初始化: dp[0][w] = 0 (0个物品时价值为0)

最终答案: dp[n][W],其中n为物品总数,W为背包总容量

// 0-1背包动态规划核心代码(空间优化版) int knapsack(int W, int weight[], int value[], int n) { int dp[W+1] = {0}; for (int i = 0; i < n; i++) { // 必须逆序更新,避免重复放入同一物品 for (int w = W; w >= weight[i]; w--) { dp[w] = max(dp[w], dp[w - weight[i]] + value[i]); } } return dp[W]; }

6. 贪心算法的知识点

贪心算法的理论基础

  • 贪心选择性质: 每一步的局部最优选择能导致全局最优解
  • 最优子结构性质: 问题的最优解包含其子问题的最优解
  • 与动态规划的区别: 贪心算法一旦做出选择就不可撤回,不解决所有子问题

经典贪心算法应用

  • 哈夫曼编码: 构造最优二叉树的过程,前缀码原理及数据压缩
  • 活动选择问题: 按结束时间排序的贪心准则
  • 最小生成树: Prim算法(从顶点扩展)、Kruskal算法(从边扩展)
  • 最短路径: Dijkstra算法(不能处理负权边)
  • 分数背包问题: 按价值重量比排序的贪心策略

贪心算法的局限性

  • 不一定总能得到全局最优解
  • 0-1背包问题: 贪心算法(按价值重量比)不能保证最优解
  • 零钱兑换问题: 对于某些硬币面额,贪心算法不能得到最优解(如面额为[1,3,4],兑换6元)
  • 需要严格证明贪心选择性质才能保证正确性

7. 图论算法的所有知识点

图的属性与基本定理

  • 握手定理(无向图): 所有顶点的度数之和等于边数的2倍
    ∑deg(v) = 2|E|
  • 握手定理(有向图): 所有顶点的入度之和等于出度之和等于边数
    ∑indeg(v) = ∑outdeg(v) = |E|
  • 生成树性质: n个顶点的生成树有n-1条边
  • 连通图: 任意两个顶点之间都有路径
  • 完全图: 任意两个顶点之间都有边,边数为n(n-1)/2

图的搜索算法

广度优先搜索(BFS)

  • 实现: 队列实现
  • 应用: 求解无权图单源最短路径、连通分量
  • 时间复杂度: O(V+E)
  • 空间复杂度: O(V)
  • 特点: 找到的路径是最短路径(边数最少)

深度优先搜索(DFS)

  • 实现: 栈/递归实现
  • 应用: 拓扑排序、连通分量、环路检测、强连通分量
  • 边分类: 树边、回边、前向边、横跨边
  • 时间复杂度: O(V+E)
  • 特点: 可以用于判断有向图是否有环(发现回边)

最小生成树(MST)算法

Prim算法

  • 策略: 从顶点扩展,贪心策略
  • 过程: 每次添加连接树与非树节点的最小权边
  • 适合场景: 稠密图
  • 时间复杂度: O(V²)(朴素实现)或O(E log V)(优先队列)
  • 与Dijkstra的区别: Prim考虑的是到树的距离,Dijkstra考虑的是到源点的距离

Kruskal算法

  • 策略: 从边扩展,贪心策略
  • 过程: 按权值排序所有边,依次加入不形成环的边
  • 数据结构: 并查集(Disjoint Set)检测环
  • 适合场景: 稀疏图
  • 时间复杂度: O(E log V)(排序占主要时间)

最短路径算法

Dijkstra算法

  • 策略: 贪心策略
  • 适用条件: 非负权边
  • 过程: 维护源点到各点的最短距离,每次扩展最短路径的顶点
  • 时间复杂度: O(V²)(朴素实现)或O(E log V)(优先队列)
  • 局限性: 不能处理负权边

Bellman-Ford算法

  • 策略: 动态规划思想
  • 适用条件: 可以处理负权边
  • 过程: 松弛所有边V-1次
  • 时间复杂度: O(VE)
  • 特点: 能检测负环(第V次松弛还能更新距离)

有向无环图(DAG)相关

  • 拓扑排序: 对有向无环图的顶点进行线性排序,使得对每条有向边(u,v),u在v之前
  • 充要条件: 有向图能进行拓扑排序 ⇔ 有向图是无环的
  • 算法:
    • Kahn算法(基于入度,BFS思想)
    • 基于DFS的拓扑排序
  • 应用: 任务调度、依赖关系解决、编译顺序确定