数据结构期末复习笔记:从“迷雾”到“清晰”的完整地图

引言

数据结构的期末复习,最怕的不是题目难,而是知识点散落在各处------讲义里有定义、PPT里有伪代码、作业里有坑题、复习资料里有复杂度表。你需要的不是更多的材料,而是一份把零散知识串成体系的完整地图。

本文正是为此而生。它不是简单的知识点罗列,而是按照**"概念 → 操作 → 复杂度 → 易错点 → 考题方向"** 的逻辑,把所有考试范围内的内容重新组织,配合高频题型和解题模板,帮你做到考前心中有数,考场上手不抖

如果说平时学习是"种树",那么期末复习就是 "画地图"------把每棵树的位置、每棵树的特征、树与树之间的路径都标清楚,考试时无论从哪个方向出题,你都能快速定位到答案。


考试范围速览

章节 涵盖内容
树(上) 树的基本概念、二叉树、二叉树遍历、二叉搜索树(BST)
树(下) 堆(优先队列)、并查集
图(上) 图的定义与存储、DFS/BFS、拓扑排序
图(下) 最短路径(无权BFS/Dijkstra/Floyd)、最小生成树(Prim/Kruskal)
排序 插入、冒泡、希尔、堆排、归并、快排、表排序、复杂度与稳定性
散列表 散列函数、分离链接、开放定址法(线性探测/平方探测/双散列/Robin Hood/再散列)

明确排除:AVL树、2-3树、红黑树、Huffman编码、最大流、alpha-beta剪枝、活动选择、回溯、NP-complete、Master定理、Precision/Recall、栈和队列综合题。


第一章:树

1.1 基本概念(必背)

树是由 n≥0n≥0 个结点组成的有限集合。当 n=0n=0 时为空树。非空树有唯一根结点,其余结点可划分为若干互不相交的子树。

核心术语速查表

术语 含义
结点的度 结点的子树个数
树的度 所有结点度数的最大值
叶结点 度为0的结点
路径长度 路径上边的条数
树的深度 树中所有结点的最大层次(根为第1层)

三条必背公式

  • 一棵有 NN 个结点的树有 N−1N−1 条边

  • 总边数 = 所有结点度数之和

  • 对度为0、1、2、3的树:n0=n2+2n3+1n0​=n2​+2n3​+1(重要!高频)

1.2 二叉树

定义:每个结点最多有2个子树的树。

完全二叉树的性质(高频考点):

  • 第 ii 层最多有 2i−12i−1 个结点(根为第1层)

  • 前 hh 层最多有 2h−12h−1 个结点

  • 数组存储时(下标从1开始):

    • 父结点:i/2

    • 左孩子:2*i

    • 右孩子:2*i + 1

完全二叉树层数题秒杀公式

题型:根为第1层,第5层有8个叶结点,问结点最多多少?

思路:为使总结点最多,让另外8个结点都有2个孩子。

  • 前5层满:25−1=3125−1=31 个结点

  • 第6层最多增加:8×2=168×2=16 个结点

  • 最多结点数:31+16=4731+16=47

1.3 二叉树的四种遍历

遍历方式 访问顺序 递归写法
前序 根 → 左 → 右 visit(T); preorder(T->L); preorder(T->R);
中序 左 → 根 → 右 inorder(T->L); visit(T); inorder(T->R);
后序 左 → 右 → 根 postorder(T->L); postorder(T->R); visit(T);
层序 从上到下、从左到右 用队列BFS

唯一还原定理(高频判断题):

  • 前序 + 中序 → 唯一确定

  • 后序 + 中序 → 唯一确定

  • 前序 + 后序 → 通常不唯一

1.4 二叉搜索树(BST)

定义

  • 左子树所有键值 小于

  • 右子树所有键值 大于

  • 左右子树也都是BST

关键性质 :BST的中序遍历结果是递增序列(判断题高频考点)

核心操作复杂度

