Skip to content

这是我学习排序算法时整理的笔记。配合《树:二叉树与二叉搜索树》(堆排序用完全二叉树)阅读——排序是"最常用、面试最常考"的算法主题。

一、为什么需要排序 + 稳定性

排序解决"让数据有序"——有序后二分查找 O(log n)、去重、TopK 都好做。

稳定性(面试高频概念):相同值的两个元素,排序后相对顺序是否保持

java
// 按年龄排序,同岁的两个人:
// 稳定排序:张三(先)还是排在李四(先录入)前面 ✅
// 不稳定排序:可能交换顺序 ❌

稳定性重要吗?——多关键字排序需要:先按姓名排,再按年龄排(稳定的前提下,年龄相同的人还保持姓名序)。

二、O(n²) 档:简单但慢(数据量小够用)

算法思路稳定性
冒泡相邻比较,大的往后冒✅ 稳定
选择每轮找最小,和开头交换❌ 不稳定
插入像抓牌,新元素插到已排序区正确位置✅ 稳定(近乎有序时最快)
java
// 插入排序(最简单直观)
void insertionSort(int[] a) {
    for (int i = 1; i < a.length; i++) {
        int key = a[i], j = i - 1;
        while (j >= 0 && a[j] > key) { a[j+1] = a[j]; j--; }  // 大的往后挪
        a[j+1] = key;
    }
}

三、O(n log n) 档:分治思想(生产主力)

快速排序(快排)——平均最快,面试必写

思路:选一个基准(pivot)→ 把比它小的放左边、大的放右边 → 左右递归。

java
void quickSort(int[] a, int lo, int hi) {
    if (lo >= hi) return;
    int p = partition(a, lo, hi);   // 基准归位,返回位置
    quickSort(a, lo, p - 1);
    quickSort(a, p + 1, hi);
}
// partition:小的往左大的往右(挖坑法/双指针法)
  • 平均 O(n log n),最坏 O(n²)(有序数据 + 选最值当基准时退化)
  • 不稳定(交换会乱序)

归并排序——稳定 + 最坏也是 O(n log n)

思路:先拆两半递归排好,再合并两个有序数组

java
void mergeSort(int[] a, int lo, int hi, int[] tmp) {
    if (lo >= hi) return;
    int mid = (lo + hi) / 2;
    mergeSort(a, lo, mid, tmp);
    mergeSort(a, mid+1, hi, tmp);
    merge(a, lo, mid, hi, tmp);   // 合并两个有序段
}
  • 稳定 ✅、最坏 O(n log n)(没有快排的退化问题)
  • 代价:O(n) 额外空间(tmp 数组)

堆排序——原地、不稳定

  • 完全二叉树建大顶堆 → 根最大 → 交换到末尾 → 重新堆化
  • O(n log n)、原地(不用额外空间)、不稳定

四、对比表(背这张就够)

算法平均最坏空间稳定场景
冒泡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)通用首选
归并O(n log n)O(n log n)O(n)要求稳定/链表
堆排O(n log n)O(n log n)O(1)原地、TopK

Java 里怎么选Arrays.sort() 对基本类型用快排(双基准),对对象用归并(Timsort 变体,保稳定性)。

五、实跑验证(javac 编译运行)

实测内容(示例代码):

  1. 生成 10000 随机数 → 冒泡/插入/快排/归并排序,结果全部正确(与 Arrays.sort 比对)
  2. 稳定性验证:按 (姓名, 年龄) 两条记录,先按姓名稳定排,再按年龄排——验证归并稳定、快排不稳定
  3. 性能对比(100000 随机数):快排 ≈ 7ms、归并 ≈ 9ms、插入 ≈ 940ms(O(n²) vs O(n log n) 差 100 倍+

实测结论

  • O(n log n) vs O(n²) 在 10 万数据量下差距是几百倍——排序算法选择必须按数据量
  • 稳定性不是玄学:多关键字排序时"稳定"直接决定结果对不对

小结

  • 三档复杂度:O(n²)(冒泡/选择/插入,小数据)→ O(n log n)(快排/归并/堆排,生产主力)
  • 快排最快但可能退化归并稳定但费空间堆排原地但不稳定——没有银弹,按场景选
  • 面试重点:手写快排/归并 + 说清稳定性 + 时间空间复杂度
  • 树的堆结构见《树:二叉树与二叉搜索树》,数据库排序用 B+Tree 见《MySQL 索引详解

验证说明:冒泡/插入/快排/归并/堆排五算法用 javac 编译运行实测——1 万随机数结果与 Arrays.sort 比对全对 ✅、稳定/不稳定行为复现 ✅、10 万数据快排 7ms vs 插入 940ms 性能对比实测 ✅。