Skip to content

速查表

第 1 页:概览与复杂度

一份快速参考,助你理解算法、效率与增长率。 阅读或编码时,可将此页置于手边。

什么是算法?

算法是一个清晰、逐步解决问题的过程。

特性描述
精确性每个步骤都明确无误
有限性必须在有限步后停止
有效性每个步骤机器或人都可执行
确定性相同输入,相同输出(通常如此)

可以将其想象成一个食谱:

  • 输入:食材
  • 步骤:说明
  • 输出:最终菜肴

核心特性

概念要问的问题
正确性它是否总能解决问题?
终止性它最终会停止吗?
复杂度它需要多少时间和空间?
清晰性它是否易于理解和实现?

为什么复杂度很重要

随着输入规模 $n$ 的增加,不同算法的增长方式不同。

增长率示例算法当 $n$ 翻倍时的影响
$O(1)$哈希查找无变化
$O(\log n)$二分查找轻微增加
$O(n)$线性扫描翻倍
$O(n\log n)$归并排序略高于 2 倍
$O(n^2)$冒泡排序变为 4 倍
$O(2^n)$子集生成急剧增长
$O(n!)$暴力排列(穷举)当 $n>10$ 时基本不可用

度量时间与空间

度量含义示例
时间复杂度操作次数从 1 循环到 $n$:$O(n)$
空间复杂度内存使用量(栈、堆、数据结构)递归调用深度:$O(n)$

简单规则:

  • 顺序步骤:成本求和
  • 嵌套循环:规模相乘
  • 递归:使用递推关系

常见模式

模式成本公式复杂度
单层循环(1 到 $n$)$T(n) = n$$O(n)$
嵌套循环($n \times n$)$T(n) = n^2$$O(n^2)$
每次减半$T(n) = \log_2 n$$O(\log n)$
分治法(分成两半)$T(n) = 2T(n/2)+n$$O(n\log n)$

翻倍规则

用 $n$ 和 $2n$ 运行算法:

观察结果可能的复杂度
时间恒定$O(1)$
时间翻倍$O(n)$
时间变为 4 倍$O(n^2)$
时间乘以对数因子$O(n\log n)$

微型代码:二分查找

python
def binary_search(arr, x):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if arr[mid] == x:
            return mid
        elif arr[mid] < x:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

复杂度: $$T(n) = T(n/2) + 1 \Rightarrow O(\log n)$$

常见陷阱

问题提示
差一错误仔细检查循环边界
无限循环确保终止条件可达
中点溢出(C/C++)使用 mid = lo + (hi - lo) / 2
在搜索中使用未排序的数据二分查找仅适用于已排序的输入

增长率快速总结

类型公式示例描述
常数$1$固定时间
对数$\log n$每次减半
线性$n$遍历所有项
线性对数$n \log n$类似排序的复杂度
平方$n^2$双层循环
立方$n^3$三层嵌套循环
指数$2^n$所有子集
阶乘$n!$所有排列

简单经验法则

手动追踪小例子。 计算步数、内存使用和递归深度。 在运行代码之前,你就能看出增长趋势。

第 2 页:递归式与主定理

本页将帮助你分解递归算法,并使用递归式估算其运行时间。

什么是递归式?

递归关系式用更小的子问题的成本来表达问题成本 $T(n)$。

典型结构:

$$ T(n) = a T\left(\frac{n}{b}\right) + f(n) $$

其中:

  • $a$ = 子问题的数量
  • $b$ = 输入规模缩小的因子
  • $f(n)$ = 每次调用额外的工作量(合并、组合等)

常见的递归式

算法递归式形式
二分查找$T(n)=T(n/2)+1$$O(\log n)$
归并排序$T(n)=2T(n/2)+n$$O(n\log n)$
快速排序(平均)$T(n)=2T(n/2)+O(n)$$O(n\log n)$
快速排序(最坏)$T(n)=T(n-1)+O(n)$$O(n^2)$
矩阵乘法$T(n)=8T(n/2)+O(n^2)$$O(n^3)$
Karatsuba 算法$T(n)=3T(n/2)+O(n)$$O(n^{\log_2 3})$

求解递归式

有几种方法可以求解递归式:

方法描述最适用场景
迭代法逐步展开简单的递归式
代入法猜测并用归纳法证明验证
递归树法可视化每一层的总工作量分治算法
主定理求解 $T(n)=aT(n/b)+f(n)$ 的捷径标准形式

主定理

给定 $$T(n) = aT(n/b) + f(n)$$

令 $$n^{\log_b a}$$ 为“临界项”

情况条件结果
1如果 $f(n) = O(n^{\log_b a - \varepsilon})$$T(n) = \Theta(n^{\log_b a})$
2如果 $f(n) = \Theta(n^{\log_b a}\log^k n)$$T(n) = \Theta(n^{\log_b a}\log^{k+1} n)$
3如果 $f(n) = \Omega(n^{\log_b a + \varepsilon})$ 且满足正则条件$T(n) = \Theta(f(n))$

示例

算法$a$$b$$f(n)$情况$T(n)$
归并排序22$n$2$\Theta(n\log n)$
二分查找12$1$1$\Theta(\log n)$
Strassen 矩阵乘法72$n^2$2$\Theta(n^{\log_2 7})$
快速排序(平均)22$n$2$\Theta(n\log n)$

递归树可视化

将成本分解到各层:

示例:$T(n)=2T(n/2)+n$

层数节点数每个节点的工作量总工作量
01$n$$n$
12$n/2$$n$
24$n/4$$n$
............

对 $\log_2 n$ 层求和:

$$T(n) = n \log_2 n$$

微型代码:快速幂运算

高效计算 $a^n$。

python
def power(a, n):
    res = 1
    while n > 0:
        if n % 2 == 1:
            res *= a
        a *= a
        n //= 2
    return res

递归式:

$$T(n) = T(n/2) + O(1) \Rightarrow O(\log n)$$

迭代法示例

求解 $T(n)=T(n/2)+n$

展开:

$$ \begin{aligned} T(n) &= T(n/2) + n \ &= T(n/4) + n/2 + n \ &= T(n/8) + n/4 + n/2 + n \ &= \ldots + n(1 + 1/2 + 1/4 + \ldots) \ &= O(n) \end{aligned} $$

常见形式

形式结果
$T(n)=T(n-1)+O(1)$$O(n)$
$T(n)=T(n/2)+O(1)$$O(\log n)$
$T(n)=2T(n/2)+O(1)$$O(n)$
$T(n)=2T(n/2)+O(n)$$O(n\log n)$
$T(n)=T(n/2)+O(n)$$O(n)$

快速检查清单

  1. 识别 $a$、$b$ 和 $f(n)$
  2. 比较 $f(n)$ 与 $n^{\log_b a}$
  3. 应用正确的情况
  4. 确认假设(正则条件)
  5. 给出最终的复杂度

理解递归式有助于你在编码前估算性能。 始终关注子问题的数量、大小以及合并成本。

第 3 页:排序概览

排序是最常见的算法任务之一。本页帮助您快速比较各种排序方法、它们的复杂度、稳定性以及适用场景。