操作 时间复杂度
查找 O(h)O(h)
插入 O(h)O(h)
删除 O(h)O(h)
查最大 一直往右,O(h)O(h)
查最小 一直往左,O(h)O(h)

其中 hh 是树高。平衡时 h=O(log⁡N)h=O(logN),最坏(退化为链)时 h=O(N)h=O(N)。

BST删除三种情况

  1. 删除叶结点:父结点指针置空

  2. 删除度为1的结点:父结点指向其唯一孩子

  3. 删除度为2的结点:用左子树最大值右子树最小值替换,再删除替身结点

易错点

  • 插入顺序不同,BST形态可能完全不同

  • 递增序列插入普通BST会退化成链

  • BST ≠ 堆!BST保证左右子树整体大小关系,堆只保证父子大小关系


第二章:堆(优先队列)

2.1 堆的定义

堆 = 完全二叉树 + 堆序性

  • 最大堆:父结点 ≥ 子结点

  • 最小堆:父结点 ≤ 子结点

2.2 核心操作与复杂度

操作 做法 时间复杂度
插入 插到末尾,上滤 O(log⁡N)O(logN)
删除堆顶 取根,末尾元素放根,下滤 O(log⁡N)O(logN)
建堆 从最后一个非叶结点开始下滤 O(N)O(N)

高频判断点 (来自 ds_exam_prediction_manual.pdf):

  • 线性建堆的复杂度是 O(N)O(N),不是 O(Nlog⁡N)O(NlogN)

  • 逐个插入建堆才是 O(Nlog⁡N)O(NlogN)

2.3 下标关系(必背)

编号方式 左孩子 右孩子 父结点
从0开始 2*i + 1 2*i + 2 (i-1)/2
从1开始 2*i 2*i + 1 i/2

2.4 建堆并DeleteMin题型(高频)

题型:用线性算法把 [16, 28, 14, 13, 7, 6] 建成最小堆,再 DeleteMin。

常见判断(来自预测手册):

  • 7是根 → ✓

  • 13和14是兄弟 → ✓

  • 13是16的父结点 → ✓

  • 28是13的左孩子 → ✗(是右孩子)


第三章:并查集

3.1 核心表示法

用数组 S\[\] 表示:

  • S[x] < 0:x 是根,-S[x] 表示集合大小或树高

  • S[x] >= 0S[x] 是 x 的父结点

3.2 核心操作

cpp

复制代码
// 查找(带路径压缩)
int Find(int S[], int x) {
    if (S[x] < 0) return x;
    return S[x] = Find(S, S[x]);
}

// 按规模归并
void Union(int S[], int r1, int r2) {
    if (S[r1] < S[r2]) {   // r1 的规模更大(负数更小)
        S[r1] += S[r2];
        S[r2] = r1;
    } else {
        S[r2] += S[r1];
        S[r1] = r2;
    }
}

3.3 并查集题型秒杀

题型:S ={1, -4, 1, 1, -3, 4, 4, 8, -2},合并包含6和8的两个集合。

分析:

  • Find(6) = 4,S4 = -3 → 规模3

  • Find(8) = 8,S8 = -2 → 规模2

  • 按规模归并:小树(8)接到大树(4)上

  • 新根 = 4,新根值 = -3 + (-2) = -5

答案:4 and -5

3.4 复杂度

优化方式 树高 复杂度
朴素并查集 可能 O(N)O(N) 最坏 O(N)O(N)
按秩归并 O(log⁡N)O(logN) O(log⁡N)O(logN)
按秩归并 + 路径压缩 近似常数 均摊近似 O(1)O(1)

易错点

  • Union 前必须先 Find,合并的是,不是原元素

  • 路径压缩改变树形,但不改变集合划分

  • 负数比较:-10 表示比 -3 更大的集合(10 > 3)


第四章:图

4.1 图的基本概念

