稳定排序算法详解

1. 概述

本文将介绍什么是稳定排序算法,以及它在实际开发中的应用场景。同时我们也会分析排序稳定性在哪些场景下是必须考虑的,哪些场景下可以忽略。

2. 排序算法中的稳定性

排序算法的“稳定性”指的是:当待排序的数据中存在多个相等的元素时,排序算法是否会保持它们原有的相对顺序。

✅ 稳定排序:相等元素在排序后的相对顺序不变

❌ 不稳定排序:相等元素之间的顺序可能被打乱

举个例子:

我们有数组 A = [5, 8, 9, 8, 3],其中有两个 8。如果我们能通过排序算法得到 [3, 5, 8, 8, 9],并且两个 8 的相对顺序没有变化,那这个排序算法就是稳定的。

下面这张图可以更直观地帮助我们理解:

如图所示,稳定排序保留了两个相同值(8)的原始顺序,而不稳定排序可能会打乱它们的顺序。

3. 稳定性何时重要?

3.1 稳定性适用的场景

并不是所有排序场景都需要稳定性。只有在以下情况下,稳定性才显得重要:

数据中存在可区分的相等元素

数据本身已经部分有序,排序操作需要保留原有顺序

举个实际例子:假设我们正在统计一篇文章中各个单词的出现频率,并希望按频率排序。如果多个单词频率相同,我们希望它们按字母顺序排列。

3.2 实例说明:单词频率排序

第一步:统计单词频率

输入文本:

how much wood would woodchuck chuck if woodchuck could chuck wood

输出结果:

how 1

much 1

wood 2

would 1

woodchuck 2

chuck 2

if 1

could 1

第二步:先按字母顺序排序,再按频率排序

第一次排序(按字母顺序):

(chuck, 2)

(could, 1)

(how, 1)

(if, 1)

(much, 1)

(wood, 2)

(woodchuck, 2)

(would, 1)

第二次排序(按频率,使用不稳定排序):

(wood, 2)

(chuck, 2)

(woodchuck, 2)

(could, 1)

(how, 1)

(if, 1)

(would, 1)

(much, 1)

⚠️ 问题来了:使用不稳定排序后,三个频率为 2 的单词顺序被打乱了,不再保持字母顺序。

如果我们使用稳定排序来按频率排序,结果如下:

(chuck, 2)

(wood, 2)

(woodchuck, 2)

(could, 1)

(how, 1)

(if, 1)

(much, 1)

(would, 1)

✅ 可以看到,频率相同的单词之间仍然保持了字母顺序。

3.3 基数排序(Radix Sort)与稳定性

基数排序(Radix Sort) 是一种非比较型排序算法,依赖于稳定排序的子过程(通常是计数排序)。

它的工作流程如下:

for 每一位数字 k(从最低位到最高位):

使用计数排序对当前位进行排序

✅ 正是因为每轮排序都使用了稳定的子排序算法(如计数排序),Radix Sort 才能保证最终结果的正确性。比如在对十位数排序时,虽然 9881 向下移动了,但它仍然保持在 9888 的前面。

4. 常见排序算法的稳定性分类

下面是一些常见排序算法的稳定性分类:

排序算法

稳定性

Merge Sort

✅ 稳定

Timsort

✅ 稳定

Counting Sort

✅ 稳定

Insertion Sort

✅ 稳定

Bubble Sort

✅ 稳定

Quicksort

❌ 不稳定

Heapsort

❌ 不稳定

Selection Sort

❌ 不稳定

⚠️ 注意:某些不稳定排序算法也可以通过修改实现稳定排序,比如在 Quicksort 中使用额外信息来记录原始顺序。

5. 总结

✅ 稳定排序保留相等元素之间的原始顺序

❌ 不稳定排序可能会打乱这些元素的顺序

📌 在需要保留数据原有顺序的场景中(如多字段排序、Radix Sort),稳定性非常重要

🧠 多数排序算法的稳定性是其固有属性,但也有些可以通过改造实现稳定

理解排序算法的稳定性,有助于我们在实际开发中选择合适的排序策略,避免“踩坑”。

Back to top: