快速排序(Quick Sort)由Tony Hoare于1960年提出,凭借其平均O(n log n)的时间复杂度与原地排序特性,成为实际应用中最高效的排序算法之一。本文将通过分步拆解原理、Java代码实现及工业级优化技巧,带你彻底掌握快速排序的精髓。
一、核心思想:分治策略与分区操作
快速排序的核心是分治法(Divide and Conquer),通过递归将问题分解为更小的子问题解决。其流程分为三步:
- 选择基准值(Pivot):从数组中选取一个元素作为分割依据(如首元素、尾元素或随机元素)。
- 分区(Partition):重新排列数组,使所有小于基准的元素位于其左侧,大于基准的元素位于右侧,基准值位于最终正确位置。
- 递归排序:对基准左侧和右侧的子数组重复上述过程,直至子数组长度为1或0。
分区过程动态演示(以数组 [10, 7, 8, 9, 1, 5] 为例):
- 选择基准
pivot = 5(末尾元素) - 遍历数组,将
<5的元素交换至左侧区间 - 最终数组分为
[1, 5]和[10, 7, 8, 9],基准值5位于索引4
二、Java基础实现(以末尾为基准)
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high); // 获取基准位置
quickSort(arr, low, pivotIndex - 1); // 递归左子数组
quickSort(arr, pivotIndex + 1, high); // 递归右子数组
}
}
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high]; // 选择末尾元素为基准
int i = low - 1; // 小于区的边界指针
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
// 交换arr[i]和arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 将基准值放到正确位置
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1; // 返回基准索引
}
public static void main(String[] args) {
int[] arr = {10, 7, 8, 9, 1, 5};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr)); // [1, 5, 7, 8, 9, 10]
}
}
关键代码解析:
partition()方法通过指针i维护“小于区”边界,遍历中将比基准小的元素交换至该区域。- 最终将基准值
arr[high]与i+1位置交换,确保基准左侧全小、右侧全大。
三、性能瓶颈与优化策略
1. 避免最坏情况 O(n²)
当输入数组已有序或基准选择不当时(如总选最小/最大值),递归树退化为链表,时间复杂度升至O(n²)。
优化方案:
- 随机选择基准:降低有序数组的影响
private static int randomPartition(int[] arr, int low, int high) {
int randomIndex = low + (int)(Math.random() * (high - low + 1));
// 交换随机元素与首元素
int temp = arr[randomIndex];
arr[randomIndex] = arr[low];
arr[low] = temp;
return partition(arr, low, high); // 调用标准分区
}
- 三数取中法:选左、中、右三元素的中位数作为基准
private static int medianOfThree(int[] arr, int low, int high) {
int mid = low + (high - low) / 2;
// 排序左、中、右三数
if (arr[mid] < arr[low]) swap(arr, mid, low);
if (arr[high] < arr[low]) swap(arr, high, low);
if (arr[high] < arr[mid]) swap(arr, high, mid);
swap(arr, mid, low); // 中位数换到low位置
return partition(arr, low, high);
}
2. 减少递归深度
对小规模子数组(如长度 < 15)改用插入排序,避免递归开销。
3. 尾递归优化
将递归转化为迭代,减少栈空间占用(工业级库常用)。
四、算法性能全面分析
| 指标 | 快速排序 | 备注 |
|---|---|---|
| 平均时间复杂度 | O(n log n) | 基于随机或优化基准选择 |
| 最坏时间复杂度 | O(n²) | 输入有序 + 固定基准选择 |
| 空间复杂度 | O(log n) | 递归栈深度 |
| 稳定性 | 不稳定 | 相等元素可能交换位置 |
| 原地性 | 是 | 仅需常数额外空间 |
⚡️ 对比冒泡排序:快速排序平均性能远超冒泡(O(n²)),但小规模数据或近乎有序时,冒泡可能更优。
五、应用场景与工程实践
- 大规模数据排序
数据库索引构建、大数据分析(如Hadoop、Spark的默认排序实现)。 - 内存敏感场景
嵌入式设备、移动端APP(原地排序节省内存)。 - 算法衍生应用
- 快速选择算法:解决Top K问题(如LeetCode 215)
- 荷兰国旗问题:三向分区优化(如LeetCode 75)。
- 编程语言内置排序
JavaArrays.sort()对基本类型使用双轴快速排序(Dual-Pivot QuickSort),进一步减少比较次数:
// 双轴快排核心逻辑(简化)
if (arr[low] > arr[high]) swap(arr, low, high);
int pivot1 = arr[low], pivot2 = arr[high];
int lt = low + 1, gt = high - 1, i = low + 1;
while (i <= gt) {
if (arr[i] < pivot1) swap(arr, i++, lt++);
else if (arr[i] > pivot2) swap(arr, i, gt--);
else i++;
}
// 递归三个子区间
六、总结:快速排序的哲学
快速排序的精髓在于**“分而治之”** 与 “随机化”:
- 分治思想 将复杂问题拆解为可并行处理的子问题;
- 随机基准选择 避免理论最坏情况,体现工程实践中的概率思维;
- 空间高效性 使其成为资源受限场景的首选。
“优秀的算法不仅是代码,更是对问题本质的洞察。” —— 理解快速排序,即理解如何用简洁规则驾驭复杂数据。

被折叠的 条评论
为什么被折叠?



