数据结构第 7 章:排序——七种内部排序的复杂度与稳定性

写于 2026-11-01

排序这一章的考点非常集中:每种算法的最好/平均/最坏时间复杂度、空间复杂度、稳定性、适用场景。八种算法看起来多,但真正需要理解透的只有三个:快排的划分、堆的调整、归并的合并。其余几种都是对照物。

一、先定义两个评价维度

内部排序 vs 外部排序:数据是否全部装入内存。

稳定性:若待排序列中两个相等元素 a 原本在 b 前面,排序后 a 仍在 b 前面,则算法稳定。稳定性只在"按多个关键字排序"时才有实际意义(比如先按姓名排、再按成绩排,稳定的算法能保留第一次的顺序)。

不稳定的四种:快速、希尔、选择、堆。口诀"快些选堆"(快=快排,些=希尔,选=选择,堆=堆排)。其余四种(插入、冒泡、归并、基数)都稳定。

二、插入类

直接插入排序:把第 i 个元素插入到前面已排好的序列里,边比较边后移。

  • 最好 O(n)(原本基本有序,几乎不移动)
  • 平均 / 最坏 O(n²)
  • 空间 O(1),稳定
  • 特点:基本有序时效率极高,常作为快排小区间(长度 < 10)的收尾方案(因为对小数组插入排序常数更小)

希尔排序(缩小增量排序):按增量 d 分组做插入排序,d 逐步缩小到 1。

  • 平均 O(n^1.3) 左右(取决于增量序列,没有统一的精确结论,考试通常写 O(n log n)~O(n²) 之间或直接写增量相关)
  • 空间 O(1),不稳定(相同元素可能被分到不同组)
  • 增量序列的最后一个必须是 1

三、交换类

冒泡排序:相邻两两比较,大的往后换。

  • 最好 O(n)(加"本趟有无交换"的提前终止标志时)
  • 平均 / 最坏 O(n²)
  • 空间 O(1),稳定

快速排序:选一个基准(pivot),把序列划分成"小于基准"和"大于基准"两部分,递归处理两侧。

  • 平均 O(n log n),最坏 O(n²)(序列已基本有序且取第一个元素为基准时,退化成单支,递归深度 n)
  • 空间 O(log n)(递归栈),最坏 O(n)
  • 不稳定
  • 优化手段:随机选基准、三数取中、小区间改用插入排序
  • 快排是平均性能最好的内部排序,也是实际库函数(qsort)的基础

四、选择类

简单选择排序:每趟从未排序列中选出最小的放到前面。

  • 最好 / 平均 / 最坏都是 O(n²)(无论数据如何,比较次数固定 n(n−1)/2)
  • 空间 O(1),不稳定(交换会跨过相等元素)
  • 交换次数少(最多 n−1 次),这是它相对冒泡的唯一优势

堆排序:把序列建成大顶堆(或小顶堆),每次取堆顶与末尾交换,然后向下调整。

  • 建初堆 O(n),每次调整 O(log n),共 n 次 → 总体 O(n log n)(最好/平均/最坏都是)
  • 空间 O(1)(原地),不稳定
  • 特点:空间最省的 O(n log n) 排序。适合内存紧张、又不允许递归深度的场景(找前 k 大是它的经典应用)
  • 下标从 1 开始时:结点 i 的孩子是 2i、2i+1;从 0 开始时是 2i+1、2i+2

五、归并类

二路归并排序:把序列两两分组排序,再两两合并,直到整体有序。

  • 最好 / 平均 / 最坏都是 O(n log n)
  • 空间 O(n)(需要辅助数组)
  • 稳定
  • 特点:唯一"稳定 + 保证 O(n log n)"的排序,适合外部排序(多路归并就是文件排序的核心)和对稳定性有要求的场景(如 Java 的 Collections.sort)

六、非比较类

基数排序:按位(个位、十位、百位…)依次做"分配—收集",每趟用桶(队列)。

  • 复杂度 O(d(n+r)),d 是位数,r 是基数(十进制为 10)
  • 空间 O(n + r),稳定(分配收集必须稳定,否则前面几趟白做)
  • 不基于比较,所以不受"比较排序下界 O(n log n)"的限制

七、七种排序总表(这张表要能默写)

算法 最好 平均 最坏 空间 稳定性 适用
直接插入 O(n) O(n²) O(n²) O(1) 稳定 基本有序、小数组
希尔 — ≈O(n^1.3) O(n²) O(1) 不稳定 中等规模、原地
冒泡 O(n) O(n²) O(n²) O(1) 稳定 教学、几乎不用
快速 O(n log n) O(n log n) O(n²) O(log n) 不稳定 通用首选
简单选择 O(n²) O(n²) O(n²) O(1) 不稳定 交换代价大时
堆排序 O(n log n) O(n log n) O(n log n) O(1) 不稳定 内存紧、要最坏保证
二路归并 O(n log n) O(n log n) O(n log n) O(n) 稳定 外部排序、要求稳定
基数 O(d(n+r)) 同 同 O(n+r) 稳定 整数/定长字符串

易错点:几条容易记混的结论

  1. 比较类排序的下界是 Ω(n log n),这是理论下界,任何基于比较的算法都突破不了;基数排序因为不比较所以例外。
  2. 快排最坏 O(n²) 的触发条件是数据已经有序(且基准取首元素)——"越有序越慢"是它最反直觉的性质。
  3. 空间复杂度 O(1) 的有:插入、希尔、冒泡、选择、堆(堆排是唯一原地 O(n log n))。
  4. 稳定 + O(n log n) 的只有归并(基数是线性对数但要求特定数据类型)。
  5. 一趟排序结束后能确定一个元素最终位置的:冒泡、快排、选择、堆(这四类每次都能"定死"一个位置)。

这一章在整门课里的位置

排序是第 1 章复杂度分析的第一次大规模实战:八种算法放在一张表里比时间、比空间、比稳定性,比较的正是第 1 章那套评价体系。它依赖第 2 章的顺序表(插入、选择类都要移动元素)和第 5 章的完全二叉树(堆排序)。往后看,第 8 章"折半查找要求有序"、外部排序的多路归并,都以本章为前提。

本篇是《数据结构与算法(93 讲)》第 7 章的知识点摘要。每种排序都有 C 语言实现与手工推演演示,见 课程页;上一章 图,下一章 查找。

相关课程

这篇笔记涉及的主题,我录过对应的视频课程: