数据结构与算法 · 公式与知识点大全

约 45 分(选择题 24 + 综合题 21)。树、图、查找、排序是综合题主力。 橙色=易错 · 蓝色=概念 · 红色=必背
一、算法与复杂度

1. 时间 / 空间复杂度

T(n) = O(f(n)):存在 c,n₀ 使 0 ≤ T(n) ≤ c·f(n)(渐进上界)
常见数量级:O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)
只保留最高阶、去掉常数系数。空间复杂度同理(含递归栈深度)。最坏/平均/最好要分别看。

2. 主定理(分治递归)

T(n) = a·T(n/b) + f(n) (a≥1,b>1)
  • 情况1:f(n)=O(n^(log_b a − ε)) → T(n)=Θ(n^(log_b a))
  • 情况2:f(n)=Θ(n^(log_b a) · logᵏn) → T(n)=Θ(n^(log_b a)·log^(k+1)n)
  • 情况3:f(n)=Ω(n^(log_b a + ε)) 且满足正则 → T(n)=Θ(f(n))
典型:归并/快排(平均)→ a=b=2,f=O(n)→O(n log n);二分→O(log n);矩阵乘法 Strassen。
二、线性表

顺序表 vs 链表

顺序表链表(单)
存取随机 O(1)顺序 O(n)
插入/删除需移动 O(n)改指针 O(1)*
空间连续、易浪费节点+指针、灵活
*链表插入删除"改指针"是 O(1),但定位前驱仍需 O(n)。静态链表用数组模拟指针。

双指针常用结论

快慢指针判环、求中点;两指针距 d 则相遇需走 2d 步(快2慢1)
三、栈与队列

1. 共享栈 / 循环队列

共享栈:两栈从数组两端向中间增长,栈满条件 top1 + 1 == top2
循环队列(容量Max):队满 = (rear+1)%Max == front;队空 = rear == front
元素个数 = (rear − front + Max) % Max
📌 循环队列"牺牲一个单元"区分满空是标准考法;也可用 size 计数或 tag 标志区分。

2. 栈的应用

  • 表达式求值(中缀→后缀,运算符优先级+栈)
  • 括号匹配(遇左进栈、遇右弹栈比配)
  • 递归(系统栈保存现场)
  • 进制转换
⚠ 后缀表达式求值:遇数入栈,遇运算符弹两算一压回。中缀转后缀时,栈内优先级 ≥ 栈外才弹出。
四、串与 KMP 核心

1. 朴素匹配 vs KMP

朴素(BF):主串指针 i 回溯,最坏 O(n·m)
KMP:主串 i 不回溯,靠 next[] 移动模式串,O(n+m)

2. next / nextval 数组

next[j] = 模式串 P[0..j−1]最长相等真前后缀长度(失配时模式串右移位数)
next[0] = −1;next[j] = k(使 P[0..k−1]==P[j−k..j−1] 的最大 k)
nextval[j] = (P[j]==P[next[j]]) ? nextval[next[j]] : next[j] (再压缩)
📌 手算 next:对每个 j,找"前缀==后缀"的最大长度;next[j] 等于该长度值(j=0 特判 −1)。nextval 是 next 的优化版,避免连续相同字符重复比较。

3. 数组地址(行/列优先)

行优先:Loc(i,j) = base + (i·n + j)·w (m行n列,起始(i,j)=(0,0))
列优先:Loc(i,j) = base + (j·m + i)·w
三维/特殊矩阵(对称、三角、稀疏)按压缩存储规律换算一维下标。
五、树与二叉树 核心

1. 二叉树五大性质 必背

① 第 i 层最多 2ⁱ⁻¹ 个节点(i≥1)
② 深度为 k 的二叉树最多 2ᵏ − 1 个节点
③ 任意二叉树 n₀ = n₂ + 1(叶子=度为2节点+1)
④ 完全二叉树节点数 n,深度 ⌊log₂n⌋ + 1
⑤ 编号 i(从1):父⌊i/2⌋,左子2i,右子2i+1;从0编号则父⌊(i−1)/2⌋

2. 遍历

先序 DLR / 中序 LDR / 后序 LRD;层次遍历用队列
📌 中序+先序中序+后序 可唯一确定二叉树;先序+后序不能(仅能定根,不能确定左右)。

3. 线索二叉树

n节点二叉树有 n+1 个空指针 → 用作线索(指向前驱/后继)
ltag=0指向左孩、=1指向前驱;rtag=0指向右孩、=1指向后继。中序线索化可实现 O(1) 找前驱后继。

4. 哈夫曼树(最优二叉树)

WPL = Σ wᵢ · lᵢ (w权重,l路径长度);哈夫曼树使 WPL 最小
构造:每次取权最小的两棵合并,新权=二者之和,反复至单树。没有度为1的节点(n₂=n₀−1)。