为什么排序很重要

排序可以组织数据,使搜索、合并和分析变得高效。 一旦输入数据被排序,许多问题就会变得更简单。

快速比较表

算法最佳情况平均情况最坏情况空间复杂度稳定原地备注
冒泡排序$O(n)$$O(n^2)$$O(n^2)$$O(1)$简单,适合教学
选择排序$O(n^2)$$O(n^2)$$O(n^2)$$O(1)$交换次数少
插入排序$O(n)$$O(n^2)$$O(n^2)$$O(1)$适用于小数据或部分有序数据
归并排序$O(n\log n)$$O(n\log n)$$O(n\log n)$$O(n)$稳定,分治策略
快速排序$O(n\log n)$$O(n\log n)$$O(n^2)$$O(\log n)$平均速度快,原地排序
堆排序$O(n\log n)$$O(n\log n)$$O(n\log n)$$O(1)$不稳定
计数排序$O(n+k)$$O(n+k)$$O(n+k)$$O(n+k)$仅适用于整数键
基数排序$O(d(n+k))$$O(d(n+k))$$O(d(n+k))$$O(n+k)$按位排序
桶排序$O(n+k)$$O(n+k)$$O(n^2)$$O(n)$需要数据均匀分布

如何选择排序算法

场景最佳选择
小数组或接近有序的数据插入排序
需要稳定排序,一般情况归并排序或 Timsort
需要原地排序且平均速度快快速排序
保证最坏情况 $O(n\log n)$堆排序
小整数键或有限范围计数排序或基数排序
外部排序(大数据)外部归并排序

微型代码:插入排序

对初学者来说简单直观。

python
def insertion_sort(a):
    for i in range(1, len(a)):
        key = a[i]
        j = i - 1
        while j >= 0 and a[j] > key:
            a[j + 1] = a[j]
            j -= 1
        a[j + 1] = key
    return a

复杂度: $$T(n) = O(n^2)$$ 平均情况,$$O(n)$$ 最佳情况(已排序)

分治排序

归并排序

分割列表,排序子序列,合并结果。

递归式: $$T(n) = 2T(n/2) + O(n) = O(n\log n)$$

微型代码:

python
def merge_sort(a):
    if len(a) <= 1:
        return a
    mid = len(a)//2
    L = merge_sort(a[:mid])
    R = merge_sort(a[mid:])
    i = j = 0
    res = []
    while i < len(L) and j < len(R):
        if L[i] <= R[j]:
            res.append(L[i]); i += 1
        else:
            res.append(R[j]); j += 1
    res.extend(L[i:]); res.extend(R[j:])
    return res
快速排序

选择枢轴,分区,排序子数组。

递归式: $$T(n) = T(k) + T(n-k-1) + O(n)$$ 平均情况:$$O(n\log n)$$ 最坏情况:$$O(n^2)$$

微型代码:

python
def quick_sort(a):
    if len(a) <= 1:
        return a
    pivot = a[len(a)//2]
    left  = [x for x in a if x < pivot]
    mid   = [x for x in a if x == pivot]
    right = [x for x in a if x > pivot]
    return quick_sort(left) + mid + quick_sort(right)

稳定排序 vs 不稳定排序

属性描述示例
稳定相等元素保持原始顺序归并排序,插入排序
不稳定可能重排相等元素的顺序快速排序,堆排序

可视化技巧

模式描述
冒泡比较并交换相邻元素
选择每轮选择最小值
插入逐步扩展有序区域
归并分割,征服,合并
快速分区并递归
建堆,重复提取

总结表

类型类别复杂度稳定空间复杂度
简单排序冒泡,选择$O(n^2)$视情况而定$O(1)$
插入排序增量式$O(n^2)$$O(1)$
分治排序归并,快速$O(n\log n)$归并稳定归并非原地
分布排序计数,基数$O(n+k)$$O(n+k)$
混合排序Timsort, IntroSort$O(n\log n)$视情况而定

如有疑问,可以从 Timsort(Python)或 std::sort(C++)开始,它们能动态适应。

第 4 页:搜索与选择

搜索意味着从集合中找到你需要的元素。选择意味着挑选特定的元素,例如最小、最大或第 k 个元素。本页对两者进行总结。

搜索基础

类型描述数据要求复杂度
线性搜索逐个检查$O(n)$
二分搜索每一步将范围除以 2已排序$O(\log n)$
跳跃搜索向前跳过固定步数已排序$O(\sqrt n)$
插值搜索基于值猜测位置已排序,均匀分布平均 $O(\log\log n)$
指数搜索扩展窗口,然后二分搜索已排序$O(\log n)$

线性搜索

简单,但对于大型输入较慢。

python
def linear_search(a, x):
    for i, v in enumerate(a):
        if v == x:
            return i
    return -1

复杂度: $$T(n) = O(n)$$

二分搜索

在已排序列表上快速。

python
def binary_search(a, x):
    lo, hi = 0, len(a) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if a[mid] == x:
            return mid
        elif a[mid] < x:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

复杂度: $$T(n) = T(n/2) + 1 \Rightarrow O(\log n)$$

二分搜索变体

变体目标返回值
下界第一个满足 $a[i] \ge x$ 的索引第一个 ≥ x 的位置
上界第一个满足 $a[i] > x$ 的索引第一个 > x 的位置
计数范围upper_bound - lower_bound排序数组中 $x$ 的个数

常见的二分搜索陷阱

问题修复方法
无限循环正确更新边界
差一错误仔细检查中间值的包含关系
不适用于未排序数据排序或使用基于哈希的搜索
溢出(C/C++)mid = lo + (hi - lo) / 2

指数搜索

用于无界或大型已排序列表。

  1. 检查位置 $1, 2, 4, 8, ...$ 直到 $a[i] \ge x$
  2. 在最后找到的区间内进行二分搜索

复杂度: $$O(\log n)$$

选择问题

查找第 $k$ 个最小或最大元素。

任务示例用例算法复杂度
最小值 / 最大值最小 / 最大元素线性扫描$O(n)$
第 k 小元素顺序统计量快速选择平均 $O(n)$
中位数中间元素快速选择平均 $O(n)$
前 k 个元素部分排序堆 / 分区$O(n\log k)$
中位数的中位数最坏情况线性选择确定性算法$O(n)$

微型代码:快速选择(第 k 小元素)

python
import random

def quickselect(a, k):
    if len(a) == 1:
        return a[0]
    pivot = random.choice(a)
    left  = [x for x in a if x < pivot]
    mid   = [x for x in a if x == pivot]
    right = [x for x in a if x > pivot]

    if k < len(left):
        return quickselect(left, k)
    elif k < len(left) + len(mid):
        return pivot
    else:
        return quickselect(right, k - len(left) - len(mid))

复杂度: 平均 $O(n)$,最坏 $O(n^2)$

微型代码:下界

python
def lower_bound(a, x):
    lo, hi = 0, len(a)
    while lo < hi:
        mid = (lo + hi) // 2
        if a[mid] < x:
            lo = mid + 1
        else:
            hi = mid
    return lo

基于哈希的搜索

当顺序无关紧要时,哈希提供接近常数的查找时间。

操作平均最坏
插入$O(1)$$O(n)$
搜索$O(1)$$O(n)$
删除$O(1)$$O(n)$

最适合大型、未排序的集合。

总结表

场景推荐方法复杂度
小型数组线性搜索$O(n)$
大型、已排序数组二分搜索$O(\log n)$
无界范围指数搜索$O(\log n)$
需要第 k 小元素快速选择平均 $O(n)$
多次查找哈希表平均 $O(1)$

快速提示

  • 在应用二分搜索之前,始终检查数据是否已排序。
  • 当你只需要第 k 个元素,而不是完全排序时,快速选择非常有用。
  • 对未排序数据使用哈希映射进行快速查找。

第 5 页:核心数据结构

数据结构组织数据以实现高效的访问和修改。 选择合适的数据结构常常能使算法变得简单而快速。

数组和列表

结构访问查找尾部插入中间插入删除备注
静态数组$O(1)$$O(n)$不支持$O(n)$$O(n)$固定大小
动态数组$O(1)$$O(n)$均摊 $O(1)$$O(n)$$O(n)$自动调整大小
单向链表$O(n)$$O(n)$头部 $O(1)$已知节点时 $O(1)$已知节点时 $O(1)$顺序访问
双向链表$O(n)$$O(n)$头部/尾部 $O(1)$已知节点时 $O(1)$已知节点时 $O(1)$双向遍历
  • 单向链表:仅有 next 指针
  • 双向链表:有 next 和 prev 指针
  • 动态数组使用倍增策略来增加容量

微型代码:动态数组扩容(类 Python)

python
def resize(arr, new_cap):
    new = [None] * new_cap
    for i in range(len(arr)):
        new[i] = arr[i]
    return new

容量倍增策略使得尾部追加操作保持均摊 $O(1)$ 复杂度。

栈和队列

结构入栈/入队出栈/出队查看顶部/队首备注
栈 (LIFO)$O(1)$$O(1)$$O(1)$撤销操作,递归
队列 (FIFO)$O(1)$$O(1)$$O(1)$调度,广度优先搜索
双端队列$O(1)$$O(1)$$O(1)$可在两端插入/删除

微型代码:栈

python
stack = []
stack.append(x)   # 入栈
x = stack.pop()   # 出栈

微型代码:队列

python
from collections import deque

q = deque()
q.append(x)   # 入队
x = q.popleft()  # 出队

优先队列(堆)

存储元素,使得最小(或最大)的元素始终位于顶部。

操作复杂度
插入$O(\log n)$
提取最小值$O(\log n)$
查看最小值$O(1)$
建堆$O(n)$

微型代码:

python
import heapq
heap = []
heapq.heappush(heap, value)
x = heapq.heappop(heap)

堆用于 Dijkstra(迪杰斯特拉)算法、Prim(普里姆)算法和调度问题。

哈希表

操作平均情况最坏情况备注
插入$O(1)$$O(n)$哈希冲突会增加开销
查找$O(1)$$O(n)$良好的哈希函数和低负载因子有帮助
删除$O(1)$$O(n)$通常使用开放寻址或链地址法

核心思想:

  • 使用哈希函数计算索引:index = hash(key) % capacity
  • 通过链地址法或开放寻址法解决冲突

微型代码:哈希映射(简化版)

python
table = [[] for _ in range(8)]
def put(key, value):
    i = hash(key) % len(table)
    for kv in table[i]:
        if kv[0] == key:
            kv[1] = value
            return
    table[i].append([key, value])

集合

基于哈希的唯一元素集合。

操作平均复杂度
添加$O(1)$
查找$O(1)$
删除$O(1)$

用于成员检查和去重。

并查集(不相交集)

跟踪连通分量。 两个主要操作:

  • find(x):获取 x 的代表元素
  • union(a, b):合并 a 和 b 所在的集合

配合路径压缩和按秩合并 → 接近 $O(1)$。

微型代码:

python
class DSU:
    def __init__(self, n):
        self.p = list(range(n))
        self.r = [0]*n
    def find(self, x):
        if self.p[x] != x:
            self.p[x] = self.find(self.p[x])
        return self.p[x]
    def union(self, a, b):
        ra, rb = self.find(a), self.find(b)
        if ra == rb: return
        if self.r[ra] < self.r[rb]: ra, rb = rb, ra
        self.p[rb] = ra
        if self.r[ra] == self.r[rb]:
            self.r[ra] += 1

总结表格

类别结构使用场景
序列数组,列表有序数据
LIFO/FIFO栈,队列递归,调度
优先级最佳优先选择,优先队列问题
基于哈希哈希表,集合快速查找,唯一性
连通性并查集图连通分量,聚类

快速提示

  • 当随机访问重要时,选择数组。
  • 当频繁插入/删除时,选择链表。
  • 为控制流选择栈或队列。
  • 为优先级选择堆。
  • 为常量时间查找选择哈希表。
  • 为不相交集或图合并选择并查集。

第 6 页:图算法速查

图用于建模对象之间的连接关系。 它们无处不在:地图、网络、依赖关系和系统中。 本页为您提供了常见图算法的简明概览。

图基础

图由顶点(节点)和边(连接)组成。

类型描述
无向图边是双向的
有向图边具有方向
加权图边带有成本或距离
无权图所有边的成本为 1

表示方法

表示方法空间复杂度最适合备注
邻接表$O(V+E)$稀疏图实践中常用
邻接矩阵$O(V^2)$稠密图常数时间查找边
边列表$O(E)$基于边的算法易于遍历边

邻接表示例(Python):

python
graph = {
    0: [(1, 2), (2, 5)],
    1: [(2, 1)],
    2: []
}

每个元组 (neighbor, weight) 代表一条边。

遍历

广度优先搜索(BFS)

逐层访问(适用于无权图中的最短路径)。

python
from collections import deque
def bfs(adj, s):
    dist = {s: 0}
    q = deque([s])
    while q:
        u = q.popleft()
        for v in adj[u]:
            if v not in dist:
                dist[v] = dist[u] + 1
                q.append(v)
    return dist

复杂度:$O(V+E)$

深度优先搜索(DFS)

在回溯之前深入探索。

python
def dfs(adj, u, visited):
    visited.add(u)
    for v in adj[u]:
        if v not in visited:
            dfs(adj, v, visited)

复杂度:$O(V+E)$

最短路径算法

算法适用图类型允许负权边?复杂度备注
BFS无权图$O(V+E)$最短跳数
Dijkstra加权图(非负权)$O((V+E)\log V)$使用优先队列
Bellman-Ford加权图$O(VE)$可检测负权环
Floyd-Warshall所有节点对$O(V^3)$动态规划方法

精简代码:Dijkstra 算法

python
import heapq

def dijkstra(adj, s):
    INF = 1018
    dist = [INF] * len(adj)
    dist[s] = 0
    pq = [(0, s)]
    while pq:
        d, u = heapq.heappop(pq)
        if d != dist[u]: 
            continue
        for v, w in adj[u]:
            nd = d + w
            if nd < dist[v]:
                dist[v] = nd
                heapq.heappush(pq, (nd, v))
    return dist

拓扑排序(仅适用于有向无环图)

对节点排序,使得每条边 $(u,v)$ 都从较早的节点指向较晚的节点。

方法思路复杂度
基于 DFS后序栈反转$O(V+E)$
Kahn 算法移除入度为 0 的节点$O(V+E)$

最小生成树(MST)

以最小的总权重连接所有节点。

算法思路复杂度备注
Kruskal对边排序,使用并查集$O(E\log E)$与边列表配合良好
Prim使用优先队列生长树$O(E\log V)$可从任意顶点开始

精简代码:Kruskal MST

python
def kruskal(edges, n):
    parent = list(range(n))
    def find(x):
        if parent[x] != x:
            parent[x] = find(parent[x])
        return parent[x]
    res = 0
    for w, u, v in sorted(edges):
        ru, rv = find(u), find(v)
        if ru != rv:
            res += w
            parent[rv] = ru
    return res

强连通分量(SCC)

子集中的每个节点都可以到达其他所有节点。 使用 Kosaraju 或 Tarjan 算法,两者复杂度均为 $O(V+E)$。

环检测

图类型方法备注
无向图带父节点信息的 DFS访问到非父节点的已访问边
有向图带颜色/状态的 DFS发现回边即存在环

总结表

任务算法复杂度备注
访问所有节点DFS / BFS$O(V+E)$遍历
最短路径(无权图)BFS$O(V+E)$计算边数
最短路径(加权图)Dijkstra$O(E\log V)$不允许负权
允许负权边Bellman-Ford$O(VE)$可检测负权环
所有节点对最短路径Floyd-Warshall$O(V^3)$动态规划矩阵
最小生成树Kruskal / Prim$O(E\log V)$最小连接成本
有向无环图排序拓扑排序$O(V+E)$仅适用于有向无环图

快速提示

  • 在无权图中寻找最短路径时使用 BFS。
  • 如果权重非负,使用 Dijkstra。
  • 在 Kruskal MST 中使用并查集。
  • 解决依赖关系时使用拓扑排序。
  • 在使用 Dijkstra 之前,务必检查是否存在负权边。

第 7 页:动态规划快速使用指南

动态规划(DP)的核心思想是将大问题分解为重叠的子问题,并重用子问题的解。本页旨在帮助你快速识别动态规划的模式。

何时使用动态规划

通常,如果问题具备以下特征,就可以应用动态规划:

特征含义
最优子结构最优解由子问题的最优解构成
重叠子问题相同的子结果会重复出现
决策 + 递推关系可以定义状态之间的转移关系

动态规划的实现方式

方式描述示例
自顶向下(记忆化)递归 + 缓存结果带记忆化的斐波那契数列
自底向上(表格法)迭代填充表格背包问题的表格解法
空间优化复用前一行/前一个状态滚动数组

斐波那契数列示例

递推关系: $$F(n)=F(n-1)+F(n-2),\quad F(0)=0,F(1)=1$$

自顶向下(记忆化)
python
def fib(n, memo={}):
    if n <= 1:
        return n
    if n not in memo:
        memo[n] = fib(n-1, memo) + fib(n-2, memo)
    return memo[n]
自底向上(表格法)
python
def fib(n):
    dp = [0, 1]
    for i in range(2, n + 1):
        dp.append(dp[i-1] + dp[i-2])
    return dp[n]

解决动态规划问题的步骤

  1. 定义状态 示例:$dp[i]$ = 前 $i$ 个物品的最优解
  2. 定义状态转移方程 示例:$dp[i]=\max(dp[i-1], value[i]+dp[i-weight[i]])$
  3. 设置基本情况 示例:$dp[0]=0$
  4. 选择计算顺序 自底向上或自顶向下
  5. 返回答案 通常是 $dp[n]$ 或 $dp[target]$

常见的动态规划类别

类别示例问题状态形式
序列最长递增子序列,最长公共子序列,编辑距离$dp[i][j]$ 基于前缀
子集背包问题,子集和问题$dp[i][w]$ 基于容量
划分回文划分,等和子集$dp[i]$ 基于切割点
网格最小路径和,不同路径$dp[i][j]$ 基于单元格
计数零钱兑换(组合数),爬楼梯从子问题累加方案数
区间矩阵链乘法,戳气球$dp[i][j]$ 基于区间子问题
状态压缩旅行商问题,任务分配$dp[mask][i]$ 基于子集状态
数位满足约束的数字计数$dp[pos][tight][sum]$ 基于数位
树形换根动态规划,子树动态规划$dp[u]$ 基于子节点

经典问题

问题状态定义状态转移方程
爬楼梯$dp[i]=$ 到达第 i 级台阶的方法数$dp[i]=dp[i-1]+dp[i-2]$
零钱兑换(组合数)$dp[x]=$ 组成金额 x 的方法数$dp[x]+=dp[x-coin]$
0/1 背包$dp[w]=$ 重量不超过 w 的最大价值$dp[w]=\max(dp[w],dp[w-w_i]+v_i)$
最长递增子序列$dp[i]=$ 以 i 结尾的最长递增子序列长度若 $a[j]<a[i]$,则 $dp[i]=dp[j]+1$
编辑距离$dp[i][j]=$ 编辑代价min(插入,删除,替换)
矩阵链乘法$dp[i][j]=$ 子链相乘的最小代价$dp[i][j]=\min_k(dp[i][k]+dp[k+1][j])$

精简代码:0/1 背包(一维空间优化)

python
def knapsack(weights, values, W):
    dp = [0]*(W+1)
    for i in range(len(weights)):
        for w in range(W, weights[i]-1, -1):
            dp[w] = max(dp[w], dp[w-weights[i]] + values[i])
    return dp[W]

序列对齐示例

编辑距离递推关系:

$$ dp[i][j] = \begin{cases} dp[i-1][j-1], & \text{if } s[i] = t[j],\ 1 + \min(dp[i-1][j],\ dp[i][j-1],\ dp[i-1][j-1]), & \text{otherwise.} \end{cases} $$

优化技巧

技巧适用场景示例
空间优化二维状态可复用为一维背包问题,最长公共子序列
前缀/后缀预处理区间聚合查询区间和/最小值查询
分治动态规划决策具有单调性矩阵链乘法
凸包优化转移方程为线性函数取最小值基于直线的动态规划
位集动态规划状态为大规模布尔集合子集和问题的优化

调试技巧

  • 打印部分 dp 数组以观察计算过程。
  • 仔细检查基本情况。
  • 确保循环顺序符合状态转移的依赖关系。
  • 在编码之前,务必确认递推关系是正确的。

第 8 页:算法快速数学指南

数学为算法推理奠定了基础。 本页汇集了每个程序员都应该了解的基本公式和方法。

数论基础

主题描述公式 / 思路
最大公约数(欧几里得算法)最大公约数$\gcd(a, b) = \gcd(b, a \bmod b)$
扩展欧几里得算法求解 $ax+by=gcd(a,b)$回溯系数
最小公倍数最小公倍数$lcm(a,b)=\frac{a\cdot b}{gcd(a,b)}$
模加法模 M 下的加法$(a+b)\bmod M$
模乘法模 M 下的乘法$(a\cdot b)\bmod M$
模逆元$a^{-1}\bmod M$若 M 为质数,则 $a^{M-2}\bmod M$
模幂运算快速幂运算平方乘算法
中国剩余定理合并同余式求解方程组 $x\equiv a_i\pmod{m_i}$

微型代码(模幂运算):

python
def modpow(a, n, M):
    res = 1
    while n:
        if n & 1:
            res = res * a % M
        a = a * a % M
        n >>= 1
    return res

素数与因数分解

算法用例复杂度备注
试除法小 n$O(\sqrt{n})$简单
埃拉托斯特尼筛法生成素数$O(n\log\log n)$经典素数筛法
米勒-拉宾算法概率性素数测试$O(k\log^3 n)$对大 n 快速
Pollard Rho 算法分解合数$O(n^{1/4})$随机化算法
Atkin 筛法更快的变体$O(n)$实现复杂

组合数学

公式描述
$n! = n\cdot(n-1)\cdots1$阶乘
$\binom{n}{k}=\dfrac{n!}{k!(n-k)!}$组合数
$P(n,k)=\dfrac{n!}{(n-k)!}$排列数
帕斯卡法则:$\binom{n}{k}=\binom{n-1}{k}+\binom{n-1}{k-1}$构建帕斯卡三角形
卡特兰数:$C_n=\dfrac{1}{n+1}\binom{2n}{n}$括号计数

微型代码(使用阶乘和逆元计算 nCr 模 M):

python
def nCr(n, r, fact, inv):
    return fact[n]*inv[r]%M*inv[n-r]%M

概率基础

概念公式或思路
概率$P(A)=\frac{\text{有利情况}}{\text{总情况}}$
补集$P(\bar{A})=1-P(A)$
并集$P(A\cup B)=P(A)+P(B)-P(A\cap B)$
条件概率$P(A \mid B)=\frac{P(A\cap B)}{P(B)}$
贝叶斯定理$P(A \mid B)=\frac{P(B \mid A)P(A)}{P(B)}$
期望值$E[X]=\sum x_iP(x_i)$
方差$Var(X)=E[X^2]-E[X]^2$

线性代数核心

运算公式 / 方法复杂度
高斯消元法求解 $Ax=b$$O(n^3)$
行列式主元的乘积$O(n^3)$
矩阵乘法$(AB)_{ij}=\sum_k A_{ik}B_{kj}$$O(n^3)$
转置$A^T_{ij}=A_{ji}$$O(n^2)$
LU 分解$A=LU$(下三角,上三角)$O(n^3)$
Cholesky 分解$A=LL^T$(对称正定)$O(n^3)$
幂法主特征值估计迭代

微型代码(高斯消元法框架):

python
for i in range(n):
    pivot = a[i][i]
    for j in range(i, n+1):
        a[i][j] /= pivot
    for k in range(n):
        if k != i:
            ratio = a[k][i]
            for j in range(i, n+1):
                a[k][j] -= ratio*a[i][j]

快速变换

变换用例复杂度备注
快速傅里叶变换多项式卷积$O(n\log n)$复数
数论变换模卷积$O(n\log n)$素数模数
快速沃尔什变换(异或)基于异或的卷积$O(n\log n)$子集动态规划

FFT 公式:

$$ X_k = \sum_{n=0}^{N-1} x_n e^{-2\pi i kn/N} $$

数值方法

方法目的公式或思路
二分法求根中点二分直到 $f(x)=0$
牛顿-拉夫森法快速收敛$x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}$
割线法近似导数$x_{n+1}=x_n-f(x_n)\frac{x_n-x_{n-1}}{f(x_n)-f(x_{n-1})}$
辛普森法则积分$\int_a^bf(x)dx\approx\frac{h}{3}(f(a)+4f(m)+f(b))$

优化与微积分

概念公式 / 思路
导数$f'(x)=\lim_{h\to0}\frac{f(x+h)-f(x)}{h}$
梯度下降$x_{k+1}=x_k-\eta\nabla f(x_k)$
拉格朗日乘数法$\nabla f=\lambda\nabla g$
凸函数$f(\lambda x+(1-\lambda)y)\le\lambda f(x)+(1-\lambda)f(y)$

微型代码(梯度下降):

python
x = x0
for _ in range(1000):
    grad = df(x)
    x -= lr * grad

代数技巧

主题公式 / 用途
幂运算通过平方乘算法计算 $a^n$
多项式求导$(ax^n)' = n\cdot a x^{n-1}$
积分$\int x^n dx = \frac{x^{n+1}}{n+1}+C$
莫比乌斯反演$f(n)=\sum_{d \mid n}g(d)\implies g(n)=\sum_{d \mid n}\mu(d)\cdot f(n/d)$

快速参考表

领域必须掌握的算法
数论最大公约数,模幂运算,中国剩余定理
组合数学帕斯卡法则,阶乘,卡特兰数
概率贝叶斯定理,期望值
线性代数高斯消元法
变换快速傅里叶变换,数论变换
优化梯度下降

第 9 页:字符串与文本算法速查

字符串是用于文本搜索、匹配和转换的字符序列。 本页提供了经典和现代字符串技术的快速参考。

