摘要:冒泡排序、选择排序、插入排序、快速排序,光看文字描述总觉得似懂非懂?这篇文章用可交互的动态演示带你逐步看清每种排序算法的执行过程,配合最简 Python 代码,一次搞定排序入门。
学算法这些年,我发现一个规律:排序这块内容,看完文字大家都说「懂了」,一上机写代码就露馅。
原因也简单——光盯着文字描述脑补执行过程,和真正理解「每一步到底在干嘛」之间,差了一整条鸿沟。就好比看菜谱和下厨房的区别,说多少遍「中火翻炒三分钟」,不如亲眼看一遍来得直观。
所以这篇文章换个路子:每种排序算法除了讲原理、贴代码之外,都配了一个可交互的动态演示。大家可以自己点按钮、调数据、逐步执行,亲眼看到数组是怎么一步步变有序的。
废话不多说,我们从最基础的开始。
什么是排序
「排序」说白了就是把一堆杂乱无章的数据按照某种规则(通常是从小到大)重新排列。这件事看起来简单,却是计算机科学里研究得最透彻的问题之一。
为什么这么重要?因为几乎所有复杂操作的前提都是「数据已排好序」——二分查找需要有序数组,数据库索引背后是排序,甚至你手机通讯录的字母分组本质上也是排序。
衡量一个排序算法好不好,主要看两个指标:
- 时间复杂度:排序需要「比较」和「交换」多少次?用大 O 记号表示,比如 O(n²) 意味着数据量翻倍,耗时大约翻四倍
- 稳定性:两个值相同的元素,排完序后相对位置会不会变?不变就是「稳定」的
接下来我们逐个看四种经典排序。每种都有演示区,建议大家边看边玩。
冒泡排序——最直觉的「冒泡泡」
冒泡排序的思路非常朴素:从头到尾依次比较相邻两个元素,如果前面的比后面的大,就交换。这样一轮下来,最大的那个数就像气泡一样「浮」到了最右边。重复这个过程,直到整个数组有序。
Python 代码如下:
def 冒泡排序(数组):
长度 = len(数组) # 获取数组长度
for i in range(长度 - 1): # 外层循环:总共需要 n-1 轮
for j in range(长度 - 1 - i): # 内层循环:每轮比较相邻元素
if 数组[j] > 数组[j + 1]: # 如果前面的比后面的大
数组[j], 数组[j + 1] = 数组[j + 1], 数组[j] # 交换位置
return 数组
# 测试代码
测试数据 = [38, 27, 43, 10, 82, 15]
print(f"排序前: {测试数据}")
结果 = 冒泡排序(测试数据)
print(f"排序后: {结果}")
两层循环,外层控制轮数,内层做相邻比较。每轮结束后,末尾又多一个「已归位」的元素。
💡 提示
冒泡排序的时间复杂度是 O(n²),属于最慢的一档。但它胜在简单好理解,非常适合入门学习。实际项目中基本不会用它处理大规模数据。
选择排序——每轮「选最小」
选择排序的思路也很直白:每轮从「未排序区间」里找到最小的那个元素,然后把它放到「已排序区间」的末尾。
想象你在整理一副扑克牌,但方法比较笨——每次都把剩余牌里最小的那张抽出来,放到左手边。
Python 代码:
def 选择排序(数组):
长度 = len(数组) # 获取数组长度
for i in range(长度 - 1): # 外层循环:逐个确定每个位置的值
最小位置 = i # 假设当前位置就是最小值
for j in range(i + 1, 长度): # 在未排序区间内查找真正的最小值
if 数组[j] < 数组[最小位置]: # 找到更小的,更新记录
最小位置 = j
数组[i], 数组[最小位置] = 数组[最小位置], 数组[i] # 把最小值换到前面
return 数组
# 测试代码
测试数据 = [38, 27, 43, 10, 82, 15]
print(f"排序前: {测试数据}")
结果 = 选择排序(测试数据)
print(f"排序后: {结果}")
注意看演示区里红色高亮的柱子,那就是当前轮次扫描到的「最小值」。一轮扫描完后,它会被换到队首。
插入排序——扑克牌式整理
这回想象你在打扑克——每摸到一张新牌,你会在手中已有的牌里找到合适的位置,把它「插」进去。插入排序就是这个思路。
从第二个元素开始,每次取出一个「待插入」的元素,然后在它前面的已排序序列中从后往前比较,找到合适的位置插入。
Python 代码:
def 插入排序(数组):
for i in range(1, len(数组)): # 从第二个元素开始遍历
待插入值 = 数组[i] # 取出当前要插入的元素
j = i - 1 # 从已排序区间的最右边开始往左找
while j >= 0 and 数组[j] > 待插入值: # 如果前面的元素比待插入值大
数组[j + 1] = 数组[j] # 把大的元素往右挪一位
j -= 1 # 继续往左找
数组[j + 1] = 待插入值 # 找到位置,插入
return 数组
# 测试代码
测试数据 = [38, 27, 43, 10, 82, 15]
print(f"排序前: {测试数据}")
结果 = 插入排序(测试数据)
print(f"排序后: {结果}")
在下面的演示中,青色浮起的柱子就是当前要插入的元素。看它怎么找位置、怎么落下去。
💡 提示
插入排序在数据「几乎有序」的情况下效率非常高,接近 O(n)。Python 内置的 sorted() 函数底层用的 Timsort 算法,核心思想就是把插入排序和归并排序结合了起来。
快速排序——分而治之
快速排序是实际开发中用得最多的排序算法之一。思路是「分治」:选一个「基准值」,把数组分成两部分——比基准小的放左边,比基准大的放右边——然后对左右两部分分别递归地做同样的事。
Python 代码:
def 快速排序(数组):
if len(数组) <= 1: # 基准情形:只有 0 或1 个元素,无需排序
return 数组
基准值 = 数组[0] # 选第一个元素作为基准
左半 = [x for x in 数组[1:] if x <= 基准值] # 比基准值小或等的放左边
右半 = [x for x in 数组[1:] if x > 基准值] # 比基准值大的放右边
return 快速排序(左半) + [基准值] + 快速排序(右半) # 递归排序左右两半,拼起来
# 测试代码
测试数据 = [38, 27, 43, 10, 82, 15]
print(f"排序前: {测试数据}")
结果 = 快速排序(测试数据)
print(f"排序后: {结果}")
这段代码比较简洁好懂,但为了方便演示,下面的动画用的是原地分区的版本。紫色柱子是基准值,注意看元素怎么「各归各位」。
四种排序一览对比
把前面讲的四种排序放在一起比较一下:
| 算法 | 平均时间 | 最坏时间 | 空间 | 稳定 | 一句话特点 |
|---|---|---|---|---|---|
| 冒泡排序 | 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(log n) | ❌ | 综合性能最优的通用排序 |
怎么选?简单粗暴的经验法则:
- 数据量小于 50,插入排序就够了
- 通用场景优先快速排序
- 要求稳定性,考虑归并排序(这篇没讲,以后再说)
- 冒泡和选择排序比较适合考试和入门练手,实际项目中基本用不到
四种排序同场竞速
光看理论复杂度可能还没什么直观感受。我们让四种排序算法在同一组数据上同时开跑,看看到底谁最快完成排序——比较次数和交换次数一目了然。
建议把数据量调到 12 以上再看效果,差距会更明显。
总结
排序算法是编程的基本功。今天讲的这四种虽然原理各异,但核心思想无非就是「比较」和「交换」(或移动)。
如果你之前只是背了个概念就去考试,建议回到演示区多玩几遍。自己输入几组数据,单步执行看看每一步发生了什么——比刷十道选择题有效。
下一篇我们聊查找算法,同样有交互演示。顺序查找和二分查找放在一起「赛跑」,速度差距一目了然。
加载评论中……