【数据结构与算法】第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,应该选择什么算法?

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

相关推荐
unicrom_深圳市由你创科技2 小时前
做虚拟示波器这种实时波形显示的上位机,用什么语言?
c++·python·c#
SatVision炼金士2 小时前
合成孔径雷达干涉测量(InSAR)沉降监测算法体系
算法
wuweijianlove2 小时前
算法稳定性与数值误差传播研究的技术2
算法
无限进步_2 小时前
【C++】电话号码的字母组合:从有限处理到通用解法
开发语言·c++·ide·windows·git·github·visual studio
计算机安禾2 小时前
【数据结构与算法】第35篇:归并排序与基数排序
c语言·数据结构·vscode·算法·排序算法·哈希算法·visual studio
专注API从业者2 小时前
淘宝商品详情 API 与爬虫技术的边界:合法接入与反爬策略的技术博弈
大数据·数据结构·数据库·爬虫
C++ 老炮儿的技术栈2 小时前
GCC编译时无法向/tmp 目录写入临时汇编文件,因为设备空间不足,解决
linux·运维·开发语言·汇编·c++·git·qt
Captain_Data2 小时前
Python机器学习sklearn线性模型完整指南:LinearRegression/Ridge/Lasso详细代码注释
python·机器学习·数据分析·线性回归·sklearn
爱码小白2 小时前
MySQL 单表查询练习题汇总
数据库·python·算法