字符串基础

概念描述示例
字母表符号的集合{a, b, c}
字符串长度字符的数量"hello" → 5
子串字符串的连续部分"ell""hello"
子序列有序子集(不一定连续)"hlo" 来自 "hello"
前缀 / 后缀字符串的起始 / 结尾部分"he", "lo"

索引:大多数算法使用从 0 开始的索引。

字符串搜索概述

算法复杂度描述
朴素搜索$O(nm)$检查所有位置
KMP$O(n+m)$前缀-后缀跳转表
Z 算法$O(n+m)$预计算匹配长度
Rabin–Karp$O(n+m)$ 平均滚动哈希检查
Boyer–Moore$O(n/m)$ 平均反向扫描,跳过不匹配

KMP 前缀函数

为模式计算前缀-后缀匹配。

步骤含义
$pi[i]$对于 $pattern[0:i]$,同时也是其后缀的最长真前缀的长度

微型代码:

python
def prefix_function(p):
    pi = [0]*len(p)
    j = 0
    for i in range(1, len(p)):
        while j > 0 and p[i] != p[j]:
            j = pi[j-1]
        if p[i] == p[j]:
            j += 1
        pi[i] = j
    return pi

搜索时使用 pi 来跳过不匹配。

Z 算法

计算从位置 i 开始的子串与模式前缀匹配的最长长度。

步骤含义
$Z[i]$从 i 开始的最长与模式前缀匹配的子串的长度

使用 $S = pattern + '$' + text$ 来查找模式出现的位置。

Rabin–Karp 滚动哈希

思路计算文本窗口的哈希值,滑动窗口,进行比较

哈希函数: $$ h(s) = (s_0p^{n-1} + s_1p^{n-2} + \dots + s_{n-1}) \bmod M $$

