大O记号 :上界
f ( n ) = O ( g ( n ) ) ⟺ ∃ c > 0 , n 0 > 0 , ∀ n ≥ n 0 : f ( n ) ≤ c ⋅ g ( n ) f(n) = O(g(n)) \iff \exists c > 0, n_0 > 0, \forall n \geq n_0: f(n) \leq c \cdot g(n) f ( n ) = O ( g ( n )) ⟺ ∃ c > 0 , n 0 > 0 , ∀ n ≥ n 0 : f ( n ) ≤ c ⋅ g ( n )
Ω记号 :下界
f ( n ) = Ω ( g ( n ) ) ⟺ ∃ c > 0 , n 0 > 0 , ∀ n ≥ n 0 : f ( n ) ≥ c ⋅ g ( n ) f(n) = \Omega(g(n)) \iff \exists c > 0, n_0 > 0, \forall n \geq n_0: f(n) \geq c \cdot g(n) f ( n ) = Ω ( g ( n )) ⟺ ∃ c > 0 , n 0 > 0 , ∀ n ≥ n 0 : f ( n ) ≥ c ⋅ g ( n )
Θ记号 :紧界
f ( n ) = Θ ( g ( n ) ) ⟺ f ( n ) = O ( g ( n ) ) ∧ f ( n ) = Ω ( g ( n ) ) f(n) = \Theta(g(n)) \iff f(n) = O(g(n)) \wedge f(n) = \Omega(g(n)) f ( n ) = Θ ( g ( n )) ⟺ f ( n ) = O ( g ( n )) ∧ f ( n ) = Ω ( g ( n ))
复杂度 名称 示例 O ( 1 ) O(1) O ( 1 ) 常数 哈希表 查找O ( log n ) O(\log n) O ( log n ) 对数 二分查找 O ( n ) O(n) O ( n ) 线性 遍历数组 O ( n log n ) O(n \log n) O ( n log n ) 线性对数 归并排序 O ( n 2 ) O(n^2) O ( n 2 ) 平方 冒泡排序 O ( 2 n ) O(2^n) O ( 2 n ) 指数 子集枚举 O ( n ! ) O(n!) O ( n !) 阶乘 全排列
主定理(Master Theorem) :
T ( n ) = a T ( n / b ) + f ( n ) T(n) = aT(n/b) + f(n) T ( n ) = a T ( n / b ) + f ( n )
情况 条件 结果 1 f ( n ) = O ( n log b a − ϵ ) f(n) = O(n^{\log_b a - \epsilon}) f ( n ) = O ( n l o g b a − ϵ ) T ( n ) = Θ ( n log b a ) T(n) = \Theta(n^{\log_b a}) T ( n ) = Θ ( n l o g b a ) 2 f ( n ) = Θ ( n log b a log k n ) f(n) = \Theta(n^{\log_b a} \log^k n) f ( n ) = Θ ( n l o g b a log k n ) T ( n ) = Θ ( n log b a log k + 1 n ) T(n) = \Theta(n^{\log_b a} \log^{k+1} n) T ( n ) = Θ ( n l o g b a log k + 1 n ) 3 f ( n ) = Ω ( n log b a + ϵ ) f(n) = \Omega(n^{\log_b a + \epsilon}) f ( n ) = Ω ( n l o g b a + ϵ ) T ( n ) = Θ ( f ( n ) ) T(n) = \Theta(f(n)) T ( n ) = Θ ( f ( n ))
将问题分解为若干子问题,递归求解后合并:
Divide: 将问题分解为子问题
Conquer: 递归求解子问题
Combine: 合并子问题的解
归并排序 :
T ( n ) = 2 T ( n / 2 ) + O ( n ) = O ( n log n ) T(n) = 2T(n/2) + O(n) = O(n \log n) T ( n ) = 2 T ( n /2 ) + O ( n ) = O ( n log n )
快速排序 :
平均:O ( n log n ) O(n \log n) O ( n log n )
最坏:O ( n 2 ) O(n^2) O ( n 2 ) (已排序输入 + 固定主元选择)
随机化后期望:O ( n log n ) O(n \log n) O ( n log n )
最近点对 :
T ( n ) = 2 T ( n / 2 ) + O ( n ) = O ( n log n ) T(n) = 2T(n/2) + O(n) = O(n \log n) T ( n ) = 2 T ( n /2 ) + O ( n ) = O ( n log n )
Strassen 矩阵乘法 :
T ( n ) = 7 T ( n / 2 ) + O ( n 2 ) = O ( n log 2 7 ) ≈ O ( n 2.807 ) T(n) = 7T(n/2) + O(n^2) = O(n^{\log_2 7}) \approx O(n^{2.807}) T ( n ) = 7 T ( n /2 ) + O ( n 2 ) = O ( n l o g 2 7 ) ≈ O ( n 2.807 )
局部最优选择能导致全局最优解。
活动选择问题 :选择最多不重叠活动。
策略:按结束时间排序,贪心选择最早结束的活动。
Huffman 编码 :
构建最优前缀码
每次合并频率最低的两个节点
时间复杂度 :O ( n log n ) O(n \log n) O ( n log n )
最小生成树 :
Kruskal 算法:按边权排序,用并查集 判断是否形成环。O ( E log E ) O(E \log E) O ( E log E )
Prim 算法:从任一顶点出发,每次选最短边扩展。O ( E log V ) O(E \log V) O ( E log V ) (优先队列)
Dijkstra 最短路径 :
T = O ( ( V + E ) log V ) T = O((V + E) \log V) T = O (( V + E ) log V )
限制:不能有负权边。
交换论证法 :
假设存在最优解 O O O 与贪心解 G G G 不同
找到第一个不同的选择
证明将 O O O 的选择替换为 G G G 的选择不会变差
反复替换,最终 O O O 变为 G G G
最优子结构 :问题的最优解包含子问题的最优解。
重叠子问题 :递归求解中大量子问题被重复计算。
定义子问题(状态)
建立状态转移方程
确定计算顺序(拓扑序)
确定边界条件
可选:空间优化
0-1 背包 :
d p [ i ] [ w ] = max ( d p [ i − 1 ] [ w ] , d p [ i − 1 ] [ w − w i ] + v i ) dp[i][w] = \max(dp[i-1][w], dp[i-1][w-w_i] + v_i) d p [ i ] [ w ] = max ( d p [ i − 1 ] [ w ] , d p [ i − 1 ] [ w − w i ] + v i )
时间:O ( n W ) O(nW) O ( nW ) ,空间可优化至 O ( W ) O(W) O ( W ) 。
最长公共子序列(LCS) :
d p [ i ] [ j ] = { d p [ i − 1 ] [ j − 1 ] + 1 if s 1 [ i ] = s 2 [ j ] max ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) otherwise dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & \text{if } s_1[i] = s_2[j] \\ \max(dp[i-1][j], dp[i][j-1]) & \text{otherwise} \end{cases} d p [ i ] [ j ] = { d p [ i − 1 ] [ j − 1 ] + 1 max ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ]) if s 1 [ i ] = s 2 [ j ] otherwise
编辑距离 :
d p [ i ] [ j ] = min { d p [ i − 1 ] [ j ] + 1 删除 d p [ i ] [ j − 1 ] + 1 插入 d p [ i − 1 ] [ j − 1 ] + cost 替换 dp[i][j] = \min \begin{cases} dp[i-1][j] + 1 & \text{删除} \\ dp[i][j-1] + 1 & \text{插入} \\ dp[i-1][j-1] + \text{cost} & \text{替换} \end{cases} d p [ i ] [ j ] = min ⎩ ⎨ ⎧ d p [ i − 1 ] [ j ] + 1 d p [ i ] [ j − 1 ] + 1 d p [ i − 1 ] [ j − 1 ] + cost 删除 插入 替换
矩阵链乘法 :
d p [ i ] [ j ] = min i ≤ k < j { d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p i − 1 ⋅ p k ⋅ p j } dp[i][j] = \min_{i \leq k < j} \{dp[i][k] + dp[k+1][j] + p_{i-1} \cdot p_k \cdot p_j\} d p [ i ] [ j ] = min i ≤ k < j { d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p i − 1 ⋅ p k ⋅ p j }
滚动数组 :当状态转移只依赖前一行/列时,只保留两行。
单调队列优化 :滑动窗口最大值问题。
斜率优化 :决策单调性问题时,用凸包维护候选决策。
def backtrack (state, choices):
if is_solution(state):
record(state)
return
for choice in choices:
if is_valid(state, choice):
make_choice(state, choice)
backtrack(state, next_choices)
undo_choice(state, choice)
约束剪枝 :不满足约束条件时提前返回。
限界剪枝 :当前解不可能优于已知最优解时返回。
N皇后 :在 n × n n \times n n × n 棋盘放置 n n n 个互不攻击的皇后。
子集和 :从集合中选取子集使和等于目标值。
图着色 :用最少的颜色给图 的顶点着色,相邻顶点颜色不同。
特性 回溯法 分支限界法 搜索方式 深度优先 广度优先/最佳优先 数据结构 栈 优先队列 目标 找所有解 找最优解 剪枝 约束+限界 限界为主
使用优先队列按限界值排序,优先扩展最有希望的节点。
0-1 背包的分支限界 :
上界估计:剩余物品按单位价值贪心装入
每次取出上界最大的节点扩展
类 定义 示例 P 多项式时间可解 排序、最短路径 NP 多项式时间可验证 TSP、SAT NPC NP中最难的问题 3-SAT、Clique co-NP NP的补 不可满足性
归约 :A ≤ p B A \leq_p B A ≤ p B 表示问题 A 可在多项式时间内归约到问题 B。
NP 完全问题 :
Cook-Levin 定理 :SAT 是 NP 完全的。
问题 描述 SAT 布尔公式可满足性 3-SAT 3-CNF 公式可满足性 Clique 图 中是否存在 k-团Vertex Cover 最小顶点覆盖 TSP 旅行商问题 Subset Sum 子集和问题 Knapsack 0-1 背包(弱NP完全) Graph Coloring 图 着色问题
对于 NP 难问题,寻找近似解:
近似比 :
ρ = max ( 近似解 最优解 , 最优解 近似解 ) \rho = \max\left(\frac{\text{近似解}}{\text{最优解}}, \frac{\text{最优解}}{\text{近似解}}\right) ρ = max ( 最优解 近似解 , 近似解 最优解 )
问题 近似比 算法 顶点覆盖 2 贪心匹配 TSP(三角不等式) 2 MST + 匹配 TSP(三角不等式) 1.5 Christofides 背包 1 + ϵ 1+\epsilon 1 + ϵ FPTAS 一般 TSP 无常数比 除非 P=NP