📖 故事时间:宝石整理大作战
国王有一堆杂乱无序的宝石(数组 [5, 3, 8, 6, 2, 7]),命令将军按大小排列。将军的智慧策略如下:
-
选“标兵”宝石:
随手拿起一颗宝石(如6)作为“标兵”(Pivot)🔮,用它来划分其他宝石。
→ 隐喻:标兵是排序的参照物,类似算法中的基准值。 -
分区作战:
-
左队:比
6小的宝石([5, 3, 2]) -
右队:比
6大的宝石([8, 7])
→ 核心操作:所有宝石只需和标兵比较一次,就能快速分组
-
-
递归征服:
将军对左右两队重复同样的策略:- 左队选新标兵(如
3)→ 分成[2]和[5] - 右队选新标兵(如
8)→ 分成[7]和[](空)
→ 递归终止:队伍只剩1颗宝石时,天然有序✅。
- 左队选新标兵(如
-
胜利会师:
合并左队 + 标兵 + 右队 →[2, 3, 5]+6+[7, 8]=[2, 3, 5, 6, 7, 8]🎉。
⚙️ Java代码实战(逐行解析)
java
Copy
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
// 🎯 步骤1:分区操作,返回标兵正确位置
int pivotIndex = partition(arr, low, high);
// 🔁 步骤2:递归排序左右子数组
quickSort(arr, low, pivotIndex - 1); // 左队
quickSort(arr, pivotIndex + 1, high); // 右队
}
}
// ✅ 分区操作:双指针扫描 + 交换
private static int partition(int[] arr, int low, int high) {
int pivot = arr[low]; // 选左边元素为标兵(也可随机选优化)
int i = low; // 左指针
int j = high; // 右指针
while (i < j) {
// 🔍 右指针向左找:第一个比标兵小的数
while (i < j && arr[j] >= pivot) j--;
if (i < j) arr[i++] = arr[j]; // 填左坑
// 🔍 左指针向右找:第一个比标兵大的数
while (i < j && arr[i] <= pivot) i++;
if (i < j) arr[j--] = arr[i]; // 填右坑
}
arr[i] = pivot; // 标兵归位
return i; // 返回标兵位置
}
// 测试
public static void main(String[] args) {
int[] gems = {5, 3, 8, 6, 2, 7};
quickSort(gems, 0, gems.length - 1);
System.out.println("排序后:" + Arrays.toString(gems));
// 输出:[2, 3, 5, 6, 7, 8]
}
}
🔍 关键步骤详解(以数组 [5, 3, 8, 6, 2, 7] 为例)
-
初始状态:
- 标兵
pivot=5(选最左元素) - 左指针
i指向5,右指针j指向7。
- 标兵
-
分区过程:
步骤 操作 数组变化 1 j左移找到2<5移动 2到i位 →[2, 3, 8, 6, _, 7]2 i右移找到8>5移动 8到j位 →[2, 3, _, 6, 8, 7]3 j左移找到i=j标兵 5归位 →[2, 3, 5, 6, 8, 7] -
递归分治:
- 左队
[2, 3]→ 继续分区(最终有序) - 右队
[6, 8, 7]→ 选6为标兵 → 分成[]和[8,7]→ 再排序右队。
- 左队
📊 性能与特点
| 特性 | 说明 |
|---|---|
| ⏱️ 时间复杂度 | 平均 O(n log n),最坏 O(n²)(当数组已有序且总选最左为标兵时)47 |
| 📦 空间复杂度 | O(log n)(递归栈深度) |
| ✅ 稳定性 | 不稳定(交换可能破坏相等元素的顺序,如 [5₁, 5₂, 1] → 1 可能到 5₁ 前面)9 |
| 💡 优化策略 | 随机选标兵 / 三数取中法(选左、中、右的中位数)25 |
⚠️ 避坑指南(Java实现注意点)
-
死循环预防:
分区时需先移动右指针(j--),再移动左指针(i++)java Copy while (i < j && arr[j] >= pivot) j--; // 先右! while (i < j && arr[i] <= pivot) i++; // 后左! -
递归终止条件:
当low >= high时停止递归,避免栈溢出 -
优化标兵选择:
随机选择标兵避免最坏情况:java Copy // 随机选标兵,避免有序数组的 O(n²) int randomIndex = low + new Random().nextInt(high - low + 1); swap(arr, low, randomIndex); // 交换到最左 int pivot = arr[low];
💎 总结
快速排序 = “分区 + 递归” 的分治策略:
- 选标兵 → 随机或固定位置;
- 双指针分区 → 小的左移,大的右移;
- 递归子数组 → 直到数组大小为1。
🌈 像国王分封领土:
选一个城主(标兵)→ 小的领主去左边,大的领主去右边 → 每个领地继续分封 → 最终天下有序!
用代码实现时,注意指针移动顺序和递归边界,即可掌握这颗“排序算法明珠”🎯。