【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景

目录

一、十大排序算法总览

二、稳定性详解

[2.1 什么是稳定性](#2.1 什么是稳定性)

[2.2 什么时候需要稳定性](#2.2 什么时候需要稳定性)

[2.3 各算法稳定性原因](#2.3 各算法稳定性原因)

三、时间复杂度分析

[3.1 增长曲线对比](#3.1 增长曲线对比)

[3.2 实际运行时间参考(n=50000随机数据)](#3.2 实际运行时间参考(n=50000随机数据))

四、空间复杂度分析

五、场景选型指南

[5.1 数据规模小(n < 1000)](#5.1 数据规模小(n < 1000))

[5.2 数据规模中等(1000 < n < 50000)](#5.2 数据规模中等(1000 < n < 50000))

[5.3 数据规模大(n > 50000)](#5.3 数据规模大(n > 50000))

[5.4 特殊场景](#5.4 特殊场景)

六、混合排序(工业级实现)

七、面试高频考点

[7.1 手写代码常考](#7.1 手写代码常考)

[7.2 概念常考](#7.2 概念常考)

八、快速对比卡片

[O(n²) 级(适合小数据)](#O(n²) 级(适合小数据))

[O(n log n) 级(工业标准)](#O(n log n) 级(工业标准))

[O(n) 级(特殊场景)](#O(n) 级(特殊场景))

九、小结

十、思考题


一、十大排序算法总览

算法 分类 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性
冒泡排序 交换 O(n²) O(n²) O(n) O(1) 稳定
快速排序 交换 O(n log n) O(n²) O(n log n) O(log n) 不稳定
直接插入排序 插入 O(n²) O(n²) O(n) O(1) 稳定
希尔排序 插入 O(n^1.3) O(n²) O(n log n) O(1) 不稳定
简单选择排序 选择 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) 稳定
计数排序 非比较 O(n+k) O(n+k) O(n+k) O(k) 稳定
桶排序 非比较 O(n+k) O(n²) O(n) O(n+k) 稳定
基数排序 非比较 O(d×(n+k)) O(d×(n+k)) O(d×(n+k)) O(n+k) 稳定

k表示数据范围(如计数排序中最大值与最小值的差),d表示位数(基数排序中)


二、稳定性详解

2.1 什么是稳定性

稳定排序:相等的元素在排序前后相对位置不变。

text

复制代码
原数组:[(2,A), (1,B), (2,C)]  // 数字为键,字母为附加信息
稳定排序后:[(1,B), (2,A), (2,C)]  // 两个2的相对顺序不变
不稳定排序后:[(1,B), (2,C), (2,A)]  // 顺序可能改变

2.2 什么时候需要稳定性

场景 说明
多关键字排序 先按A排序,再按B排序,稳定性能保留A的顺序
业务数据排序 用户列表按时间排序后,再按地区排序,同地区内时间顺序不变
多次排序 后续排序需要保留之前的排序结果

2.3 各算法稳定性原因

算法 稳定性 原因
冒泡排序 稳定 相邻交换,相等时不交换
插入排序 稳定 相等元素不移动
归并排序 稳定 合并时相等取左边
计数/桶/基数排序 稳定 按顺序分配收集
快速排序 不稳定 分区时可能跨过相等元素
希尔排序 不稳定 分组排序,不同组间顺序打乱
选择排序 不稳定 交换可能把后面的相等元素换到前面
堆排序 不稳定 堆化过程无法保证顺序

三、时间复杂度分析

3.1 增长曲线对比

text

复制代码
n=1000时各算法大致比较次数:
O(n):        1000
O(n log n):  1000 × 10 = 10000
O(n²):       1000000

n=1000000时:
O(n):        1000000
O(n log n):  1000000 × 20 = 20000000
O(n²):       1000000000000(无法接受)

3.2 实际运行时间参考(n=50000随机数据)

算法 时间(ms) 量级
快速排序 ~8 最快
归并排序 ~12 很快
堆排序 ~15 很快
希尔排序 ~20 中等
基数排序 ~25 中等(依赖数据范围)
冒泡/插入/选择 ~2000+ 慢

注:数据因机器而异,但相对关系类似


四、空间复杂度分析

算法 额外空间 是否原地
冒泡/插入/选择/希尔/快排/堆排 O(1) 是
归并排序 O(n) 否
计数排序 O(k) 否
桶排序 O(n+k) 否
基数排序 O(n+k) 否

内存受限场景:优先选择原地排序算法。


五、场景选型指南

5.1 数据规模小(n < 1000)

场景 推荐 理由
基本有序 插入排序 时间复杂度O(n)
无特殊要求 插入排序 代码简单,稳定性好
需要稳定 冒泡/插入 稳定且简单

5.2 数据规模中等(1000 < n < 50000)

场景 推荐 理由
通用场景 快速排序 平均最快
需要稳定 归并排序 O(n log n)稳定
内存紧张 堆排序 原地O(n log n)
数据基本有序 插入排序 退化为O(n)

5.3 数据规模大(n > 50000)

场景 推荐 理由
通用场景 快速排序 常数因子小
需要稳定 归并排序 唯一稳定O(n log n)
内存极度紧张 堆排序 原地排序
数据范围小 计数/基数排序 可达到O(n)

5.4 特殊场景

场景 推荐 理由
整数且范围小 计数排序 O(n+k),k很小时极快
整数且范围大 基数排序 O(d×n),d通常为常数
浮点数/字符串 快速排序/归并排序 通用比较排序
外部排序(磁盘) 归并排序 适合多路归并
系统级排序 混合排序(如introsort) 快排+堆排+插入

六、混合排序(工业级实现)

实际的标准库排序(C++ STL sort、Java Arrays.sort)通常采用混合策略:

  1. 数据量大:用快速排序

  2. 递归深度过大:切换为堆排序(防止最坏情况)

  3. 分区后子数组小:用插入排序(小数组效率高)

c

复制代码
// 混合排序伪代码
void hybridSort(int arr[], int left, int right) {
    if (right - left < THRESHOLD) {
        insertionSort(arr, left, right);
        return;
    }
    if (depth > MAX_DEPTH) {
        heapSort(arr, left, right);
        return;
    }
    int pivot = partition(arr, left, right);
    hybridSort(arr, left, pivot - 1, depth + 1);
    hybridSort(arr, pivot + 1, right, depth + 1);
}

七、面试高频考点

7.1 手写代码常考

算法 考察频率
快速排序 ⭐⭐⭐⭐⭐
归并排序 ⭐⭐⭐⭐
堆排序 ⭐⭐⭐⭐
插入排序 ⭐⭐⭐
冒泡排序 ⭐⭐

7.2 概念常考

  • 稳定性的含义及判断

  • 各算法的时间/空间复杂度

  • 为什么快速排序实际最快(缓存友好、常数因子小)

  • 堆排序为什么不稳定

  • 计数排序的局限性


八、快速对比卡片

O(n²) 级(适合小数据)

算法 一句话记忆
冒泡 两两交换,大的下沉
选择 每次选最小的放前面
插入 像打牌,插到合适位置

O(n log n) 级(工业标准)

算法 一句话记忆
快速 选基准,分两边,递归
归并 先分后合,需要额外空间
堆 建堆,取堆顶,再调整
希尔 分组插入,逐步缩小间隔

O(n) 级(特殊场景)

算法 一句话记忆
计数 统计频率,直接输出
桶 分到桶里,各桶排序
基数 按位分配,逐位收集

九、小结

场景 首选 备选
通用排序 快速排序 归并排序
需要稳定 归并排序 插入排序(小数据)
内存紧张 堆排序 快速排序
整数范围小 计数排序 基数排序
数据基本有序 插入排序 冒泡排序
数据量极小 插入排序 冒泡排序
外部排序 归并排序 -

选型口诀:

  • 小数据用插入

  • 要稳定用归并

  • 内存紧用堆排

  • 通用场景快排

  • 整数范围小计数


十、思考题

  1. 为什么快速排序的实际表现通常优于归并排序,即使它们的时间复杂度都是 O(n log n)?

  2. 堆排序是原地排序,为什么实际应用中不如快速排序常用?

  3. 计数排序和基数排序的时间复杂度都是 O(n),它们有什么局限性?

  4. 如果你需要排序一个包含100万个整数的文件,内存只有50MB,应该选择什么算法?

欢迎在评论区讨论你的答案。

相关推荐
aqiu1111111 小时前
【C++算法打怪专栏】8. 字符串转换整数 (atoi)
开发语言·c++·算法
h39746 小时前
DirectShow过滤器开发-视频源过滤器
c++·windows·音视频·directshow
程序猿编码7 小时前
榨干 RTX5090 算力!Qwen3 专用单卡推理引擎,手写C++/CUDA 算子实现 MTP 推测解码
开发语言·c++·大模型推理·qwen3·多模态推理
西飘客8 小时前
vs 怎么根据dll 生成lib文件
c++·qt
程序员老陆9 小时前
深入理解 C++ thread_local:线程私有存储的正确打开方式
开发语言·c++·程序设计
沐晓时光9 小时前
C语言入门,深入理解指针(3)
c语言·开发语言
信奥卷王9 小时前
[GESP202609 六级] 分树规划
算法
夏幻灵9 小时前
LeetCode 21:合并两个有序链表——双指针与哑节点详解
算法·leetcode·链表
SuperByteMaster11 小时前
wfi进入之前没有清除basepri, 导致中断优先级不够,wfi 退不出来
c语言