术语 含义
有向图 边有方向 <v, w>
无向图 边无方向 (v, w)
网络 边带权值
连通图 无向图中任意两点连通
强连通图 有向图中任意两点双向可达
路径 顶点序列,相邻顶点之间有边
回路 起点等于终点的路径
连通分量 无向图的极大连通子图
强连通分量 有向图的极大强连通子图

4.2 图的存储(常考判断)

存储方式 空间 查边 遍历邻接点 适用场景
邻接矩阵 O(V2)O(V2) O(1)O(1) O(V)O(V) 稠密图
邻接表 O(V+E)O(V+E) 较慢 O(deg⁡)O(deg) 稀疏图

高频判断点

  • 邻接表空间是否只与顶点数有关?------ ,还与边数有关(O(V+E)O(V+E))

  • 邻接矩阵适合稠密图,邻接表适合稀疏图

4.3 DFS / BFS

DFS:深度优先,类似树的前序遍历,递归或栈。一条路走到底再回溯。

BFS:广度优先,用队列。一层一层向外扩展。

复杂度

存储方式 DFS/BFS 复杂度
邻接表 O(V+E)O(V+E)
邻接矩阵 O(V2)O(V2)

连通分量:遍历所有顶点,遇到未访问顶点就启动一次 DFS/BFS,每启动一次得到一个连通分量。

BFS vs DFS 适用场景

  • BFS 可求无权图最短路

  • DFS 不保证最短,但适合连通性判断和回溯搜索

4.4 拓扑排序

定义:如果从 v 到 w 有路径,则 v 一定排在 w 之前。满足此条件的顶点序列称为拓扑序。

适用条件 :图必须是 DAG(有向无环图)

队列版算法(常考程序填空):

text

复制代码
1. 计算所有顶点入度
2. 所有入度为0的顶点入队
3. while (队列非空) {
     v = 出队; 输出 v; count++;
     for (v 的每个邻接点 w) {
         indegree[w]--;
         if (indegree[w] == 0) 入队(w);
     }
   }
4. if (count < V) 图中有回路

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

常考点

  • 拓扑排序能否检测有向图回路?------

  • 入度队列法如果使用堆栈而不是队列,得到的拓扑序可能不同,但都是合法的


第五章:最短路径

5.1 三种算法对比(必背)

问题 算法 适用条件 时间复杂度
无权单源 BFS 边权相同 O(V+E)O(V+E)
有权单源 Dijkstra 非负权 邻接矩阵 O(V2)O(V2),堆优化 O(Elog⁡V)O(ElogV)
多源 Floyd 任意两点 O(V3)O(V3)

5.2 Dijkstra 核心逻辑

text

复制代码
while (1) {
    V = 未收录顶点中 dist 最小者;
    if (不存在) break;
    collected[V] = true;
    for (V 的每个邻接点 W) {
        if (!collected[W]) {
            if (dist[V] + G[V][W] < dist[W]) {
                dist[W] = dist[V] + G[V][W];
                path[W] = V;
            }
        }
    }
}

程序填空关键代码

cpp

复制代码
if (!collected[w]) {
    if (dist[v] + G[v][w] < dist[w]) {
        dist[w] = dist[v] + G[v][w];
        path[w] = v;
    }
}

高频判断点

  • Dijkstra 按距离递增顺序确定最短路 → ✓

  • Dijkstra 不能处理负边 → ✓

  • "负边会导致 Dijkstra 无限循环" → ✗(它只是结果不正确,不会无限循环)

  • 所有边权相等时,BFS 可求最短路径 → ✓

5.3 Floyd 核心逻辑(程序填空必考)

cpp

复制代码
// 初始化 D[i][j] = G[i][j]
for (k = 0; k < N; k++)
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
            if (D[i][k] + D[k][j] < D[i][j])
                D[i][j] = D[i][k] + D[k][j];

口诀:最外层是"允许经过的中间点"

另一种写法(如果题目变量顺序不同):

cpp