滑动一个字符时高效地更新哈希值。

微型代码:

python
def rolling_hash(s, base=257, mod=109+7):
    h = 0
    for ch in s:
        h = (h*base + ord(ch)) % mod
    return h

高级模式匹配

算法使用场景复杂度
Boyer–Moore大字母表$O(n/m)$ 平均
Sunday最后字符移位启发式$O(n)$ 平均
Bitap近似匹配$O(nm/w)$
Aho–Corasick多模式搜索$O(n+z)$

Aho–Corasick 自动机

从模式构建字典树并计算失败链接。

步骤描述
构建字典树添加所有模式
失败链接回退到下一个前缀
输出链接记录模式匹配

微型代码框架:

python
from collections import deque

def build_ac(patterns):
    trie = [{}]
    fail = [0]
    for pat in patterns:
        node = 0
        for c in pat:
            node = trie[node].setdefault(c, len(trie))
            if node == len(trie):
                trie.append({})
                fail.append(0)
    # 计算失败链接
    q = deque()
    for c in trie[0]:
        q.append(trie[0][c])
    while q:
        u = q.popleft()
        for c, v in trie[u].items():
            f = fail[u]
            while f and c not in trie[f]:
                f = fail[f]
            fail[v] = trie[f].get(c, 0)
            q.append(v)
    return trie, fail

后缀结构

结构用途构建时间
后缀数组后缀索引的排序列表$O(n\log n)$
LCP 数组后缀的最长公共前缀$O(n)$
后缀树后缀的字典树$O(n)$ (Ukkonen)
后缀自动机子串的最小确定性有限自动机$O(n)$

后缀数组倍增法:

  • 对长度为 $2^k$ 的子串进行排名
  • 使用排名对进行排序和合并

通过 Kasai 算法计算 LCP: $$ LCP[i]=\text{后缀 } S[SA[i]:] \text{ 和 } S[SA[i-1]:] \text{ 的公共前缀长度} $$

回文检测

算法描述复杂度
Manacher 算法最长回文子串$O(n)$
动态规划表检查子串是否为回文$O(n^2)$
中心扩展围绕中心扩展$O(n^2)$

Manacher 算法核心:

  • 使用分隔符(#)进行转换
  • 跟踪每个中心周围回文的半径

编辑距离系列

算法描述复杂度
Levenshtein 距离插入/删除/替换$O(nm)$
Damerau–Levenshtein增加相邻字符交换$O(nm)$
Hirschberg空间优化的 LCS$O(nm)$ 时间,$O(n)$ 空间

递推关系: $$ dp[i][j]=\min \begin{cases} dp[i-1][j]+1 \ dp[i][j-1]+1 \ dp[i-1][j-1]+(s_i\neq t_j) \end{cases} $$

压缩技术

算法类型思路
Huffman 编码前缀码为高频字符分配更短的码字
算术编码范围编码分数区间表示
LZ77 / LZ78基于字典重用先前的子串
BWT + MTF + RLE块排序在编码前将相似字符分组

Huffman 原理: 为频率更高的符号分配更短的比特串。

哈希与校验和

算法使用场景备注
CRC32错误检测简单的多项式取模
MD5哈希(旧版)不安全
SHA-256安全哈希加密用途
滚动哈希子串比较用于 Rabin–Karp

快速参考

任务算法复杂度
单模式搜索KMP / Z$O(n+m)$
多模式搜索Aho–Corasick$O(n+z)$
近似搜索Bitap / Wu–Manber$O(kn)$
子串查询后缀数组 + LCP$O(\log n)$
回文Manacher$O(n)$
压缩Huffman / LZ77可变
编辑距离动态规划表$O(nm)$

第 10 页:几何、图形与空间算法速查

几何帮助我们解决关于形状、距离和空间关系的问题。 本页通过简单的公式和示例总结了核心的计算几何技术。

坐标基础

概念描述公式 / 示例
点距离$(x_1,y_1)$ 和 $(x_2,y_2)$ 之间的距离$d=\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}$
中点两点之间$(\frac{x_1+x_2}{2}, \frac{y_1+y_2}{2})$
点积角度与投影$\vec{a}\cdot\vec{b}= \Vert\vec{a}\Vert \Vert\vec{b}\Vert \cos\theta$
叉积 (2D)有向面积,方向判定$a\times b = a_xb_y - a_yb_x$
方向测试逆时针、顺时针、共线检查$\text{sign}(a\times b)$

微型代码 (方向测试):

python
def orient(a, b, c):
    val = (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0])
    return 0 if val == 0 else (1 if val > 0 else -1)

凸包

寻找包含所有点的最小凸多边形。

算法复杂度备注
Graham Scan$O(n\log n)$按角度排序,使用栈
Andrew's Monotone$O(n\log n)$按 x 排序,构建上/下包
Jarvis March$O(nh)$包裹法,h = 凸包大小
Chan's Algorithm$O(n\log h)$输出敏感的凸包算法

步骤:

  1. 对点排序
  2. 构建下凸包
  3. 构建上凸包
  4. 连接

最近点对

分治法。

步骤描述
按 x 分割将点分成两半
递归与合并跟踪跨越条带的最小距离

复杂度: $O(n\log n)$

公式: $$ d(p,q)=\sqrt{(x_p-x_q)^2+(y_p-y_q)^2} $$

线段相交

两条线段 $(p_1,p_2)$ 和 $(q_1,q_2)$ 相交的条件是:

  1. 方向测试结果不同
  2. 如果共线,则线段在直线上有重叠

微型代码:

python
def intersect(p1, p2, q1, q2):
    o1 = orient(p1, p2, q1)
    o2 = orient(p1, p2, q2)
    o3 = orient(q1, q2, p1)
    o4 = orient(q1, q2, p2)
    return o1 != o2 and o3 != o4

多边形面积 (鞋带公式)

对于按顺序排列的顶点 $(x_i, y_i)$:

$$ A=\frac{1}{2}\left|\sum_{i=0}^{n-1}(x_iy_{i+1}-x_{i+1}y_i)\right| $$

微型代码:

python
def area(poly):
    s = 0
    n = len(poly)
    for i in range(n):
        x1, y1 = poly[i]
        x2, y2 = poly[(i+1)%n]
        s += x1*y2 - x2*y1
    return abs(s)/2

点是否在多边形内

方法思路复杂度
射线法计算边交叉次数$O(n)$
环绕数法跟踪有向旋转次数$O(n)$
凸多边形测试检查所有方向$O(n)$

射线法: 交叉次数为奇数 → 在内部。

旋转卡尺

用于:

  • 多边形直径 (最远点对)
  • 最小包围矩形
  • 宽度与对跖点对

思路: 使用切线围绕凸包扫描。 复杂度: 构建凸包后为 $O(n)$。

扫描线技术

问题方法复杂度
最近点对按 y 维护活动集$O(n\log n)$
线段相交基于事件的扫描$O((n+k)\log n)$
矩形并集面积垂直边事件$O(n\log n)$
天际线问题按高度合并$O(n\log n)$

使用平衡树或优先队列维护活动集。

