Skip to content

快速排序

快速排序(Quicksort)是实际工程中最快的通用比较排序算法——由 Tony Hoare 于 1959 年提出,至今仍是 C++ std::sort、Java Arrays.sort(基本类型)、V8 Array.prototype.sort(大数组分支)等主流排序实现的骨架。它的核心思想极其朴素:分治——从数组里挑一个元素做基准(pivot),把所有比 pivot 小的扔到它左边、比它大的扔到它右边(这一步叫分区 partition),然后对左右两个子区间递归重复。pivot 就此落到最终位置,递归 log n 层后整个数组有序。

快排的全部考点都源于一个数学事实:分区策略 + pivot 选择 ⇒ 平均 O(n log n) 最坏 O(n²)。由此衍生出三大主题:①分区算法(Lomuto 单指针简单、Hoare 双指针交换少、三路分区解决大量重复元素);②pivot 选择与最坏情况规避(随机化、三数取中、Introsort 兜底);③工程实现(小数组切到插入排序优化常数、尾递归优化控栈深、Dual-pivot 快排 Java/V8 实际在用、与归并/堆排的对比)。它是原地、不稳定的比较排序,但常数小、缓存友好,所以在大多数输入上跑得比理论更优的归并排序还快——理解这一点是理解「为什么 O(n log n) 还能分快慢」的关键。

评价

优点

  • 平均 O(n log n)、常数因子最小:内层循环极简(比较 + 交换,无额外分配),实测速度通常是归并/堆排的 1.5~2 倍——这是快排「实际最快」的根本原因
  • 原地排序(in-place):只需 O(log n) 的递归栈空间(优化后),不需要归并排序那样 O(n) 的辅助数组
  • 缓存友好(cache-friendly):分区是顺序扫描连续内存段,命中 CPU 缓存行,分支预测友好——比堆排(跨层跳跃破坏缓存)快得多
  • 工程可裁剪性强:可叠加小数组切插入、三数取中、三路分区、尾递归、Introsort 等多种优化,适应不同输入

缺点

  • 最坏 O(n²):当输入有序/逆序且 pivot 总取首元素时,每次分区极度不平衡,退化为冒泡——必须靠随机化/三数取中/Introsort规避
  • 不稳定:分区时的交换会打乱相等元素的相对顺序(相同值的元素可能被跨段换位),需要稳定排序的场景(多关键字排序)要用归并或 Timsort
  • 递归栈深度:最坏退化时递归深度 O(n),可能栈溢出——靠尾递归优化(对较短子段递归、较长子段迭代)把栈深压到 O(log n)

本叶地图

  • 入门 —— 快排核心思想(选 pivot 分区递归)、分治三步、为何平均 O(n log n)、最坏 O(n²) 退化原因、为何实际最快(常数小/原地/缓存友好)
  • 分区策略 —— Lomuto 单指针、Hoare 双指针、三路快排(荷兰国旗)、随机化/三数取中规避最坏、代码实现
  • 工程实践 —— 小数组切插入排序、尾递归优化、Dual-pivot 快排(Java/V8)、Introsort(C++ std::sort)、快排 vs 归并
  • 参考 —— 复杂度表、Lomuto/Hoare/三路代码模板、优化清单、与归并/堆排对比、易错点

交互演示

幻灯片地址

快速排序

测试题

快速排序测试题