复制代码
for (i = 0; i < N; i++)
    for (k = 0; k < N; k++)
        for (j = 0; j < N; j++)
            if (D[k][i] + D[i][j] < D[k][j])
                D[k][j] = D[k][i] + D[i][j];

第六章:最小生成树(MST)

6.1 MST 的性质(高频判断)

  • 是一棵树:无回路,有 V-1 条边

  • 是生成树:包含全部 V 个顶点

  • 边权和最小

  • 只对连通无向图存在

  • 如果图不连通,MST 不存在 → 判断"MST always exists"是错的

6.2 Prim vs Kruskal

算法 思路 选边方式 判环 适用
Prim 一棵小树长大 树到外部的最小边 无显式判环 稠密图
Kruskal 森林合并 全图剩余最小边 并查集 稀疏图

Prim 程序填空要点

  • 与 Dijkstra 很像,但 dist[W] 表示到当前树的最小边权,不是源点到 w 的路径长

  • 初始化:dist[s] = 0,其他为 INF

Kruskal 程序填空要点

  • 边按权重排序(或用最小堆)

  • 用并查集判断是否成环

  • 不成环就加入,成环就丢弃

  • 直到收录 V-1 条边

复杂度:Kruskal O(Elog⁡E)O(ElogE)


第七章:排序

7.1 七种排序复杂度总表(必背)

排序算法 最好 平均 最坏 稳定 额外空间
冒泡排序 O(N)O(N) O(N2)O(N2) O(N2)O(N2) O(1)O(1)
插入排序 O(N)O(N) O(N2)O(N2) O(N2)O(N2) O(1)O(1)
选择排序 O(N2)O(N2) O(N2)O(N2) O(N2)O(N2) O(1)O(1)
希尔排序 与增量有关 与增量有关 常见优于 O(N2)O(N2) O(1)O(1)
堆排序 O(Nlog⁡N)O(NlogN) O(Nlog⁡N)O(NlogN) O(Nlog⁡N)O(NlogN) O(1)O(1)
归并排序 O(Nlog⁡N)O(NlogN) O(Nlog⁡N)O(NlogN) O(Nlog⁡N)O(NlogN) O(N)O(N)
快速排序 O(Nlog⁡N)O(NlogN) O(Nlog⁡N)O(NlogN) O(N2)O(N2) O(log⁡N)O(logN)(递归栈)

7.2 稳定性判断口诀(来自 ds_final_review.pdf

插入适合基本有序,堆排原地但不稳定;

归并稳定但费空间,快排平均快但最坏平方。

稳定性速记:冒泡、插入、归并稳定;选择、希尔、堆排、快排不稳定。

7.3 逆序对(必考概念)

  • 如果 i<ji<j 且 Ai>AjAi>Aj,则 (i,j)(i,j) 是一对逆序对

  • 交换相邻元素一次只能消去 1 个逆序对

  • 插入排序复杂度可写成 O(N+I)O(N+I),其中 II 是逆序对数

  • 任何仅交换相邻元素的排序,平均时间复杂度下界为 Ω(N2)Ω(N2)

7.4 快速排序(高频过程题)

三数取中:取头、中、尾三个元素的中位数作为主元(pivot)

题型 :输入 [21, 85, 32, 49, 67, 1, 12],三数取中,问第一次 partition 后序列。

分析:

  • 头=21,中=49,尾=12 → 中位数是 21,主元为 21

  • 按课程代码模拟交换后结果:[12, 1, 21, 85, 67, 32, 49]

注意:这类题不要只找 pivot,要按课程代码模拟交换!

7.5 堆排序(下标注意)

0 下标堆

  • 左孩子:2*i + 1

  • 右孩子:2*i + 2

  • 父结点:(i-1)/2

建堆和排序算法要点

  1. 从最后一个非叶结点开始下滤建最大堆

  2. 每次将堆顶(最大值)与末尾元素交换

  3. 堆规模减1,从根下滤恢复堆序

  4. 重复直到堆空


第八章:散列表

8.1 基本概念

  • 散列函数h(key) 将关键字映射到地址

  • 装填因子:α=n/mα=n/m(n 为元素个数,m 为表空间大小)

  • 冲突:不同关键字映射到同一地址

  • 理想情况下查找/插入/删除为 O(1)O(1)

8.2 散列函数

整数 :除留余数法 h(x) = x % TableSize,TableSize 取素数效果较好

字符串:移位法

cpp

复制代码
Index Hash(const char *x, int TableSize) {
    unsigned int HashVal = 0;
    while (*x != '\0')
        HashVal = (HashVal << 5) + *x++;
    return HashVal % TableSize;
}

8.3 冲突处理方式

方式 特点 聚集类型
分离链接法 同义词挂同一链表 无聚集
线性探测 f(i) = i 初级聚集
平方探测 f(i) = i^2 二级聚集
双散列 f(i) = i * h2(key) 无明显聚集
再散列 扩表后重新插入 -

8.4 平方探测题型秒杀(高频)

题型:表长 13,h(c) = c % 13,平方探测,插入 [10, 23, 1, 36, 19, 5],问下标 6 放谁。

过程:

  • 10 → 10

  • 23 → 10 冲突,10+12=1110+12=11

  • 1 → 1

  • 36 → 10 冲突,11 冲突,10+22=14≡110+22=14≡1 冲突,10+32=19≡610+32=19≡6

  • 19 → 6 冲突,6+12=76+12=7

  • 5 → 5

答案:下标 6 是 36

重要性质

  • 当 TableSize 为 4k+34k+3 形式的素数时,平方探测可以探查到整个表空间

  • 装填因子小于 0.5 时,平方探测总能找到插入空位

8.5 线性探测 vs 双散列判断点(高频)

  • 线性探测等价于双散列中步长为 1 → ✓

  • 线性探测会引起初级聚集 → ✓

  • 平方探测会引起二级聚集 → ✓

  • 再散列在装填因子过高时触发


第九章:考前最后背诵清单

9.1 必背公式与模板

  • 边数 = N - 1

  • n0=n2+2n3+1n0​=n2​+2n3​+1

  • 完全二叉树前 h 层结点数 = 2h−12h−1

  • 建堆 O(N)O(N),插入/删除 O(log⁡N)O(logN)

并查集

  • Sx < 0 是根,负数表示规模

  • Union 先 Find

  • 邻接表 DFS/BFS = O(V+E)O(V+E)

  • 邻接矩阵 DFS/BFS = O(V2)O(V2)

  • 无权 BFS → Dijkstra → Floyd

MST

  • Prim 稠密图,Kruskal 稀疏图

  • Kruskal 判环用并查集

排序

  • 冒泡/插入/归并稳定;选择/希尔/堆排/快排不稳定

  • 快排最坏 O(N2)O(N2),三数取中优化

散列表

  • 装填因子 α=n/mα=n/m

  • 平方探测表长取素数 4k+34k+3

9.2 考场优先检查

  1. 题目问的是"有向图"还是"无向图"

  2. 边是否有权,权值是否可能为负

  3. 图是稀疏还是稠密

  4. 排序是否要求稳定

  5. 数组下标是从 0 还是从 1 开始

  6. 散列表表长是否为素数

  7. 输出的是一种合法结果,还是要求唯一结果


总结

数据结构的学习,本质上是理解数据在计算机中的组织方式 以及在这些组织方式上的操作效率。树是分层关系,图是多对多关系,散列表是映射关系,排序是整理关系------每一种结构都有其诞生的原因和适用的场景。

期末复习不必追求"背下所有细节",而应该做到**"看到题目能快速定位到对应章节和对应公式"**。这份笔记已经把最核心的公式、复杂度、易错点和高频题型都整理好了,剩下的就是动手写几遍、画几遍、推几遍。