1. 为什么你需要理解排序算法?
如果你刚开始学编程,可能会觉得排序算法离你很远,心想:“反正有现成的 sort() 函数,我干嘛要学这个?” 我刚开始也是这么想的,直到有一次面试,面试官让我在白板上手写一个快速排序,我当场就懵了。后来在实际工作中,我也踩过坑:有一次处理一个几万条用户行为日志,直接用语言内置的排序函数,结果页面卡了十几秒才响应。排查后发现,数据几乎已经是倒序排列的,而内置的排序算法在这种“最坏情况”下性能急剧下降。如果我当时懂点排序算法的原理,换个更合适的算法或者提前做点预处理,问题早就解决了。
所以,理解排序算法绝不是纸上谈兵。它至少有三个实实在在的好处:
- 应对面试与笔试:这是最直接的,算法是技术面试的常客。
- 写出更高效的代码:知道不同算法的特性,你就能在特定场景下选择最优解。比如,对于几乎有序的小数组,插入排序可能比快速排序更快。
- 理解更复杂系统的基础:很多高级数据结构和系统设计(比如数据库索引、负载均衡)的底层思想,都脱胎于这些经典的排序逻辑。
这篇文章,我就用动态图解+实战代码的方式,带你彻底搞懂十大经典排序算法。我们不只讲“怎么做”,更重点讲“为什么这么做”,以及“什么时候用”。我会用最直白的语言和生活中的类比,让你像看故事一样,轻松理解每个算法的核心。
为了方便你对比学习,我们先来看一个总览表:
| 排序算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 是否稳定 | 核心思想 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 相邻比较,大数沉底 |
| 选择排序 | O(n²) | O(n²) | O(1) | 不稳定 | 每次选最小,放到前面 |
| 插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 整理扑克牌,逐个插入 |
| 希尔排序 | O(n log n) ~ O(n²) | O(n²) | O(1) | 不稳定 | 插入排序的升级,先宏观调整 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 | 分而治之,先分后合 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 | 分而治之,找基准分区 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 | 利用堆结构选择最值 |
| 计数排序 | O(n + k) | O(n + k) | O(n + k) | 稳定 | 非比较,统计元素出现次数 |
| 桶排序 | O(n + k) | O(n²) | O(n + k) | 稳定 | 数据分桶,桶内排序 |
| 基数排序 | O(n * k) | O(n * k) | O(n + k) | 稳定 | 按位排序,从低位到高位 |
提示:
n代表数据量,k代表数据的范围(如计数排序)或最大位数(如基数排序)。稳定排序是指如果两个相等的元素在排序前后的相对位置不变。
下面,我们就从最直观、最好理解的三个基础算法开始。
2. 冒泡排序:像气泡一样上浮
想象一下水底的气泡,大的气泡会更快地浮到水面。冒泡排序就是这个原理:每一轮遍历,都把当前未排序部分中最大的元素“冒泡”到最后面。
2.1 算法核心与动态图解
它的过程非常直观:
- 从数组的第一个元素开始,比较相邻的两个元素。
- 如果顺序不对(比如前一个比后一个大),就交换它们。
- 对每一对相邻元素重复这个过程。这样,第一轮结束后,最大的元素就像气泡一样“浮”到了数组末尾。
- 忽略最后一个已经排好的元素,对剩下的元素重复步骤1-3,直到整个数组有序。
我们用一个数组 [5, 3, 8, 6, 4] 来演示一下动态过程:
第一轮冒泡:
- 比较 5 和 3:5 > 3,交换 →
[3, 5, 8, 6, 4] - 比较 5 和 8:5 < 8,不交换 →
[3, 5, 8, 6, 4] - 比较 8 和 6:8 > 6,交换 →
[3, 5, 6, 8, 4] - 比较 8 和 4:8 > 4,交换 →
[3, 5, 6, 4, 8]第一轮结束,最大的数字 8 已经就位。
第二轮冒泡(在 [3, 5, 6, 4] 中进行):
- 比较 3 和 5:不交换
- 比较 5 和 6:不交换
- 比较 6 和 4:交换 →
[3, 5, 4, 6, 8]第二轮结束,第二大的数字 6 就位。
如此反复,直到所有元素有序。你会发现,每一轮都会把一个当前最大的数推到它最终的位置。
2.2 代码实战与优化
基础版本的Python实现非常简单:
def bubble_sort_basic(arr):
n = len(arr)
# 外层循环控制轮数,n个元素需要n-1轮
for i in range(n - 1):
# 内层循环进行相邻比较,每轮结束后,末尾i个元素已有序
for j in range(n - 1 - i):
if arr[j] > arr[j + 1]:
# 交换
arr[j], arr[j + 1] = arr[j +


被折叠的 条评论
为什么被折叠?



