数据结构复习提纲

MD · 18.8 KB · 2026-06-23

数据结构复习提纲

根据课程重点整理,涵盖各章节核心考点与示例


第1章 绪论 / 时间复杂度

考点

  • while循环的时间复杂度分析,特别是基本语义变量与循环变量有关联的情况

示例问题

问题:分析以下代码的时间复杂度

int i = 1;
while (i <= n) {
    i = i * 2;
}

答案:O(log₂n)。循环变量 i 以指数增长,执行次数为 log₂n。


问题:分析以下嵌套循环的时间复杂度

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= i; j++) {
        // 操作
    }
}

答案:O(n²)。内层循环次数为 1+2+3+...+n = n(n+1)/2。


第2章 线性表

2.1 顺序表

考点

  • 插入元素时移动元素个数:分最好情况、最坏情况、平均情况
  • 顺序表支持随机存取(O(1)时间访问任意位置)

示例问题

问题:在长度为 n 的顺序表中插入一个元素,分别求最好、最坏、平均情况下需要移动的元素个数。

答案: | 情况 | 插入位置 | 移动元素个数 | |------|---------|------------| | 最好 | 表尾(第 n+1 位) | 0 | | 最坏 | 表头(第 1 位) | n | | 平均 | 等概率随机插入 | n/2 |

平均情况推导:等概率插入 n+1 个位置,移动次数分别为 n, n-1, ..., 1, 0 $$ASL = \frac{1}{n+1} \sum_{i=0}^{n} i = \frac{n(n+1)/2}{n+1} = \frac{n}{2}$$


2.2 链表(重点)

考点

  • 链表原地逆置:不借助额外空间,将链表元素翻转(空间复杂度 O(1))

示例问题

问题:给定一个带头结点的单链表,编写算法在不使用额外数据空间的情况下将其逆置。

答案

typedef struct LNode {
    int data;
    struct LNode *next;
} LNode, *LinkList;

// 原地逆置单链表
void Reverse(LinkList &L) {
    LNode *p = L->next;    // p指向第一个数据节点
    L->next = NULL;        // 头结点指向空,作为新链表的尾

    while (p != NULL) {
        LNode *q = p;      // q保存当前节点
        p = p->next;       // p后移
        q->next = L->next; // 头插法:q插入到头结点之后
        L->next = q;
    }
}

解析:使用三个指针(p遍历原链表,q保存当前节点,L->next指向新链表头),逐个将节点头插到新链表中,实现逆置。空间复杂度 O(1),时间复杂度 O(n)。


第3章 栈和队列

3.1 栈

考点

  • 给定入栈序列,判断可能的出栈次序
  • 栈的本质是操作受限的线性表(先进后出 FILO)

示例问题

问题:入栈序列为 1, 2, 3,判断以下出栈序列是否可能: - (1) 3, 2, 1 - (2) 2, 3, 1 - (3) 3, 1, 2

答案: - (1) 可能。1入→2入→3入→3出→2出→1出 - (2) 可能。1入→2入→2出→3入→3出→1出 - (3) 不可能。3先出说明1,2,3都已入栈,此时栈顶是2,1在栈底,1不可能在2之前出栈。


3.2 队列

考点

  • 循环队列的长度计算:区分长度(元素个数)与容量
  • 循环队列的队空和队满条件
  • 队列也是受限线性表(先进先出 FIFO)

示例问题

问题:循环队列用数组实现,队头指针 front,队尾指针 rear,数组容量为 MaxSize。如何计算队列长度?队空和队满的条件是什么?

答案

// 牺牲一个单元区分队空和队满
队列长度 = (rear - front + MaxSize) % MaxSize;

// 队空条件:front == rear
// 队满条件:(rear + 1) % MaxSize == front

解析:循环队列通过取模运算实现"循环",长度计算需考虑 rear 可能在 front 之前的情况。牺牲一个存储单元是为了区分队空和队满两种状态。


第4章 串

4.1 数组

考点

  • 数组元素地址计算:给定行列数、元素大小、按行/列优先存储方式
  • 二维数组的地址计算

示例问题

问题:二维数组 A[0..9][0..9],每个元素占 4 个存储单元,按行优先存储,基地址为 1000。求 A[2][5] 的地址。

答案: $$LOC(A[i][j]) = LOC(A[0][0]) + (i \times n + j) \times L$$ $$LOC(A[2][5]) = 1000 + (2 \times 10 + 5) \times 4 = 1000 + 25 \times 4 = 1100$$


4.2 模式匹配

考点

  • KMP算法相对于BF算法的主要优势

示例问题

问题:KMP算法相对于BF(暴力)算法的主要优势是什么?

答案: 1. 主串指针不回溯:BF算法匹配失败时主串指针回溯,KMP利用已匹配信息避免回溯 2. 时间复杂度 O(m+n):BF为 O(m×n),KMP为线性时间 3. 通过next数组:预处理模式串,利用最长公共前后缀信息跳过不必要的比较


4.3 广义表

考点

  • 广义表的深度:计算括号嵌套层数

示例问题

问题:求广义表 A = (a, (b, c), ((d, e), f)) 的深度。

答案深度为 3

解析:深度 = 括号的最大嵌套层数。 - 第1层:A 本身 - 第2层:(b, c) 和 ((d, e), f) - 第3层:(d, e)


第5章 树和二叉树

5.1 完全二叉树性质

考点

  • 完全二叉树叶子节点数:给定 n 个节点,会算叶子节点数
  • 二叉树性质:度为1的节点要么为0,要么为1
  • 满二叉树 vs 完全二叉树:满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树

示例问题

问题1:一棵完全二叉树有 100 个节点,求叶子节点数。

答案: - 设 n₀ 为叶子节点数,n₁ 为度为1的节点数,n₂ 为度为2的节点数 - 节点总数:n = n₀ + n₁ + n₂ = 100 - 分支数:n - 1 = n₁ + 2n₂(总分支数 = 总节点数 - 1) - 联立得:n₀ = n₂ + 1 - 完全二叉树中 n₁ 只能是 0 或 1 - 代入:100 = n₀ + n₁ + (n₀ - 1) = 2n₀ + n₁ - 1 - 若 n₁ = 0:2n₀ = 101(不成立,n₀ 不是整数) - 若 n₁ = 1:2n₀ = 100,n₀ = 50

叶子节点数为 50


问题2:完全二叉树中度为1的节点个数有什么规律?

答案: - 完全二叉树中度为1的节点个数只能是 0 或 1 - 原因:完全二叉树只在最底层从右往左连续缺少若干节点 - 若节点总数 n 为偶数:度为1的节点数为 1 - 若节点总数 n 为奇数:度为1的节点数为 0


5.2 遍历序列恢复二叉树

考点

  • 先序+中序后序+中序恢复二叉树

示例问题

问题:先序序列为 ABDECF,中序序列为 DBEACF,画出该二叉树。

答案

步骤: 1. 先序第一个元素 A 是根节点 2. 在中序中找到 A,左边 DBE 是左子树,右边 CF 是右子树 3. 左子树先序为 BDE,中序为 DBE → B 是根,D 在左,E 在右 4. 右子树先序为 CF,中序为 CF → C 是根,F 在右

        A
       / \
      B   C
     / \    \
    D   E    F

5.3 线索二叉树

考点

  • 根据遍历序列画先序线索二叉树

示例问题

问题:画出上述二叉树的先序线索二叉树。

答案

先序遍历序列:A → B → D → E → C → F

线索化规则: - 若左孩子为空,则左指针指向其先序前驱 - 若右孩子为空,则右指针指向其先序后继

        A
       / \
      B   C
     / \    \
    D   E    F

线索: - D 的左线索为空(先序第一个),右线索指向 E - E 的左线索为空,右线索指向 C - F 的左线索为空,右线索为空(先序最后一个)


5.4 树、森林与二叉树的转换

考点

  • 树 ↔ 二叉树 ↔ 森林的相互转换(孩子兄弟表示法)

示例问题

问题:将以下森林转换为二叉树。

森林: - 树1:A(B(E,F), C, D) - 树2:G(H, I)