5. 二叉排序树 BST / 平衡树 AVL

BST:左子树 < 根 < 右子树;查找/插入/删除平均 O(log n)(退化链表 O(n))
AVL:任意节点 |h_L − h_R| ≤ 1;失衡后 LL/LR/RL/RR 旋转调整
⚠ 平衡因子 = 左高 − 右高 ∈ {−1,0,1}。插入后从插入点向上更新,第一个失衡点为旋转根。

6. B 树 / B+ 树 必背

m阶B树:根[1, m−1]关键字;非根[⌈m/2⌉−1, m−1];孩子数 = 关键字数+1
高度(含叶) h:满足 ⌈m/2⌉^(h−1) ≤ n+1 ≤ m^h(n为关键字总数)
B+树:所有关键字在叶子层且链表相连,非叶只作索引;更适合范围查询与数据库索引。
六、图 核心

1. 图的基本概念

无向完全图边数 = n(n−1)/2;有向完全图 = n(n−1)
生成树边数 = n − 1;连通分量、强连通分量
邻接矩阵 O(n²)、适合稠密图;邻接表 O(n+e)、适合稀疏图。有向图入度/出度分别统计。

2. 最小生成树 MST

  • Prim:从一点起,每次加连接已选集的最小边;O(n²),适合稠密
  • Kruskal:边按权排序,按不形成环依次加入;O(e log e),适合稀疏

3. 最短路径

Dijkstra(单源,非负权):每轮选距离最小未访问点,松弛其邻边;O(n²)/O((n+e)log n)
Floyd(多源):DP, Dᵏ[i][j] = min(Dᵏ⁻¹[i][j], Dᵏ⁻¹[i][k]+Dᵏ⁻¹[k][j]);O(n³)
⚠ Dijkstra 不能处理负权边(会出错);有负权用 Bellman-Ford / SPFA。

4. 拓扑排序 / 关键路径

拓扑:每次删入度0点;有环则无法完成全部排序
关键路径:ve(最早)=max(前驱ve+权);vl(最晚)=min(后继vl−权)
活动最早 e=ve(弧尾),最晚 l=vl(弧头)−权;e == l 的活动为关键活动
📌 关键路径长度是工程最短总工期;缩短非关键活动不影响总工期。
七、查找 核心

1. 线性 / 折半查找

顺序查找 ASL(成功) ≈ (n+1)/2;折半查找判定树高 ⌈log₂(n+1)⌉
折半:low=0,high=n−1;mid=(low+high)/2;判定树是平衡二叉树
分块查找:块间有序+块内无序,ASL = 块间 + 块内。

2. 哈希(散列)查找 必背

装填因子 α = n / m(n记录数,m表长)
平均查找长度与 α 正相关:α越小冲突越少;开放定址法 ASL≈(1+1/(1−α))/2
冲突处理:开放定址(线性/二次/双重探测)、链地址法。构造:除留余数 H(key)=key%p(p取质数)。
⚠ 线性探测易"聚集";二次探测可缓解但不能探测所有位置;装填因子一般控在 0.7 左右。

3. B 树查找

B树查找:从根沿关键字区间下行,磁盘 I/O 次数 = 树高;适合外存
八、内部排序 核心

排序算法特性一览 必背

排序平均最好最坏空间稳定
直接插入O(n²)O(n)O(n²)O(1)稳定
冒泡O(n²)O(n)O(n²)O(1)稳定
简单选择O(n²)O(n²)O(n²)O(1)不稳定
希尔O(n^1.3)O(1)不稳定
快速O(n log n)O(n log n)O(n²)O(log n)不稳定
O(n log n)O(n log n)O(n log n)O(1)不稳定
归并O(n log n)O(n log n)O(n log n)O(n)稳定
基数O(d(n+r))O(n+r)稳定
📌 稳定排序:插入、冒泡、归并、基数。快排最坏发生在已有序且选端为枢轴,用"三数取中"优化。堆排序建堆 O(n)。

快排 partition 与堆

partition:选枢轴pivot,双指针划分数组 ≤pivot | pivot | ≥pivot;递归两侧
大顶堆:k[i] ≥ k[2i] 且 ≥ k[2i+1];小顶堆相反;下沉/上浮维护
外排序:归并排序 + 多路归并 + 败者树/置换-选择;减少磁盘 I/O 趟数。
九、算法设计思想

四大范式对比

方法核心典型
分治分解→解决→合并快排/归并/二分
动态规划重叠子问题+最优子结构,填表0-1背包/LCS/矩阵链
贪心每步局部最优(需证明)Prim/Kruskal/Dijkstra/哈夫曼
回溯/分支限界试探+剪枝(DFS/BFS)N皇后/装载/0-1背包
⚠ 动态规划与分治区别:DP 子问题重叠需记忆化;贪心不一定得全局最优,必须验证贪心选择性质。