排序算法可以从多个维度进行分类,理解这些分类有助于在实际应用中选择合适的算法。本文将从时间复杂度、比较方式、稳定性、空间复杂度等多个角度对排序算法进行系统分类和介绍。
一、按时间复杂度分类
1.1 平方阶排序 O(n²)
这类排序算法实现简单,适合小规模数据,但在大数据量时性能较差。
| 算法 | 最优 | 平均 | 最坏 | 特点 |
|---|---|---|---|---|
| 冒泡排序 | O(n) | O(n²) | O(n²) | 最简单,可提前退出 |
| 选择排序 | O(n²) | O(n²) | O(n²) | 交换次数最少 |
| 插入排序 | O(n) | O(n²) | O(n²) | 对小数据最快 |
适用场景:
- 数据量小于 50:优先使用插入排序
- 数据量 50-1000:考虑使用希尔排序或更高级算法
- 教学演示:使用冒泡排序(最直观)
代码示例对比:
1 | // 三种 O(n²) 排序的核心操作对比 |
1.2 线性对数阶排序 O(n log n)
这类排序算法是实际应用中最常用的,适合大规模数据。
| 算法 | 最优 | 平均 | 最坏 | 空间 | 稳定性 |
|---|---|---|---|---|---|
| 快速排序 | O(n log n) | O(n log n) | O(n²) | O(log n) | 不稳定 |
| 归并排序 | O(n log n) | O(n log n) | O(n log n) | O(n) | 稳定 |
| 堆排序 | O(n log n) | O(n log n) | O(n log n) | O(1) | 不稳定 |
| 希尔排序 | O(n log n) | O(n^1.3) | O(n²) | O(1) | 不稳定 |
选择建议:
1 | 数据规模较大时: |
性能对比(排序 100 万个随机整数):
| 算法 | 耗时 | 内存占用 |
|---|---|---|
| 快速排序 | ~100ms | ~log n |
| 归并排序 | ~120ms | O(n) |
| 堆排序 | ~150ms | O(1) |
| 希尔排序 | ~200ms | O(1) |
1.3 线性阶排序 O(n)
这类排序算法突破了比较排序的下界,但需要特定数据条件。
| 算法 | 时间复杂度 | 空间复杂度 | 数据要求 | 稳定性 |
|---|---|---|---|---|
| 计数排序 | O(n + k) | O(n + k) | 整数,范围小 | 稳定 |
| 桶排序 | O(n + k) | O(n + k) | 分布均匀 | 稳定 |
| 基数排序 | O(d(n + k)) | O(n + k) | 固定位数 | 稳定 |
k = 数据范围,d = 数字位数
适用条件对比:
1 | // 计数排序:适合数据范围小 |
二、按比较/非比较分类
2.1 比较排序
定义:通过比较元素之间的大小关系来决定元素的相对位置。
特点:
- 适用范围广:可以排序任何可比较的数据类型
- 理论下界:平均和最坏情况时间复杂度不低于 O(n log n)
- 灵活性高:可以实现稳定或不稳定排序
算法列表:
- 冒泡排序、选择排序、插入排序
- 希尔排序、快速排序、归并排序、堆排序
比较次数分析:
1 | 决策树模型: |
2.2 非比较排序
定义:不通过直接比较元素大小,而是利用数据的特定特征进行排序。
特点:
- 时间复杂度可突破 O(n log n),达到 O(n)
- 适用范围受限:需要数据满足特定条件
- 通常需要额外的空间
算法列表:
- 计数排序、桶排序、基数排序
工作原理对比:
1 | // 计数排序:统计每个值的出现次数 |
三、按稳定性分类
3.1 稳定排序
定义:相等元素的相对顺序在排序前后保持不变。
稳定排序算法:
- 冒泡排序 ✓
- 插入排序 ✓
- 归并排序 ✓
- 计数排序 ✓
- 桶排序 ✓
- 基数排序 ✓
稳定性示例:
1 | // 学生记录,先按班级排序,再按成绩排序 |
实现稳定性的关键:
1 | // 归并排序的稳定性保证 |
3.2 不稳定排序
定义:相等元素的相对顺序可能改变。
不稳定排序算法:
- 选择排序 ✗
- 希尔排序 ✗
- 快速排序 ✗
- 堆排序 ✗
为什么不稳定:
1 | // 选择排序的不稳定性示例 |
四、按空间复杂度分类
4.1 原地排序 O(1)
定义:除了存储输入数据外,只需要常数级别的额外空间。
原地排序算法:
- 冒泡排序
- 选择排序
- 插入排序
- 希尔排序
- 堆排序
优势:
- 内存占用低
- 适合嵌入式系统
- 缓存友好
1 | // 所有原地排序的共同特点 |
4.2 需要额外空间
| 算法 | 空间复杂度 | 空间用途 |
|---|---|---|
| 快速排序 | O(log n) | 递归栈空间 |
| 归并排序 | O(n) | 临时数组 |
| 计数排序 | O(n + k) | 计数数组 + 输出数组 |
| 桶排序 | O(n + k) | 桶数组 |
| 基数排序 | O(n + k) | 计数/桶数组 |
空间换时间的权衡:
1 | // 归并排序:用 O(n) 空间换取稳定性和 O(n log n) 保证 |
五、按算法思想分类
5.1 交换排序
思想:通过交换逆序元素来达到有序。
| 算法 | 交换方式 | 特点 |
|---|---|---|
| 冒泡排序 | 相邻元素交换 | 简单,可提前退出 |
| 快速排序 | 分区交换 | 高效,分治思想 |
1 | 冒泡排序的交换过程: |
5.2 选择排序
思想:每次选择最值元素放到已排序区。
| 算法 | 选择策略 | 特点 |
|---|---|---|
| 选择排序 | 线性扫描选最值 | 交换次数最少 |
| 堆排序 | 堆结构选最值 | O(n log n) 保证 |
1 | 选择排序 vs 堆排序的选择过程: |
5.3 插入排序
思想:将元素插入到已排序序列的合适位置。
| 算法 | 插入策略 | 特点 |
|---|---|---|
| 插入排序 | 逐个插入 | 对小数据最快 |
| 希尔排序 | 分组插入 | 突破 O(n²) |
1 | 插入排序过程: |
5.4 归并排序
思想:分治法,先分后合。
| 算法 | 分割策略 | 特点 |
|---|---|---|
| 归并排序 | 二分分割 | 稳定,O(n log n) 保证 |
| 快速排序 | 按基准分割 | 平均最快 |
1 | 归并排序的分治过程: |
5.5 分布排序
思想:根据数据的分布特征直接定位。
| 算法 | 分布方式 | 特点 |
|---|---|---|
| 计数排序 | 统计频率 | 整数,范围小 |
| 桶排序 | 均匀分桶 | 分布均匀 |
| 基数排序 | 按位分桶 | 固定位数 |
六、综合选择指南
6.1 决策树
1 | 开始选择排序算法 |
6.2 各语言/库的排序实现
| 语言/库 | 实现算法 | 备注 |
|---|---|---|
| Java Arrays.sort() | Dual-Pivot QuickSort(基本类型) Timsort(对象类型) |
基本类型追求速度 对象类型追求稳定 |
| Python sorted() | Timsort | 归并排序的优化版本 |
| C++ std::sort() | Introsort | 快速排序 + 堆排序混合 |
| C qsort() | QuickSort | 标准实现 |
| JavaScript sort() | QuickSort / Timsort | 引擎相关 |
| Go sort.Sort() | QuickSort | 标准实现 |
6.3 实际应用案例
1 | // 案例 1:大规模用户数据排序(需要稳定性) |
七、总结
7.1 分类总览表
| 分类维度 | 类别 | 包含算法 |
|---|---|---|
| 时间复杂度 | O(n²) | 冒泡、选择、插入 |
| O(n log n) | 快速、归并、堆、希尔 | |
| O(n) | 计数、桶、基数 | |
| 比较方式 | 比较排序 | 冒泡、选择、插入、希尔、快速、归并、堆 |
| 非比较排序 | 计数、桶、基数 | |
| 稳定性 | 稳定 | 冒泡、插入、归并、计数、桶、基数 |
| 不稳定 | 选择、希尔、快速、堆 | |
| 空间复杂度 | O(1) | 冒泡、选择、插入、希尔、堆 |
| O(log n) | 快速 | |
| O(n) | 归并 | |
| O(n + k) | 计数、桶、基数 | |
| 算法思想 | 交换 | 冒泡、快速 |
| 选择 | 选择、堆 | |
| 插入 | 插入、希尔 | |
| 归并 | 归并 | |
| 分布 | 计数、桶、基数 |
7.2 学习建议
- 入门阶段:理解冒泡、选择、插入三种基础排序
- 进阶阶段:掌握快速排序和归并排序的分治思想
- 深入阶段:理解堆结构和堆排序,学习希尔排序的增量序列
- 拓展阶段:了解线性时间排序的适用场景
7.3 面试重点
- 必会算法:快速排序(手写)、归并排序(手写)、堆排序(理解)
- 常考问题:
- 快速排序的优化(三数取中、小数组插入排序)
- 归并排序的迭代实现
- 堆排序的堆化过程
- 各种排序算法的稳定性和时间复杂度
- 实际场景下的算法选择
理解排序算法的分类,不仅有助于记忆各种算法的特点,更重要的是能够在实际开发中根据具体场景选择最合适的算法,达到最佳的性能表现。
如果您喜欢此博客或发现它对您有用,欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !