考研复习 Day 22 | 数据结构与算法--排序(下)

一、归并排序

1.1 基本思想

归并排序:将两个或多个有序序列合并为一个更长的有序序列。2路归并排序是其中最常用的形式。

2路归并排序过程

初始: [49] [38] [65] [97] [76] [13] [27]
第一趟: [38 49] [65 97] [13 76] [27]
第二趟: [38 49 65 97] [13 27 76]
第三趟: [13 27 38 49 65 76 97]

1.2 归并操作(Merge)

复制代码
ElemType *B = (ElemType *)malloc((n + 1) * sizeof(ElemType));

void Merge(ElemType A[], int low, int mid, int high) {
    for (int k = low; k <= high; k++) B[k] = A[k];
    int i = low, j = mid + 1, k = low;
    while (i <= mid && j <= high) {
        if (B[i] <= B[j]) A[k++] = B[i++];
        else A[k++] = B[j++];
    }
    while (i <= mid) A[k++] = B[i++];
    while (j <= high) A[k++] = B[j++];
}

1.3 递归实现

复制代码
void MergeSort(ElemType A[], int low, int high) {
    if (low < high) {
        int mid = (low + high) / 2;
        MergeSort(A, low, mid);
        MergeSort(A, mid + 1, high);
        Merge(A, low, mid, high);
    }
}

1.4 性能分析

指标
空间复杂度 O(n)(需辅助数组)
时间复杂度(最好/最坏/平均) O(n log₂n)
稳定性 稳定
适用性 顺序存储 + 链式存储

每趟归并需遍历所有元素O(n),共⌈log₂n⌉趟,总时间复杂度O(n log₂n)。


二、基数排序

2.1 基本思想

基数排序 :不比较元素大小,而是基于关键字各位的大小进行排序。是一种多关键字排序

两种方法

  • MSD(最高位优先):按关键字位权重递减,逐层划分

  • LSD(最低位优先):按关键字位权重递增,依次排序(更常用)

2.2 链式基数排序(LSD)

以10个三位数为例:278, 109, 063, 930, 589, 184, 505, 269, 008, 083

第一趟(按个位分配→收集):

分配:0:930 3:063,083 4:184 5:505 8:278,008 9:109,589,269
收集:930,063,083,184,505,278,008,109,589,269

第二趟(按十位分配→收集):

分配:0:505,008,109 6:063,269 7:278 8:083,184 9:589,930
收集:505,008,109,063,269,278,083,184,589,930

第三趟(按百位分配→收集):

分配:0:008,063,083 1:109,184 2:269,278 5:505,589 9:930
收集:008,063,083,109,184,269,278,505,589,930

2.3 性能分析

指标
空间复杂度 O(r)(r个队列)
时间复杂度 O(d(n + r))
稳定性 稳定
适用性 顺序存储 + 链式存储

基数排序的时间复杂度与序列初始状态无关


三、计数排序

3.1 基本思想

计数排序:统计每个元素值出现的次数,根据次数确定每个元素在有序序列中的位置。

复制代码
void CountSort(ElemType A[], ElemType B[], int n, int k) {
    int C[k];
    for (int i = 0; i < k; i++) C[i] = 0;
    for (int i = 0; i < n; i++) C[A[i]]++;
    for (int i = 1; i < k; i++) C[i] = C[i] + C[i - 1];
    for (int i = n - 1; i >= 0; i--) {
        B[C[A[i]] - 1] = A[i];
        C[A[i]]--;
    }
}

3.2 性能分析

指标
空间复杂度 O(n + k)
时间复杂度 O(n + k)
稳定性 稳定
适用性 顺序存储

:计数排序不在统考大纲范围内,但其思想在历年真题中多次涉及。


四、各种内部排序算法的比较

4.1 时间复杂度对比

算法 最好 平均 最坏
直接插入排序 O(n) O(n²) O(n²)
冒泡排序 O(n) O(n²) O(n²)
简单选择排序 O(n²) O(n²) O(n²)
希尔排序 --- O(n^1.3) O(n²)
快速排序 O(n log₂n) O(n log₂n) O(n²)
堆排序 O(n log₂n) O(n log₂n) O(n log₂n)
归并排序 O(n log₂n) O(n log₂n) O(n log₂n)
基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r))

4.2 空间复杂度对比

算法 空间复杂度
直接插入/冒泡/简单选择/希尔/堆排序 O(1)
快速排序 O(log₂n) ~ O(n)
归并排序 O(n)
基数排序 O(r)

4.3 稳定性对比

稳定 不稳定
直接插入排序 简单选择排序
冒泡排序 快速排序
归并排序 希尔排序
基数排序 堆排序

在平均时间复杂度为O(n log₂n)的排序算法中,只有归并排序是稳定的

4.4 适用性对比

适用顺序存储 顺序存储 + 链式存储
折半插入排序 直接插入排序
希尔排序 冒泡排序
快速排序 简单选择排序
堆排序 归并排序
--- 基数排序

4.5 排序算法选型建议

场景 推荐算法
n较小 直接插入排序 / 简单选择排序
n较大 快速排序 / 堆排序 / 归并排序
基本有序 直接插入排序 / 冒泡排序
关键字位数少、可分解 基数排序
要求稳定 归并排序(O(n log₂n)中唯一稳定)
记录信息量大 链表 + 插入类/归并类排序

五、外部排序

5.1 基本概念

