排序算法稳定性对比表:一文看懂哪些排序会打乱相等元素

排序算法稳定性到底是什么意思?

你有没有遇到过这种情况:给学生成绩排序,分数一样的人,原本按姓名字母排好的顺序,结果一排序,顺序全乱了?这很可能是因为你用的排序算法是“不稳定”的。

所谓排序算法的“稳定性”,指的是当两个元素的值相等时,排序后它们的相对位置是否保持不变。比如有两个学生,分数都是90分,A同学在B同学前面,稳定排序后,A依然在B前面;而不稳定排序可能就会把B排到A前面。

常见排序算法稳定性一览

下面这张对比表列出了常用排序算法的稳定性、时间复杂度和典型应用场景:

排序算法 是否稳定 平均时间复杂度 最坏时间复杂度 空间复杂度
冒泡排序 O(n²) O(n²) O(1)
插入排序 O(n²) O(n²) O(1)
归并排序 O(n log n) O(n log n) O(n)
计数排序 O(n + k) O(n + k) O(k)
基数排序 O(nk) O(nk) O(n + k)
选择排序 O(n²) O(n²) O(1)
快速排序 O(n log n) O(n²) O(log n)
堆排序 O(n log n) O(n log n) O(1)

为什么稳定性有时候很重要?

举个实际例子:你在处理电商订单,先按下单时间排序好,然后想按金额从高到低排。如果用了不稳定的排序,相同金额的订单可能会把原来的时间顺序打乱,导致分析出错。

再比如,你导出了一份员工名单,已经按部门排好了序,现在想按工号排序,但希望同部门的人尽量保持原有次序。这时候就得选稳定的排序算法。

怎么自己实现一个稳定排序?

如果你手写的排序不能保证稳定性,一个取巧的办法是:在排序键上加上原始索引。比如对数组排序时,把每个元素变成 [value, original_index],排序时如果值相等,就比较索引。这样即使算法本身不稳定,也能靠索引“强制”稳定。

例如 Python 中可以用:

sorted(data, key=lambda x: (x.value, data.index(x)))

不过更推荐直接使用内置的稳定排序,比如 Python 的 sorted()list.sort() 都是基于归并排序的思想,是稳定的。

快速排序能变稳定吗?

标准的快速排序是不稳定的,因为它在分区过程中可能把相等元素的相对位置交换。但如果额外使用更多空间,比如把相等元素单独存起来,是可以改造成稳定的,只是代价高,一般没人这么做。

相比之下,归并排序天生稳定,因为合并两个有序数组时,相等元素优先取左边的,自然就保留了原顺序。