在编程中,排序算法是绕不开的基础技能 —— 不管是面试刷题,还是实际开发中的数据处理,都离不开它。但很多人只关注 “排序快不快”(时间复杂度),却忽略了一个关键问题:排序后相同大小的元素,相对位置会不会变? 这就是 “稳定性” 的核心意义。今天就用最通俗的语言,带你吃透 7 种常见排序算法的复杂度和稳定性,新手也能轻松看懂~

先搞懂:什么是排序的 “稳定性”?

首先要明确:排序的 “稳定性” 和时间复杂度的波动没关系!它的准确定义是:当数组中有多个值相等的元素时,排序后这些元素的原始相对位置是否保持不变。

举个生活化的例子:高考成绩排序时,若两名考生总分相同,就需要按语文成绩二次排序;如果语文成绩也相同,再看数学…… 这时候就要求排序算法是 “稳定” 的 —— 总分相同的考生,不能因为排序而打乱了原本的语文成绩排序逻辑。如果算法不稳定,可能会出现 “总分相同但语文差的考生排前面” 的情况,这显然不合理。

7 种常见排序算法:逐个拆解稳定性 + 复杂度

下面我们逐个分析最常用的 7 种排序算法,核心看 “怎么排”(核心逻辑)和 “稳不稳”(稳定性),再补充关键的复杂度数据。

1. 插入排序:“循序渐进的老实人”

核心逻辑:把数组分成 “已排序区间” 和 “未排序区间”,每次从无序列区间拿一个元素,往前插到有序区间的合适位置(就像玩扑克牌时,把新摸的牌插到手里的有序牌组里)。稳定性:稳定!因为插入时只会往前 “挪位置”,不会和其他元素交换,相同值的元素自然不会改变相对顺序。复杂度:时间复杂度 O (N²)(最坏 / 平均情况),空间复杂度 O (1)(原地排序,不用额外空间)。

2. 希尔排序:“跳着排的升级版插入排序”

核心逻辑:是插入排序的优化版 —— 先按 “步长” 把数组分成几组,每组内部做插入排序(比如步长 5 就每隔 5 个元素一组),然后逐渐缩小步长,最后步长 = 1 时就是普通插入排序。稳定性:不稳定!因为 “跳着排序” 的过程中,相同值的元素可能被分到不同组,交换时会打乱原始相对位置。复杂度:时间复杂度 O (N^1.3)(平均情况,比插入排序快),空间复杂度 O (1)(原地排序)。

3. 选择排序:“挑最小的往前提”

核心逻辑:每次从无序列区间里找到最小(或最大)的元素,和无序列区间的第一个元素交换位置,重复直到排序完成。稳定性:不稳定!举个直观例子:数组 [6, 6, 1, 1]第一次找最小元素是 1,和第一个 6 交换,变成 [1, 6, 6, 1]—— 原来两个 6 的相对位置被打乱了(原本第一个 6 在第二个 6 前面,现在还是,但再看第二次:找剩下的最小元素 1,和第二个位置的 6 交换,变成 [1, 1, 6, 6],但第一次交换已经破坏了 6 的顺序)。所以选择排序是不稳定的。复杂度:时间复杂度 O (N²)(最坏 / 平均 / 最好情况都一样,每次都要找最小值),空间复杂度 O (1)。

4. 堆排序:“基于堆的筛选排序”

核心逻辑:先把数组建成一个 “大顶堆”(或小顶堆),堆顶是最大(或最小)元素;然后把堆顶和数组最后一个元素交换,再对剩下的元素重新调整成堆,重复直到排序完成。稳定性:不稳定!堆顶元素和末尾元素交换时,会直接打乱相同值元素的相对位置,比如两个相同值的元素一个在堆顶附近,一个在末尾,交换后顺序必然改变。复杂度:时间复杂度 O (N logN)(堆的调整是 logN 级别,共需 N 次调整),空间复杂度 O (1)(原地排序)。

5. 冒泡排序:“相邻元素慢慢冒”

核心逻辑:从数组开头开始,依次比较相邻的两个元素,若顺序不对就交换(比如升序时,大的元素往后 “冒”);重复遍历数组,直到没有元素需要交换。稳定性:稳定!因为只交换 “相邻且顺序不对” 的元素,相同值的元素不会被交换,相对位置保持不变。复杂度:时间复杂度 O (N²)(最坏 / 平均情况),空间复杂度 O (1)(原地排序)。

6. 快速排序:“选基准分两边”

核心逻辑:选一个元素当 “基准值”,把数组分成两部分 —— 左边全是比基准值小的元素,右边全是比基准值大的元素;然后对左右两部分分别递归执行同样的操作,直到整个数组有序。稳定性:不稳定!举个例子:数组 [6, 6, 6],如果选第一个 6 当基准值,排序时可能会把第一个 6 交换到右边,导致三个 6 的原始顺序被打乱(原本第一个 6 在最前,排序后可能跑到最后)。这种 “跳跃式交换” 是不稳定的根源。复杂度:时间复杂度 O (N logN)(平均情况,最快的排序算法之一),最坏情况 O (N²)(比如基准值选得极差,数组已有序时);空间复杂度 O (logN)(递归调用栈的空间)。

7. 归并排序:“分而治之再合并”

核心逻辑:采用 “分治法”—— 先把数组分成左右两半,分别对两半递归排序,最后把两个有序的子数组合并成一个有序数组(合并时按顺序取两个子数组的元素,不交换)。稳定性:稳定!合并两个有序子数组时,只会按顺序 “取元素”,不会交换元素的位置,相同值的元素会保留原始相对顺序。复杂度:时间复杂度 O (N logN)(最坏 / 平均 / 最好情况都一样,稳定高效),空间复杂度 O (N)(需要额外空间存储合并后的数组)。

一张表总结:7 种排序算法核心信息

排序算法时间复杂度(平均)时间复杂度(最坏)空间复杂度稳定性插入排序O(N²)O(N²)O(1)稳定希尔排序O(N^1.3)O(N²)O(1)不稳定选择排序O(N²)O(N²)O(1)不稳定堆排序O(N logN)O(N logN)O(1)不稳定冒泡排序O(N²)O(N²)O(1)稳定快速排序O(N logN)O(N²)O(logN)不稳定归并排序O(N logN)O(N logN)O(N)稳定

最后:怎么选排序算法?

若需要稳定排序(比如多条件排序,如高考成绩、电商商品按价格 + 销量排序):优先选「归并排序」「插入排序」「冒泡排序」(归并排序效率最高,插入 / 冒泡适合小规模数据)。若追求排序效率(大规模数据):优先选「快速排序」「堆排序」「归并排序」(快速排序平均最快,堆排序省空间,归并排序稳定)。若数据量极小(比如几十条数据):「插入排序」「冒泡排序」就够了,实现简单且 overhead 小。若要求原地排序(不能用额外空间):排除「归并排序」,选「快速排序」「堆排序」「插入排序」等。

排序算法的选择没有绝对的 “最好”,关键看场景 —— 理解了稳定性和复杂度,就能根据需求精准选型啦!如果有疑问,欢迎在评论区交流~