外部排序:待排序记录存储在外存中,无法一次性全部装入内存,需在内存与外存之间多次交换数据。

外部排序时间组成

总时间 = 内部排序时间 + 外存读/写时间 + 内部归并时间

外存读/写时间远大于内部排序/归并时间,是优化重点。

5.2 外部排序的基本方法

两个阶段

  1. 生成初始归并段:将文件分块读入内存,用内部排序排序后写回外存

  2. 多路归并:逐趟归并初始归并段,直到得到完整有序文件

示意图(2路归并,8个初始归并段):

R1 R2 R3 R4 R5 R6 R7 R8
↓ ↓ ↓ ↓
R1' R2' R3' R4'
↓ ↓
R1" R2"

R1"'

5.3 多路平衡归并与败者树

问题:增大归并路数k可减少归并趟数,但内部归并时间会增加(每选一个最小元素需k-1次比较)。

解决方案 :引入败者树

败者树特点

  • k个叶结点存放k个归并段当前参与比较的元素

  • 内部结点记录"败者"(关键字较大者)

  • 胜者继续向上比较

  • 最终胜者存于ls[0]

使用败者树后,每选一个最小元素仅需⌈log₂k⌉次比较,内部归并比较次数与k无关

归并路数k的限制:k不能过大,否则每个输入缓冲区容量减小,每趟归并中外存读/写次数增加,可能反而降低性能。

5.4 置换-选择排序(生成初始归并段)

目的:生成更长的初始归并段,减少归并段个数r。

算法步骤

  1. 从输入文件FI读入w个记录到工作区WA

  2. 从WA中选出关键字最小的记录,记为MINIMAX

  3. 将MINIMAX输出到输出文件FO

  4. 若FI非空,读入下一个记录到WA

  5. 从WA中所有关键字≥当前MINIMAX的记录中,选出最小者作为新的MINIMAX

  6. 重复3-5,直到WA中无关键字≥当前MINIMAX的记录(完成一个归并段)

  7. 重复2-6,直到FI和WA均为空

5.5 最佳归并树

问题:初始归并段长度不等,如何安排归并顺序使I/O次数最少?

解决方案 :构造k叉哈夫曼树(最佳归并树)

虚段的添加

  • 设初始归并段个数为n₀,归并路数为k

  • (n₀ - 1) % (k - 1) = 0,可直接构造严格k叉树

  • 否则,需添加 (k - 1) - (n₀ - 1) % (k - 1) 个虚段(长度为0)


六、思考

1. 归并排序 ≈ 合并两个有序链表

这和合并两个有序链表的代码思想是一样的。归并排序就是反复做这件事------先把序列拆成单个元素(天然有序),再两两合并。
2. 基数排序 ≈ 图书馆图书排序

先按个位数字排(书架号),再按十位数字排(楼层号),最后按百位数字排(馆区号)。每趟排序不影响上一趟的相对顺序。(反之亦可)
3. 败者树 ≈ 锦标赛淘汰赛

8个选手比赛,每场记录败者,胜者晋级。最后胜者是冠军,而败者树记录了每场比赛谁输了。
4. 置换-选择排序 ≈ 流水线生产

工作区就像一个"蓄水池",不断从上游(输入文件)取新元素,同时向下游(输出文件)送元素,只要新元素不小于刚送走的元素,就继续。


七、各算法复杂度表

算法 时间复杂度 空间复杂度 稳定性
归并排序 O(n log₂n) O(n) 稳定
基数排序 O(d(n+r)) O(r) 稳定
计数排序 O(n+k) O(n+k) 稳定
直接插入排序 O(n²) O(1) 稳定
冒泡排序 O(n²) O(1) 稳定
简单选择排序 O(n²) O(1) 不稳定
希尔排序 O(n^1.3) O(1) 不稳定
快速排序 O(n log₂n) O(log₂n) 不稳定
堆排序 O(n log₂n) O(1) 不稳定

注:以上内容参考 2027年数据结构考研复习指导 王道论坛 组编,其中有一些个人想法,如有任何错误或不妥,欢迎各位大佬指出,如果各位有一些有意思的想法,也可以和我交流一下~感谢!

另:因计划报考的的学校专业课只考察《计算机网络》《数据结构与算法》,故其他两门计算机专业课不再复习。并且这两门专业课已经复习完毕,所以明日起就开始写题。

相关推荐
LG.YDX2 小时前
笔试训练48天:最长无重复子数组
数据结构·算法
SHARK_pssm2 小时前
【数据结构——顺序表】
c语言·数据结构·经验分享·笔记
gumichef2 小时前
*链表OJ
数据结构·链表
如君愿3 小时前
考研复习 Day 21 | 数据结构与算法--排序(上)
数据结构·考研·排序算法·记录考研
hnjzsyjyj3 小时前
全排列问题DFS实现执行示意图
数据结构·dfs
故事和你914 小时前
洛谷-算法2-2-常见优化技巧3
开发语言·数据结构·c++·算法·深度优先·动态规划·图论
菜鸟555554 小时前
2025江西省CCPC省赛暨全国邀请赛(南昌)
数据结构·c++·算法·acm·思维·ccpc·xcpc
꧁细听勿语情꧂5 小时前
用队列实现栈、用栈实现队列,树、二叉树、满二叉树、完全二叉树,堆、向下向上调整算法、出堆入堆、堆排序
c语言·开发语言·数据结构·算法
周末也要写八哥5 小时前
什么是快速选择及案例分析
数据结构