V

算法可视化

ZHEN
分享到微博分享到 XGitHub 仓库个人主页
学习工具算法复杂度速查算法学习路径
数据结构数组链表栈队列树堆哈希表图字典树并查集LRU 缓存跳表线段树B+ 树布隆过滤器树状数组
经典排序算法冒泡排序鸡尾酒排序双调排序选择排序插入排序二分插入排序希尔排序归并排序自顶向下归并快速排序三路快排双轴快排堆排序计数排序基数排序桶排序
图算法Dijkstra 最短路Kruskal 最小生成树Prim 最小生成树Bellman-Ford 最短路拓扑排序Floyd 多源最短路强连通分量2-SAT最大流二分图匹配LCA 倍增欧拉路径
动态规划编辑距离0-1 背包完全背包最长公共子序列最长递增子序列硬币找零方案数石子合并旅行商 TSP树形 DP数位 DP换根 DP
回溯与搜索N 皇后子集生成全排列组合总和迷宫寻路岛屿数量单词搜索数独A* 寻路
字符串KMP 字符串匹配Rabin-KarpBoyer-MooreManacher后缀数组LCP / height 数组AC 自动机Z 函数
数学与数论埃氏筛线性筛欧几里得算法快速幂扩展欧几里得中国剩余定理欧拉函数米勒-拉宾FFTPollard's Rho
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

快速排序 Quick Sort

交换排序 · 实践中最快的通用排序

什么是快速排序

快速排序是「分而治之」的代表:挑一个基准 pivot,把数组重排成「比它小的都在左、比它大的都在右」——这一步叫分区(partition),做完后 pivot 就落到了它最终该在的位置。然后对左右两段各自递归重复。它平均只要 O(n log n)、而且原地不吃额外内存、常数因子小,是大多数语言标准库通用排序的底子。

它怎么做

本页用 Lomuto 分区(取区间末位当 pivot)+ 一个显式区间栈代替递归:栈里存「待处理的区间」,每次弹出一段做分区、把 pivot(品红柱)换到正确位置钉死,再把左右两个子区间压回栈。点「下一步」看 [7, 6, 5, 10, 9, 8, 4, 3, 2, 1]:右侧区间栈显示还有哪些段待排,主轨上 pivot 一次次归位、绿色就位块逐渐铺满。

7
6
5
10
9
8
4
3
2
1
区间栈 · 每格 = 一段待排序子数组 a[lo..hi](栈顶先弹出分区)
栈空 → 全部就位

弹出区间 [0,9]

1function quickSort(a: number[]): number[] {
2 const n = a.length;
3 const stack: [number, number][] = [[0, n - 1]];
4 while (stack.length > 0) {
5 const [lo, hi] = stack.pop()!;
6 if (lo >= hi) continue;
7 const pivot = a[hi];
8 let i = lo;
9 for (let j = lo; j < hi; j++) {
10 if (a[j] < pivot) {
11 [a[i], a[j]] = [a[j], a[i]];
12 i++;
13 }
14 }
15 [a[i], a[hi]] = [a[hi], a[i]];
16 stack.push([i + 1, hi]);
17 stack.push([lo, i - 1]);
18 }
19 return a;
20}
n10
栈深0
lo0
hi9
pivot-
i-
j-
a[j]-
swapCount0
1 / 93

复杂度与适用

平均 O(n log n)、原地、不稳定。软肋是最坏 O(n²)——当 pivot 每次都取到极值(如对已有序数组取末位),分区会退化成一头沉。实战用「随机 / 三数取中」选 pivot 来规避。另外大量重复元素也会拖慢它——这正是三路快排要解决的。

快排的两个变体(本站都有):
三路快排:分成 < / == / > 三段,等值元素一次归位——治大量重复。
双轴快排:用两个基准分三段——Java 基本类型 Arrays.sort 实际采用。

看懂本页的单轴分区后,去三路快排和双轴快排页看它在真实世界怎么被打磨——同样的 lt/i/gt 指针,玩法各不相同。