答案

步骤: 1. 每棵树先转为二叉树(左孩子右兄弟) 2. 树1转二叉树:A 为根,B 为左孩子,C 为 B 的右兄弟,D 为 C 的右兄弟;E、F 为 B 的左孩子和右兄弟 3. 树2转二叉树:G 为根,H 为左孩子,I 为 H 的右兄弟 4. 森林转二叉树:将树2的根 G 作为树1根 A 的右孩子

        A
       / 
      B — C — D
     / 
    E — F

    (G 作为 A 的右孩子)

第6章 图

6.1 握手定理

考点

  • 给定边数和顶点度范围,求顶点数

示例问题

问题:无向图有 18 条边,度为4的顶点有2个,度为3的顶点有4个,度为5的顶点有1个,其余顶点度为1或2。求总顶点数。

答案: - 握手定理:所有顶点度数之和 = 2 × 边数 = 36 - 已知顶点度数和:4×2 + 3×4 + 5×1 = 8 + 12 + 5 = 25 - 剩余度数:36 - 25 = 11 - 设度为1的顶点有 x 个,度为2的顶点有 y 个 - 方程:x + 2y = 11,且 x + y = 剩余顶点数 - 可能解:y=5, x=1(顶点数6)或 y=4, x=3(顶点数7)等 - 需根据题目其他条件确定唯一解


6.2 图的遍历

考点

  • 会写 DFSBFS 遍历序列
  • 实现区别:DFS用栈/递归,BFS用队列

示例问题

问题:给定下图,从顶点 A 出发写出 DFS 和 BFS 序列。

    A — B — C
    |   |   |
    D — E — F

答案: - DFS(深度优先):A → B → C → F → E → D(假设邻接表按字母序) - BFS(广度优先):A → B → D → C → E → F

实现区别: | | DFS | BFS | |--|-----|-----| | 数据结构 | 栈(递归隐式栈) | 队列 | | 访问顺序 | 深入到底再回溯 | 逐层扩展 | | 应用 | 连通分量、拓扑排序 | 最短路径(无权图) |


6.3 最小生成树

考点

  • Prim算法Kruskal算法的构造过程

示例问题

问题:用 Prim 算法求以下图的最小生成树。

    A —2— B —3— C
    |4    |1    |5
    D —6— E —2— F

答案

Prim算法步骤(从A开始): 1. 选 A,邻接边:(A,B,2), (A,D,4),选最小 (A,B,2) 2. 集合 {A,B},邻接边:(A,D,4), (B,E,1), (B,C,3),选最小 (B,E,1) 3. 集合 {A,B,E},邻接边:(A,D,4), (B,C,3), (E,D,6), (E,F,2),选最小 (E,F,2) 4. 集合 {A,B,E,F},邻接边:(A,D,4), (B,C,3), (E,D,6),选最小 (B,C,3) 5. 集合 {A,B,E,F,C},邻接边:(A,D,4), (E,D,6),选最小 (A,D,4)

最小生成树总权值:2 + 1 + 2 + 3 + 4 = 12


6.4 拓扑排序

考点

  • 适用于有向无环图(DAG)
  • 环图无法进行拓扑排序
  • 拓扑排序应用:项目管理(AOV网)

示例问题

问题:判断以下有向图能否进行拓扑排序。

    A → B → C
    ↑       ↓
    └———————D

答案: - 存在环:A → B → C → D → A - 不能进行拓扑排序 - 原因:环中每个顶点都有前驱,找不到入度为0的顶点作为起点


6.5 邻接矩阵

考点

  • 无向图的邻接矩阵是对称的
  • 根据邻接矩阵画图
  • 根据边集和顶点集写出邻接矩阵

示例问题

问题:无向图的邻接矩阵有什么特点?

答案: - 对称矩阵:A[i][j] = A[j][i] - 对角线元素为0(无自环) - 第 i 行(或第 i 列)非零元素个数 = 顶点 i 的度


第7章 查找

7.1 哈希表(重点)