圆几何

概念公式
方程$(x-x_c)^2+(y-y_c)^2=r^2$
切线长度$\sqrt{d^2-r^2}$
两圆相交基于距离的几何

空间数据结构

结构用途备注
KD-Tree最近邻搜索轴对齐分割
R-Tree范围查询包围盒层次结构
Quadtree2D 递归细分图形学,碰撞检测
Octree3D 扩展体积划分
BSP Tree平面分割渲染,碰撞

光栅化与图形学

算法目的备注
Bresenham 直线在整数网格上绘制直线无需浮点数
中点画圆圆的光栅化利用对称性
扫描线填充多边形填充算法对边排序,水平扫描
Z-Buffer隐藏面消除逐像素深度比较
Phong 着色平滑光照插值法向量

空间路径规划

算法描述备注
A*启发式最短路径$f(n)=g(n)+h(n)$
Theta*任意角度路径基于捷径
RRT / RRT*随机探索机器人规划
PRM概率路线图采样图
可见性图连接可见点几何规划

快速总结

任务算法复杂度
凸包Graham / Andrew$O(n\log n)$
最近点对分治法$O(n\log n)$
线段相交检测扫描线$O(n\log n)$
点是否在多边形内射线法$O(n)$
多边形面积鞋带公式$O(n)$
最近邻搜索KD-Tree$O(\log n)$
路径规划A*$O(E\log V)$

提示

  • 几何预处理时,总是先对点排序。
  • 使用叉积进行方向测试。
  • 尽可能使用整数运算以避免浮点误差。

第 11 页:系统、数据库与分布式算法速查

系统和数据库依赖于管理内存、并发、持久性和协调性的算法。本页概述了其中最重要的部分。

并发控制

确保多个事务或线程同时运行时保持正确性。

方法核心思想备注
两阶段锁 (2PL)先获取锁,提交后再释放保证可串行化
严格两阶段锁持有所有锁直到提交防止级联中止
保守两阶段锁执行前锁定所有所需资源无死锁但并行度较低
时间戳排序按时间戳排序可能中止较晚的事务
多版本并发控制 (MVCC)读取者获取快照用于 PostgreSQL, InnoDB
乐观并发控制 (OCC)提交时验证适用于低冲突工作负载

微型代码:时间戳排序

python
# 简化版
if write_ts[x] > txn_ts or read_ts[x] > txn_ts:
    abort()
else:
    write_ts[x] = txn_ts

每个对象跟踪其读时间戳和写时间戳。

死锁

事务间形成循环等待。

检测构建等待图,检测环
预防Wait-Die(老等少) / Wound-Wait(少中止)

检测复杂度:$O(V+E)$

微型代码(等待图环检测):

python
def has_cycle(graph):
    visited, stack = set(), set()
    def dfs(u):
        visited.add(u)
        stack.add(u)
        for v in graph[u]:
            if v not in visited and dfs(v): return True
            if v in stack: return True
        stack.remove(u)
        return False
    return any(dfs(u) for u in graph)

日志与恢复

技术描述备注
预写日志数据写入前先写日志确保持久性
ARIES分析、重做、撤销三阶段行业标准
检查点保存一致性快照加速恢复
影子分页写时复制更新简单但灵活性较差

崩溃后恢复步骤:

  1. 分析:找出活跃事务
  2. 重做:重新应用已提交的更改
  3. 撤销:回滚未提交的更改

索引

加速查找和范围查询。

索引类型描述备注
B-树 / B+树平衡多路树适合磁盘存储
哈希索引仅支持精确匹配不支持范围查询
GiST / R-树空间数据边界框层次结构
倒排索引文本搜索将词项映射到文档列表

B+树复杂度:$O(\log_B N)$ (B = 分支因子)

微型代码(索引中的二分查找):

python
def search(node, key):
    i = bisect_left(node.keys, key)
    if i < len(node.keys) and node.keys[i] == key:
        return node.values[i]
    if node.is_leaf:
        return None
    return search(node.children[i], key)

查询处理

步骤描述
解析构建抽象语法树
优化重排连接,选择索引
执行计划为每个操作符选择算法
执行评估迭代器或流水线

常见连接策略:

连接类型复杂度备注
嵌套循环连接$O(nm)$简单,慢
哈希连接$O(n+m)$构建 + 探测
排序合并连接$O(n\log n+m\log m)$输入已排序

缓存与替换策略

策略描述备注
LRU驱逐最近最少使用项简单,利用时间局部性
LFU驱逐最不经常使用项适用于稳定模式
ARC / LIRS自适应混合策略处理混合工作负载
随机随机驱逐简单,公平

微型代码(使用 OrderedDict 实现 LRU):

python
from collections import OrderedDict

class LRU:
    def __init__(self, cap):
        self.cap = cap
        self.cache = OrderedDict()
    def get(self, k):
        if k not in self.cache: return -1
        self.cache.move_to_end(k)
        return self.cache[k]
    def put(self, k, v):
        if k in self.cache: self.cache.move_to_end(k)
        self.cache[k] = v
        if len(self.cache) > self.cap: self.cache.popitem(last=False)

分布式系统核心

问题描述典型解决方案
共识跨节点就某个值达成一致Paxos, Raft
领导者选举选取协调者Bully, Raft
复制维护副本日志复制
分区拆分数据一致性哈希
成员关系检测节点Gossip 协议

Raft 共识算法(简化版)

阶段动作
选举节点投票,选举领导者
复制领导者追加日志条目
提交一旦多数节点确认

安全性:已提交的条目永不改变。 活性:故障时选举出新领导者。

微型代码草图:

python
if vote_request.term > term:
    term = vote_request.term
    voted_for = candidate

一致性哈希

平滑地将键分布到节点上。

步骤描述
将每个节点哈希到环上例如 hash(node_id)
哈希每个键找到顺时针方向的下一个节点
添加/删除节点仅移动附近键

用于:Dynamo, Cassandra, Memcached。

容错模式

模式描述示例
复制多个副本主备复制
检查点定期保存进度机器学习训练
心跳活性检测集群管理器
重试 + 退避处理瞬时故障API 调用
法定读写需要多数节点同意Cassandra

分布式协调

工具 / 协议描述示例用途
ZooKeeper集中式协调锁,配置管理
Raft分布式共识日志复制
Etcd基于 Raft 的键值存储集群元数据

总结表

主题算法 / 概念复杂度备注
2PL, MVCC, OCC可变事务隔离
死锁Wait-Die, 检测$O(V+E)$基于图的检查
恢复ARIES, WAL可变崩溃恢复
索引B+树, 哈希索引$O(\log N)$加速查询
连接哈希 / 排序合并可变查询优化
缓存LRU, LFU$O(1)$数据局部性
共识Raft, Paxos$O(n)$ 消息容错
分区一致性哈希$O(1)$ 平均可扩展性

快速提示

  • 在并发中始终确保可串行化。
  • 对于读密集型工作负载,使用 MVCC。
  • ARIES 通过 WAL 确保持久性。
  • 为了可扩展性,明智地进行分区和复制。
  • 共识对于共享状态的正确性是必需的。

第 12 页:AI、ML 与优化算法速查

本页汇集了驱动现代人工智能和机器学习系统的经典算法,涵盖从聚类和分类到基于梯度的学习和元启发式方法。

经典机器学习算法

类别算法核心思想复杂度
聚类k-Means分配到最近的质心,更新中心点$O(nkt)$
聚类k-Medoids (PAM)以代表性点作为中心点$O(k(n-k)^2)$
聚类高斯混合模型 (EM)通过概率进行软分配每次迭代 $O(nkd)$
分类朴素贝叶斯在特征独立的假设下应用贝叶斯规则$O(nd)$
分类逻辑回归线性 + sigmoid 激活函数$O(nd)$
分类SVM(线性)通过凸优化最大化间隔约 $O(nd)$
分类k-NN基于最近邻投票每次查询 $O(nd)$
决策树 (CART)通过不纯度递归分割$O(nd\log n)$
投影LDA / PCA寻找最大化方差或类间分离度的投影$O(d^3)$

微型代码:k-Means

python
import random, math

def kmeans(points, k, iters=100):
    # 随机选择 k 个点作为初始质心
    centroids = random.sample(points, k)
    for _ in range(iters):
        # 为每个质心创建分组
        groups = [[] for _ in range(k)]
        for p in points:
            # 找到距离最近的质心索引
            idx = min(range(k), key=lambda i: (p[0]-centroids[i][0])**2 + (p[1]-centroids[i][1])**2)
            groups[idx].append(p)
        new_centroids = []
        for g in groups:
            if g:
                # 计算新质心作为组内点的平均值
                x = sum(p[0] for p in g)/len(g)
                y = sum(p[1] for p in g)/len(g)
                new_centroids.append((x,y))
            else:
                # 如果分组为空,则随机选择一个点作为新质心
                new_centroids.append(random.choice(points))
        if centroids == new_centroids: break
        centroids = new_centroids
    return centroids

线性模型

模型公式损失函数
线性回归$\hat{y}=w^Tx+b$均方误差:$\frac{1}{n}\sum(y-\hat{y})^2$
逻辑回归$\hat{y}=\sigma(w^Tx+b)$交叉熵
岭回归线性 + $L_2$ 惩罚项$L=\text{MSE}+\lambda|w|^2$
Lasso 回归线性 + $L_1$ 惩罚项$L=\text{MSE}+\lambda|w|_1$

微型代码(线性回归的梯度下降):

python
def train(X, y, lr=0.01, epochs=1000):
    w = [0]*len(X[0])
    b = 0
    for _ in range(epochs):
        for i in range(len(y)):
            # 计算预测值
            y_pred = sum(w[j]*X[i][j] for j in range(len(w))) + b
            err = y_pred - y[i]
            # 更新权重和偏置
            for j in range(len(w)):
                w[j] -= lr * err * X[i][j]
            b -= lr * err
    return w, b

决策树与集成方法

算法描述备注
ID3 / C4.5 / CART基于信息增益或基尼指数进行分割递归的,可解释性强
随机森林Bagging + 决策树降低方差
梯度提升顺序拟合残差XGBoost, LightGBM, CatBoost
AdaBoost加权弱学习器对噪声敏感

不纯度度量:

  • 基尼指数:$1-\sum p_i^2$
  • 熵:$-\sum p_i\log_2p_i$

支持向量机 (SVM)

寻找最大间隔超平面。

目标函数: $$ \min_{w,b} \frac{1}{2}|w|^2 + C\sum\xi_i $$ 约束条件:$y_i(w^Tx_i+b)\ge1-\xi_i$

核技巧实现非线性分离: $$K(x_i,x_j)=\phi(x_i)\cdot\phi(x_j)$$

神经网络基础

组件描述
神经元$y=\sigma(w\cdot x+b)$
激活函数Sigmoid, ReLU, Tanh
损失函数均方误差,交叉熵
训练梯度下降 + 反向传播
优化器SGD, Adam, RMSProp

前向传播: $$a^{(l)} = \sigma(W^{(l)}a^{(l-1)}+b^{(l)})$$ 反向传播逐层计算梯度。

梯度下降变体

变体思想备注
批量每一步使用所有数据稳定但慢
随机每个样本更新一次有噪声,快
小批量分组更新常见做法
动量添加速度项收敛更快
Adam自适应矩估计最流行

更新规则: $$ w = w - \eta \cdot \frac{\partial L}{\partial w} $$

无监督学习

算法描述备注
PCA基于方差的投影特征分解
ICA独立成分分析信号分离
t-SNE保持局部结构仅用于可视化
自编码器神经网络重构模型降维

PCA 公式: 协方差矩阵 $C=\frac{1}{n}X^TX$,$C$ 的特征向量是主成分轴。

概率模型

模型描述备注
朴素贝叶斯独立性假设$P(yx)\propto P(y)\prod P(x_iy)$
隐马尔可夫模型序列隐状态Viterbi 算法解码
马尔可夫链转移概率$P(x_tx_{t-1})$
高斯混合模型软聚类EM 算法

优化与元启发式算法

算法类别备注
梯度下降凸优化目标函数可微
牛顿法二阶方法使用海森矩阵
模拟退火概率搜索逃离局部最小值
遗传算法进化算法基于种群的搜索
粒子群优化集体移动受群体行为启发
爬山算法贪心搜索局部优化

强化学习核心

概念描述示例
智能体学习者/决策者机器人,策略
环境提供状态和奖励游戏,模拟器
策略状态到动作的映射$\pi(s)=a$
价值函数期望回报$V(s)$, $Q(s,a)$

Q-Learning 更新: $$ Q(s,a)\leftarrow Q(s,a)+\alpha(r+\gamma\max_{a'}Q(s',a')-Q(s,a)) $$

微型代码:

python
Q[s][a] += alpha * (r + gamma * max(Q[s_next]) - Q[s][a])

AI 搜索算法

算法描述复杂度备注
BFS无权图最短路径$O(V+E)$层序搜索
DFS深度探索$O(V+E)$回溯
A* 搜索启发式搜索$O(E\log V)$$f(n)=g(n)+h(n)$
IDA*迭代加深 A*内存效率高若 $h$ 可采纳则最优
束搜索保留最好的 k 个状态近似NLP 解码

评估指标

任务指标公式 / 含义
分类准确率,精确率,召回率$\frac{TP}{TP+FP}$, $\frac{TP}{TP+FN}$
回归RMSE, MAE, $R^2$拟合度与误差大小
聚类轮廓系数内聚性与分离性
排序MAP, NDCG顺序敏感

混淆矩阵:

预测 +预测 -
实际 +TPFN
实际 -FPTN

总结

类别算法示例备注
聚类k-Means, GMM无监督分组
分类逻辑回归,SVM,决策树有监督标注
回归线性回归,岭回归,Lasso预测连续值
优化GD, Adam, 模拟退火最小化损失
概率贝叶斯,HMM,EM不确定性建模
强化学习Q-Learning, SARSA基于奖励的学习

基于 CC BY-NC-SA 4.0 许可协议发布