摘要
本文整理了 2025Spring 李言辉老师的《高级算法》课程复习题。
部分内容来自《算法导论》,推荐作为复习参考。
算法复杂度
- 最坏复杂度:算法在任何输入实例上运行时间的最坏情况,记为
。 - 举例:快速排序最坏情况
(当数组已有序且主元选择不当)。 - 平均复杂度:算法在所有可能输入实例上运行时间的期望,记为
。 - 举例:快速排序平均情况
。 - 最优复杂度:算法在“最优”输入实例上的最低运行时间(较少使用)。
- 举例:冒泡排序在已有序数组上为
。
渐近记号定义与函数证明
定义:
《算法导论》给出了三者的形象描述:
函数证明:
证明:
递归关系求解
解递归式:
解法(特征方程法):
- 特征方程:
,解得根 。 - 通解:
- 代入初始条件求解
。
例如,对于
则特征方程为
其解为
对于通项公式
代入
解得
则通项公式为
主定理应用
求解一般式:
主定理:
- 若
,则 。 - 若
,则 。 - 若
且 ( ),则 。
示例 1:
, 。 示例 2:
堆的定义与操作
- 定义:完全二叉树,满足堆性质(小根堆:$ \forall i,\ A[i] \leq A[\text{child}(i)] $)。
- 数组表示:下标从 1 开始,
的父节点在 ,子节点在 和 。 - 操作:
Insert(x)
:插至末尾,向上调整(),时间复杂度 。 ExtractMin()
:取根元素,末尾元素移至根,向下调整(),时间复杂度 。
快排平均复杂度分析复用
解递归式:
步骤:
- 两边乘
: 。 - 写
。 - 相减消和式:
- 令
,则有:
显然,
对抗策略分析
含义:通过构造最坏输入序列证明算法复杂度的下界。
问题:在数组中同时找到最大元素和最小元素
算法(分组比较法):
- 基本思路:采用分治策略,将元素两两分组处理,减少冗余比较。
- 具体步骤:
- 分组比较:将元素分为
对(若 为奇数,则保留一个元素单独处理)。每对比较一次,得到局部最小值和局部最大值(较小者参与最小值后续比较,较大者参与最大值后续比较)。比较次数: 。 - 找全局最小值:从所有局部最小值(共
个)中找全局最小值,比较次数: 。 - 找全局最大值:从所有局部最大值(共
个)中找全局最大值,比较次数: 。 - 总比较次数:
为偶数: 。 为奇数:设 ,比较次数为 (因为 当 为奇数)。 下界证明:
(对抗策略)
- 目标:证明任何算法在最坏情况下都至少需要
次比较。 - 对抗策略:对手通过控制比较结果最大化算法的不确定性:
- 初始状态:每个元素同时是最大值和最小值的候选(共
个候选状态)。 - 当算法比较两个元素
和 :
- 若两者均同时是最大值和最小值候选:对手返回
(固定顺序),并更新:
不再是最大值候选(因为 ), 不再是最小值候选(因为 )。 - 效果:最大值候选数减 1,最小值候选数减 1。
- 其他情况:对手选择结果以最小化状态减少(如仅使一个候选集减 1)。
- 候选状态变化:
- 最大值候选数需从
减至 (减少 个), - 最小值候选数需从
减至 (减少 个)。 - 总需减少状态数:
。 - 关键观察:
- 最有效的比较(同时减少两个候选集)最多发生
次(即分组阶段),对应减少 个状态。 - 剩余状态数:
。 - 剩余比较需单步减少(每次减 1),所需比较次数:
。 - 总最坏比较次数:
结合奇偶性分析:
- 若
为偶数: 。 - 若
为奇数: ,而 (因为 ),故相等。 - 紧致性:分组比较法达到此下界,证明其最优。
问题:在 个元素中找第二大元素
算法(锦标赛方法)
- 基本思路:模拟单淘汰锦标赛:
- 阶段 1(找最大值):元素两两比较,胜者(较大者)晋级。重复直至确定最大值(冠军)。
- 比较次数:
(每场淘汰 1 个元素)。 - 最大元素的直接对手:在晋级路径中,最大值依次击败
个元素,其中 (树的高度)。 - 阶段 2(找第二大):在最大元素的
个直接对手中,用 次比较找到最大值(即第二大)。 - 总比较次数:
。 下界证明:
(对抗策略)
- 核心定理:第二大元素
必须直接与最大元素 比较过(否则无法验证其为第二大)。 反证:若
未与 比较,则存在元素 使得 且算法未比较 与 (对手可设置 破坏 的第二大地位)。
- 对抗策略:
- 对手维护最大元素候选集和第二大元素候选集。
- 当算法比较两个元素时,对手选择结果使:
- 最大元素
尽可能迟地被确定(需至少 次比较), - 第二大元素候选集尽可能大(至少保留
个候选)。 - 下界分析:
- 确认最大元素
:需至少 次比较( 必须击败所有其他元素)。 - 确认第二大元素
: 必须直接败给 ,且是 的直接对手中的最大者。
至少有 个直接对手(因为其晋级路径高度为 )。 - 在
个候选中找到最大值需 次比较。 - 总比较次数下界:
- 紧致性:锦标赛方法达到该下界,证明其最优性。
平摊分析
含义:将操作序列的总代价分摊到每个操作的平均代价。
栈的 MultiPop 操作:平摊分析(会计法)
问题描述
- 栈支持两种操作:
Push(x)
:压入元素,实际代价为 。 MultiPop(k)
:弹出栈顶个元素(若元素不足,则弹出所有),实际代价为弹出元素个数。
- 序列从空栈开始,分析
次操作的平摊代价。
会计法分析
- 平摊代价分配:
Push
:分配平摊代价。 用于支付 Push
操作本身的实际代价。存储为信用,用于支付该元素未来被弹出的代价。
MultiPop(k)
:分配平摊代价。 - 弹出每个元素时,使用其存储的
信用支付实际代价。
- 弹出每个元素时,使用其存储的
- 总平摊代价分析:
- 设操作序列中有
次 Push
操作。 Push
总平摊代价:。 MultiPop
平摊代价:。 - 总平摊代价
。
- 设操作序列中有
- 实际代价说明:
MultiPop
弹出的元素总数(因元素均由 Push
压入)。- 总实际代价
( Push
代价)( MultiPop
弹出总代价)。
- 平摊结论:
- 总实际代价
( 为操作序列长度)。 - 每个操作的平均代价
。
- 总实际代价
删除大于中位数的元素:DeleteLargerHalf(平摊分析:势能法)
问题描述
- 动态集合支持:
Insert(x)
:插入元素,实际代价 。 DeleteLargerHalf()
:删除大于中位数的元素(即删除个最大元素),实际代价 (使用线性时间选择算法)。
- 分析
次操作序列的平摊代价。
势能法分析
- 势能函数:
,其中 是集合当前元素个数。 - 操作分析:
Insert
操作:- 实际代价:
。 - 势能变化:
。 - 平摊代价:
。
- 实际代价:
DeleteLargerHalf
操作:- 设操作前集合大小为
。 - 实际代价:
( 为正常数,来自选择算法)。 - 删除元素数:
。 - 势能变化:
。 - 平摊代价:
。
- 设操作前集合大小为
平摊代价上界证明
- 关键不等式:
因为 。 - 总平摊代价:
- 设序列中有
次 Insert
,次 DeleteLargerHalf
。 Insert
总平摊代价:。 DeleteLargerHalf
总平摊代价:。 - 总平摊代价
。
- 设序列中有
的界
- 引理:
。 - 证明:
- 记
为第 次删除的元素数,则 ( 为最终元素数)。 - 由
,得 ,求和得 。
- 记
- 证明:
- 总平摊代价:
- 最终结论:
( 为操作序列长度)。 - 总平摊代价
。 - 每个操作平摊代价
。
两种分析的核心思想对比
方法 | 适用场景 | 核心技巧 | 分析目标 |
---|---|---|---|
会计法 | 操作间存在"预支付"关系 | 为高风险操作分配额外信用 | 单次操作平摊代价 |
势能法 | 状态变化连续的复杂操作序列 | 定义势能函数描述系统状态,关联实际代价变化 | 证明总代价上界 |
两种方法均证明:单次最坏操作代价虽高,但序列平均代价为常数。
图搜索算法应用
深度优先搜索(DFS)思想:递归探索未访问邻居,回溯时处理节点
应用:
- 拓扑排序:在 DAG 上运行 DFS,按结束时间逆序排列。
- 强连通分量(SCC):Kosaraju 算法(正图 DFS + 反图 DFS)。
广度优先搜索(BFS)思想:按层遍历,使用队列管理节点
应用:
- 最短路径(无权图):记录起点到各点的层数(距离)。
- 任务调度关键路径:基于拓扑序的 BFS 计算最早开始时间。
贪心法原理与应用
-
思想:局部最优选择扩展至全局解。
-
特点:高效但不一定全局最优,需证明正确性(贪心选择性质与最优子结构)。
-
换硬币问题(特定面额):
- 策略:每次选最大面额硬币。
- 证明:若面额满足
,则贪心法最优。
-
活动安排问题:
- 策略:选结束时间最早且与已选活动兼容的活动。
- 证明(贪心选择性):设最优解包含活动
(最早结束),用 替换另一相容活动结果不变。
动态规划原理与应用
- 思想:将问题分解为重叠子问题,自底向上或备忘录求解。
- 特点:需满足最优子结构(子问题最优解组合为原问题最优解)。
最长公共子序列(LCS)
递归式:
伪代码:
1 |
|
最优二叉搜索树(OBST)
关于问题和算法的讲解:最优二叉搜索树 动态规划算法_哔哩哔哩_bilibili
问题定义
- 二叉搜索树(BST):满足每个节点的左子树键值 ≤ 该节点键值 ≤ 右子树键值的二叉树
- 访问频率:每个键
有搜索频率 (成功),区间 有搜索频率 (失败) - 目标:构建 BST 使搜索期望代价最小(考虑键值比较次数)
搜索代价公式
核心递归式
令
其中:
(区间总频率) 为当前子树的根节点
动态规划解法
1. 计算频率前缀和
1 |
|
2. 自底向上填表
1 |
|
示例 ( )
键 | 频率 |
区间 | 频率 |
---|---|---|---|
0.15 | 0.05 | ||
0.10 | 0.10 | ||
0.05 | 0.05 | ||
0.10 | 0.05 | ||
0.10 |
计算
1 |
|
最终代价:
时间复杂度分析
- 三重循环:
- 空间复杂度:
- 重建最优树:根据
root
表递归构造
Knuth 优化
发现最优根位置满足:
优化后时间复杂度降为
1 |
|
OBST vs Huffman 树
特性 | 最优二叉搜索树 | Huffman 编码树 |
---|---|---|
键位置 | 有序 | 无序 |
结构要求 | 满足 BST 性质 | 无结构限制 |
频率类型 | 成功+失败频率 | 仅叶节点频率 |
时间复杂度 | ||
主要用途 | 搜索优化 | 数据压缩 |
OBST 的核心思想:通过动态规划平衡高频键靠近树根,最小化整体期望搜索路径。它在数据库索引、语言模型等需要高效搜索的场景中有重要应用。