Skip to content

8.1 排序基本概念

稳定性

Pasted image 20251102185141.png

分类

Pasted image 20251102185109.png

8.2 插入与希尔

8.2.1 插入排序

Pasted image 20251103143724.png

代码实现

每次将大于待插入元素的前面的元素后移一位,再将待插入元素移入空出的位置: Pasted image 20251103144227.png 空间复杂度:O(1),时间复杂度:O(n^2),稳定。在待排序的记录基本有序的时候,时间复杂度能达到O(1)的级别。

优化

在待插入元素的前面的部分是有序的,可以使用折半查找优化: Pasted image 20251103145002.pngPasted image 20251103145042.pngPasted image 20251103145127.png 此时low在high的右侧,停止折半查找,high右侧的元素均大于i所指元素,low左侧则均小于。在low和high中间的位置就是待插入元素的插入位置: Pasted image 20251103145257.png 特殊的,在这种情况下,不应该停止查找,而应该是在右侧继续寻找60,确保算法的稳定性: Pasted image 20251103145651.pngPasted image 20251103145812.png 这时,插入60就能够保证算法的稳定性: Pasted image 20251103145845.pngPasted image 20251103150256.png

8.2.2 希尔排序

将表中的元素拆分为多个子表,将子表中的元素排序,最后整体有序。

Pasted image 20251103151307.pngPasted image 20251103151347.pngPasted image 20251103151522.pngPasted image 20251103151545.png 在这一次排序中,表中的数据就已经基本有序: Pasted image 20251103151630.pngPasted image 20251103151712.png

代码实现

Pasted image 20251103152435.png 空间复杂度:O(1),时间复杂度:在O(n^1.3)到O(n^2),暂时无法用数学形式证明。不稳定。并且只能基于线性表实现。

8.3 交换排序

8.3.1 冒泡排序

Pasted image 20251103153829.pngPasted image 20251103153859.pngPasted image 20251103153950.pngPasted image 20251103154029.png

算法实现

若某一趟排序过程没有交换,就说明已经有序,可以提前结束: Pasted image 20251103154257.png

空间复杂度:O(1),时间复杂度:O(n^2),稳定。 Pasted image 20251103154658.png

8.3.1 快速排序

Pasted image 20251103160249.pngPasted image 20251103160517.pngPasted image 20251103160538.pngPasted image 20251103160556.pngPasted image 20251103160609.pngPasted image 20251103160626.png 将基准元素左右两侧的子表继续快排,直到左右子表就剩一个元素的时候

代码实现(重点)

Pasted image 20251103160903.png

空间复杂度:O(递归层数),时间复杂度:O(n * 递归层数),不稳定。递归层数的数量级在log n与n之间。理想情况下,空间复杂度:O(log n),时间复杂度:O(nlog n) Pasted image 20251103163056.png

对于本有序的序列,快排的性能最差。可以随机选取枢轴元素来解决这个问题。

8.4 选择排序

8.4.1 简单选择排序(暴力)

在待排序的元素中选着最小的加入有序队列

代码实现

Pasted image 20251103163815.png 空间复杂度:O(1),时间复杂度:O(n^2),不稳定。

8.4.2 堆排序

Pasted image 20251103171311.png 被选出的堆顶元素,就不会再继续参与排序(已确定)

代码实现

Pasted image 20251103171801.png 建堆过程时间复杂度:O(n),排序整体时间复杂度:O(nlog n),空间复杂度:O(1),不稳定

Pasted image 20251103165446.png 逻辑上是一棵顺序存储的完全二叉树,大根堆就是根节点大于左右子树。

将初始的序列建立为一个大根堆,再使用选择排序就能提高效率。 Pasted image 20251103170150.png

堆的代码实现

Pasted image 20251103170622.png

堆插入与删除

Pasted image 20251103173416.pngPasted image 20251103173627.png

8.5

8.5.1 归并排序

Pasted image 20251103191125.png

代码实现

Pasted image 20251103191743.png

Pasted image 20251103192432.png 时间复杂度:O(nlog n),空间复杂度:O(n)(其中包括辅助数组n和递归栈所占用的空间log n),稳定的。

归并

将多个有序的序列合并 Pasted image 20251103190045.png

考试通常是二路,手算。上例是二路归并,下面为四路: Pasted image 20251103190716.png

8.5.2 基数排序

将关键字的每个位数取出排序后,再取高位再排序,直到全部的位都被排序过 Pasted image 20251103193727.pngPasted image 20251103193651.pngPasted image 20251103193916.pngPasted image 20251103194045.pngPasted image 20251103194202.pngPasted image 20251103194249.pngPasted image 20251103194333.png 通常基于链式存储实现,空间复杂度:O(每位可能取值的个数),时间复杂度:O(分配的次数 * (关键字的位数 + 每位可能取值的个数)),稳定的。

对于要排序的数据可以拆分、分组不太多的时候时候使用: Pasted image 20251103195835.png

8.5.3 计数排序

8.7 外部排序

8.7.1 基础概念

Pasted image 20251108153246.png

构造初始归并段

将第一块与第二块的内容读入内存,进行内部排序,再写入内存变为一个归并段: Pasted image 20251108153650.png

第一趟归并

将归并段进行归并: Pasted image 20251108153909.pngPasted image 20251108154021.png

第二趟归并

Pasted image 20251108154156.png

时间开销分析

Pasted image 20251108154418.png 时间开销=读写时间+排序时间(构造初始归并段)+归并时间

减少归并的次数就能减少读写的时间: Pasted image 20251108155045.png

8.7.2 败者树

Pasted image 20251108155324.png 使用多路归并时,对比的次数会变多,可以使用败者树优化

Pasted image 20251108160637.pngPasted image 20251108160729.png 初始构造时对比k-1次,之后使用败者树最少可以对比log k向上取整的次数,比k-1要少的多

8.7.3 置换-选择排序

优化构造初始归并段的过程,来构建更长的初始归并段: Pasted image 20251108162054.png

将比minmax更大的最小值输出,直到wa中不能再输出,就生成一个新的归并段: Pasted image 20251108162221.png

8.7.4 最佳归并树

使用置换-选择排序算法生成的归并段长度不一,每次都要读写每个段的长度: Pasted image 20251108162604.png

使用哈夫曼树来得到wpl最小的树: Pasted image 20251108162745.png

构造3路归并的最佳归并树: Pasted image 20251108162951.png

如果无法构造k叉归并树: Pasted image 20251108163232.pngPasted image 20251108163732.png