🌟 ​​快速排序:国王的宝石分治术​​

31 阅读3分钟

📖 ​​故事时间:宝石整理大作战​

国王有一堆​​杂乱无序的宝石​​(数组 [5, 3, 8, 6, 2, 7]),命令将军按大小排列。将军的智慧策略如下:

  1. ​选“标兵”宝石​​:
    随手拿起一颗宝石(如 6)作为“标兵”(Pivot)🔮,用它来划分其他宝石。
    → ​​隐喻​​:标兵是排序的参照物,类似算法中的​​基准值​​。

  2. ​分区作战​​:

    • ​左队​​:比 6 小的宝石([5, 3, 2]

    • ​右队​​:比 6 大的宝石([8, 7]
      → ​​核心操作​​:所有宝石只需和标兵比较一次,就能快速分组

  3. ​递归征服​​:
    将军对左右两队​​重复同样的策略​​:

    • 左队选新标兵(如 3)→ 分成 [2] 和 [5]
    • 右队选新标兵(如 8)→ 分成 [7] 和 [](空)
      → ​​递归终止​​:队伍只剩1颗宝石时,天然有序✅。
  4. ​胜利会师​​:
    合并左队 + 标兵 + 右队 → [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] 为例)

  1. ​初始状态​​:

    • 标兵 pivot=5(选最左元素)
    • 左指针 i 指向 5,右指针 j 指向 7
  2. ​分区过程​​:

    步骤操作数组变化
    1j 左移找到 2<5移动 2 到 i 位 → [2, 3, 8, 6, _, 7]
    2i 右移找到 8>5移动 8 到 j 位 → [2, 3, _, 6, 8, 7]
    3j 左移找到 i=j标兵 5 归位 → [2, 3, 5, 6, 8, 7]
  3. ​递归分治​​:

    • 左队 [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实现注意点)

  1. ​死循环预防​​:
    分区时需先移动右指针(j--),再移动左指针(i++

    java
    Copy
    while (i < j && arr[j] >= pivot) j--; // 先右!
    while (i < j && arr[i] <= pivot) i++; // 后左!
    
  2. ​递归终止条件​​:
    当 low >= high 时停止递归,避免栈溢出

  3. ​优化标兵选择​​:
    随机选择标兵避免最坏情况:

    java
    Copy
    // 随机选标兵,避免有序数组的 O(n²)
    int randomIndex = low + new Random().nextInt(high - low + 1);
    swap(arr, low, randomIndex); // 交换到最左
    int pivot = arr[low];
    

💎 ​​总结​

快速排序 = ​​“分区 + 递归”​​ 的分治策略:

  1. ​选标兵​​ → 随机或固定位置;
  2. ​双指针分区​​ → 小的左移,大的右移;
  3. ​递归子数组​​ → 直到数组大小为1。

🌈 ​​像国王分封领土​​:
选一个城主(标兵)→ 小的领主去左边,大的领主去右边 → 每个领地继续分封 → 最终天下有序!

用代码实现时,​​注意指针移动顺序​​和​​递归边界​​,即可掌握这颗“排序算法明珠”🎯。