- 基本概念
• 排序:将一组无序的数据元素,按指定关键字的递增或递减顺序重新排列的过程。
• 稳定性:若待排序序列中,两个关键字相同的元素,排序后相对位置不变,则为稳定排序;否则为不稳定排序。
• 内排序:排序过程中,所有数据都在内存中完成,适用于数据量较小的场景。
• 外排序:数据量过大,内存无法容纳全部数据,需借助外存(如磁盘)辅助排序。
- 常见内排序算法
2.1 插入排序
直接插入排序
-
初始化:将待排序序列分为有序区(初始为第一个元素)和无序区。
-
插入操作:依次取出无序区的第一个元素,与有序区元素从后往前比较,找到合适位置插入,扩大有序区。
-
重复步骤2:直到无序区元素全部插入有序区。
• 特性:稳定排序;最好时间复杂度 O(n)(序列已有序),最坏和平均 O(n^2);空间复杂度 O(1)。
希尔排序(缩小增量排序)
-
增量分组:选定一个增量 d_1(通常 d_1=\lfloor n/2 \rfloor),将序列分为 d_1 个组,每组元素下标间隔为 d_1。
-
组内排序:对每组进行直接插入排序。
-
缩小增量:取 d_{i+1}=\lfloor d_i/2 \rfloor,重复分组和排序操作,直到增量 d=1。
-
最终排序:增量为1时,对整个序列做一次直接插入排序,完成排序。
• 特性:不稳定排序;时间复杂度与增量选择有关,平均约 O(n^{1.3});空间复杂度 O(1)。
2.2 交换排序
冒泡排序
-
相邻比较:从序列起始位置开始,依次比较相邻两个元素,若逆序则交换。
-
趟数控制:每一趟排序都会将当前无序区的最大元素"冒泡"到末尾,形成有序区的一部分。
-
提前结束:若某一趟排序中没有发生任何交换,说明序列已有序,可直接终止排序。
• 特性:稳定排序;最好时间复杂度 O(n)(一趟无交换),最坏和平均 O(n^2);空间复杂度 O(1)。
快速排序
-
划分操作:选取序列中的一个元素作为基准,将序列划分为两部分,左部分元素均小于等于基准,右部分元素均大于等于基准,基准元素放到最终位置。
-
递归排序:对左、右两个子序列分别递归执行划分和排序操作,直到子序列长度为1或0。
• 特性:不稳定排序;平均时间复杂度 O(n\log n),最坏 O(n^2)(如有序序列选两端为基准);空间复杂度 O(\log n)(递归栈深度)。
2.3 选择排序
简单选择排序
-
选择最值:在无序区中找到关键字最小的元素,与无序区的第一个元素交换位置。
-
扩大有序区:无序区起始位置后移一位,重复步骤1,直到无序区为空。
• 特性:不稳定排序;最好、最坏、平均时间复杂度均为 O(n^2);空间复杂度 O(1)。
堆排序
-
构建大顶堆:将待排序序列调整为大顶堆(每个父结点关键字大于等于子结点)。
-
堆顶交换:将堆顶元素(当前最大值)与堆的最后一个元素交换,此时堆的有效长度减1。
-
堆调整:将剩余的有效序列重新调整为大顶堆,重复步骤2和3,直到有效堆长度为1。
• 特性:不稳定排序;时间复杂度 O(n\log n)(构建堆 O(n),调整堆 O(n\log n));空间复杂度 O(1)。
2.4 归并排序
-
分解:将待排序序列递归地划分为两个长度大致相等的子序列,直到子序列长度为1。
-
合并:将两个有序子序列合并为一个有序序列,重复合并操作,直到得到完整的有序序列。
• 特性:稳定排序;时间复杂度 O(n\log n)(最好、最坏、平均);空间复杂度 O(n)(需要额外数组存储合并结果)。
- 排序算法性能对比核心要点
• 平均时间复杂度 O(n\log n) 的算法:快速排序、堆排序、归并排序。
• 稳定排序算法:直接插入排序、冒泡排序、归并排序。
• 原地排序(空间复杂度 O(1)):直接插入、希尔、冒泡、简单选择、堆排序。
- 算法选择依据
• 数据量小:优先选直接插入排序或冒泡排序,实现简单。
• 数据基本有序:直接插入排序或冒泡排序,效率接近 O(n)。
• 数据量大:选快速排序、堆排序或归并排序;追求稳定性选归并排序,关注空间选堆排序。