快速排序(Quick Sort) 是一种高效的分治算法,由 Tony Hoare 于 1960 年提出。它的核心思想是通过递归分区将数组排序,平均时间复杂度为 O(nlogn)O(nlogn),是实际应用中最快的通用排序算法之一。
快速排序的核心步骤
-
选择基准(Pivot)
从数组中选取一个元素作为基准值(Pivot)。常见选择方式包括:- 第一个元素
- 最后一个元素
- 中间元素
- 随机元素(推荐,避免最坏情况)
-
分区(Partitioning)
将数组重新排列,使得:- 左半部分所有元素 ≤ 基准值
- 右半部分所有元素 ≥ 基准值
基准值最终位于正确的位置。
-
递归排序子数组
对左右两个子数组重复上述过程,直到子数组长度为 1(自然有序)。
分区过程详解(Lomuto 分区方案)
以数组 [3, 6, 2, 8, 1, 5, 4]
为例,选择最后一个元素 4
作为基准:
- 初始化指针
i
指向起始位置前一位(i = -1
)。 - 遍历数组,将比基准小的元素交换到左侧。
- 最终将基准交换到正确位置。
具体步骤:
复制
原数组:3 6 2 8 1 5 [4]
遍历过程:
i=-1, j=0 → 3 < 4 → i=0, 交换3和3 → [3] 6 2 8 1 5 4
i=0, j=1 → 6 > 4 → 无交换
i=0, j=2 → 2 < 4 → i=1, 交换6和2 → 3 [2] 6 8 1 5 4
i=1, j=3 → 8 > 4 → 无交换
i=1, j=4 → 1 < 4 → i=2, 交换6和1 → 3 2 [1] 8 6 5 4
i=2, j=5 → 5 > 4 → 无交换
最后交换基准:i+1=3 → 交换8和4 → 3 2 1 [4] 8 6 5
分区结果:[3, 2, 1] 4 [8, 6, 5]
代码实现(JavaScript)
javascript
复制
function quickSort(arr, low = 0, high = arr.length - 1) {
if (low < high) {
const pivotIndex = partition(arr, low, high); // 获取基准位置
quickSort(arr, low, pivotIndex - 1); // 递归左半部分
quickSort(arr, pivotIndex + 1, high); // 递归右半部分
}
return arr;
}
function partition(arr, low, high) {
const pivot = arr[high]; // 选择最后一个元素作为基准
let i = low - 1; // 指向比基准小的区域的末尾
for (let j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
[arr[i], arr[j]] = [arr[j], arr[i]]; // 交换元素
}
}
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]]; // 将基准放到正确位置
return i + 1;
}
时间复杂度
场景 | 时间复杂度 | 说明 |
---|---|---|
平均情况 | O(nlogn)O(nlogn) | 每次分区大致平衡 |
最佳情况 | O(nlogn)O(nlogn) | 每次分区完全平衡 |
最坏情况 | O(n2)O(n2) | 每次分区极度不平衡(如已排序数组) |
优化最坏情况:
- 随机选择基准:避免固定选择首/尾元素。
- 三数取中法:选择首、中、尾元素的中位数作为基准。
快速排序 vs. 归并排序
特性 | 快速排序 | 归并排序 |
---|---|---|
时间复杂度 | 平均 O(nlogn)O(nlogn) | 稳定 O(nlogn)O(nlogn) |
空间复杂度 | O(logn)O(logn)(递归栈) | O(n)O(n)(需额外空间) |
稳定性 | 不稳定 | 稳定 |
适用场景 | 通用排序,内存敏感 | 需稳定性或链表排序 |
快速排序的优势
- 原地排序(In-place)
仅需少量额外内存(递归栈空间)。 - 缓存友好
顺序访问数组元素,提高缓存命中率。 - 实际效率高
常数因子较小,性能通常优于其他 O(nlogn)O(nlogn) 算法。
应用场景
- 大规模数据排序(如数据库查询优化)。
- 编程语言内置排序实现(如 C++
std::sort
、Pythonlist.sort
)。 - 需要快速但不要求稳定性的场景。
总结
快速排序通过分治策略和高效分区,在大多数情况下实现极快的排序速度。尽管最坏时间复杂度为 O(n2)O(n2),但通过随机化基准选择可有效规避,使其成为实际开发中的首选排序算法。理解其核心思想(分区与递归)和优化技巧,是掌握算法设计与分析的关键一步。