考点

  • 除留余数法构造哈希表
  • 三种冲突解决方法:线性探测法、二次探测法、链地址法
  • 线性探测法的缺点:聚集现象
  • 二次探测法解决冲突时产生的问题
  • 哈希表三层结构:下标、关键字位置、查找比较次数
  • 平均查找长度(ASL)计算
  • 哈希表长度与地址空间的关系

示例问题

问题1:关键字序列 {7, 8, 30, 11, 18, 9, 14},哈希函数 H(key) = key mod 7,用线性探测法解决冲突,构造哈希表并计算 ASL。

答案

下标 0 1 2 3 4 5 6
关键字 7 14 9 8 18 30 11
比较次数 1 7 2 1 2 1 1

计算过程: - H(7) = 0,下标0空,放入,比较 1 次 - H(14) = 0,冲突→1,2,3,4,5,6空,放入,比较 7 次 - H(30) = 2,下标2空,放入,比较 1 次 - H(11) = 4,下标4空,放入,比较 1 次 - H(18) = 4,冲突→5空,放入,比较 2 次 - H(9) = 2,冲突→3空,放入,比较 2 次 - H(8) = 1,下标1空,放入,比较 1

ASL(成功) = (1+7+1+1+2+2+1) / 7 = 15/7 ≈ 2.14

线性探测法缺点:产生"聚集"现象,如 14 需要探测多次才能找到空位。


问题2:哈希表地址空间为 0~8,哈希表长度是多少?

答案9(0~8 共 9 个地址单元)

解析:哈希表长度 m = 9,哈希函数 H(key) = key mod m 中的 m 就是表长。表长与解决冲突时的增量计算直接相关。


问题3:二次探测法解决冲突时会产生什么问题?

答案: - 二次探测法:Hᵢ = (H(key) + dᵢ) mod m,其中 dᵢ = 1², -1², 2², -2², ... - 优点:可以缓解聚集现象(避免线性探测的连续堆积) - 缺点:可能产生二次聚集(二次堆积),且不一定能探测到所有空位


7.2 二叉排序树(BST)

考点

  • 给定序列建立二叉排序树
  • 计算查找成功时的平均查找长度

示例问题

问题:给定关键字序列 {50, 30, 70, 20, 40, 60, 80},建立二叉排序树并计算查找成功时的 ASL。

答案

        50
       /  \
     30    70
    / \   / \
  20  40 60  80

查找过程: | 关键字 | 所在层 | 比较次数 | |--------|--------|---------| | 50 | 1 | 1 | | 30 | 2 | 2 | | 70 | 2 | 2 | | 20 | 3 | 3 | | 40 | 3 | 3 | | 60 | 3 | 3 | | 80 | 3 | 3 |

ASL(成功) = (1×1 + 2×2 + 4×3) / 7 = 17/7 ≈ 2.43

解析:查找时先与根比较,小则走左子树,大则走右子树,递归进行。


7.3 折半查找

考点

  • 对有序顺序表进行二分查找
  • 查找失败时最多比较次数

示例问题

问题:在有序表 {5, 13, 19, 21, 37, 56, 64, 75, 80, 88, 92} 中查找 20,最多比较几次?画出判定树。

答案

判定树

            56(5)
           /     \
        19(2)    80(8)
       /    \    /    \
    13(1)  37(4) 64(7)  88(10)
   /       /       \      /   \
  5(0)   21(3)    75(6)  92(11)

查找 20: 1. 与 56 比较,20 < 56,进入左子树 2. 与 19 比较,20 > 19,进入右子树 3. 与 37 比较,20 < 37,进入左子树 4. 与 21 比较,20 < 21,应到左子树但为空

查找失败,比较 4 次

最多比较次数 = 树高 = ⌈log₂(n+1)⌉ = ⌈log₂12⌉ = 4


第8章 排序

8.1 快速排序

考点

  • 快速排序过程
  • 快速排序在数据基本有序时效率最差

示例问题

问题:对序列 {49, 38, 65, 97, 76, 13, 27, 49} 进行快速排序,写出第一趟结果。

答案

选第一个元素 49 为枢轴:

初始:49, 38, 65, 97, 76, 13, 27, 49

过程(双向扫描): 1. 从右向左找 < 49 的:27,与 49 交换位置 → 27, 38, 65, 97, 76, 13, 49, 49 2. 从左向右找 > 49 的:65,与 49 交换 → 27, 38, 49, 97, 76, 13, 65, 49 3. 从右向左找 < 49 的:13,交换 → 27, 38, 13, 97, 76, 49, 65, 49 4. 从左向右找 > 49 的:97,交换 → 27, 38, 13, 49, 76, 97, 65, 49 5. 从右向左找 < 49 的:无(左右指针相遇)

第一趟结果:27, 38, 13, 49, 76, 97, 65, 49 (49 已归位,左边都小于49,右边都大于等于49)


问题:快速排序在什么情况下效率最差?为什么?

答案: - 数据基本有序时效率最差 - 原因:每次选择的枢轴都是当前子序列的最大/最小值,导致划分极不平衡 - 时间复杂度退化为 O(n²) - 改进:随机选择枢轴或三数取中法


8.2 堆排序

考点

  • 建堆过程(大根堆/小根堆)
  • 堆排序是增序还是降序

示例问题

问题:堆排序如果要排增序,应该建大根堆还是小根堆?

答案: - 排增序 → 建大根堆 - 原因:每次将堆顶(最大值)与最后一个元素交换,最大值放到末尾,然后对前面元素重新调整堆 - 类似地,排降序 → 建小根堆


排序算法对比表

算法 最好时间 平均时间 最坏时间 空间复杂度 稳定性
直接插入排序 O(n) O(n²) O(n²) O(1) ✅ 稳定
冒泡排序 O(n) O(n²) O(n²) O(1) ✅ 稳定
快速排序 O(n log n) O(n log n) O(n²) O(log n) ❌ 不稳定
简单选择排序 O(n²) O(n²) O(n²) O(1) ❌ 不稳定
堆排序 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) ✅ 稳定

附加:哈夫曼树

考点

  • 根据字符频率构造哈夫曼树
  • 计算带权路径长度(WPL)
  • 写出哈夫曼编码
  • 由 n 个权值构成的哈夫曼树有多少个节点
  • 根据哈夫曼编码计算报文长度

示例问题

问题1:字符集 {A, B, C, D},频率分别为 {4, 7, 5, 2},构造哈夫曼树并求 WPL 和编码。

答案

构造过程: 1. 选最小两个:D(2), A(4) → 合并为 6 2. 选最小两个:C(5), B(7) → 合并为 12 3. 选最小两个:6, 12 → 合并为 18(根)

          18
         /  \
        6    12
       / \   / \
      D   A C   B
     (2) (4)(5) (7)

哈夫曼编码(左0右1): | 字符 | 编码 | 路径长度 | |------|------|---------| | D | 00 | 2 | | A | 01 | 2 | | C | 10 | 2 | | B | 11 | 2 |

WPL = 2×2 + 4×2 + 5×2 + 7×2 = 36

或:WPL = 所有非叶子节点权值之和 = 6 + 12 + 18 = 36

特点:哈夫曼编码是前缀编码,任何编码都不是另一个编码的前缀,确保解码唯一。


问题2:由 n 个权值构成的哈夫曼树有多少个节点?

答案2n - 1 个节点

推导: - n 个叶子节点(权值节点) - 每次合并产生一个新节点,共需 n-1 次合并 - 总节点数 = n + (n-1) = 2n - 1


问题3:根据上述哈夫曼编码,若报文为 "ABCD",计算编码后的报文长度。

答案: - A: 01 (2位) - B: 11 (2位) - C: 10 (2位) - D: 00 (2位) - 报文 "ABCD" 编码:01 11 10 00 - 总长度 = 2+2+2+2 = 8 位


答题注意事项

  1. 排序题:不要只写一轮结果,要写出完整的排序过程,每轮都要展示
  2. 画图题:二叉树、线索二叉树、哈夫曼树、图等要画清楚
  3. 计算题:写出推导过程,不要只写结果
  4. 算法题:链表逆置等要写完整代码或清晰步骤

祝考试顺利!🎓