算法中的快速排序

10 阅读3分钟

快速排序(Quick Sort)  是一种高效的分治算法,由 Tony Hoare 于 1960 年提出。它的核心思想是通过递归分区将数组排序,平均时间复杂度为 O(nlog⁡n)O(nlogn),是实际应用中最快的通用排序算法之一。


快速排序的核心步骤

  1. 选择基准(Pivot)
    从数组中选取一个元素作为基准值(Pivot)。常见选择方式包括:

    • 第一个元素
    • 最后一个元素
    • 中间元素
    • 随机元素(推荐,避免最坏情况)
  2. 分区(Partitioning)
    将数组重新排列,使得:

    • 左半部分所有元素 ≤ 基准值
    • 右半部分所有元素 ≥ 基准值
      基准值最终位于正确的位置
  3. 递归排序子数组
    对左右两个子数组重复上述过程,直到子数组长度为 1(自然有序)。


分区过程详解(Lomuto 分区方案)

以数组 [3, 6, 2, 8, 1, 5, 4] 为例,选择最后一个元素 4 作为基准:

  1. 初始化指针 i 指向起始位置前一位(i = -1)。
  2. 遍历数组,将比基准小的元素交换到左侧。
  3. 最终将基准交换到正确位置。

具体步骤

复制

原数组:3  6  2  8  1  5 [4]
遍历过程:
i=-1, j=03 < 4i=0, 交换33[3] 6 2 8 1 5 4
i=0, j=16 > 4 → 无交换
i=0, j=22 < 4i=1, 交换623 [2] 6 8 1 5 4
i=1, j=38 > 4 → 无交换
i=1, j=41 < 4i=2, 交换613 2 [1] 8 6 5 4
i=2, j=55 > 4 → 无交换
最后交换基准:i+1=3 → 交换843 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(nlog⁡n)O(nlogn)每次分区大致平衡
最佳情况O(nlog⁡n)O(nlogn)每次分区完全平衡
最坏情况O(n2)O(n2)每次分区极度不平衡(如已排序数组)

优化最坏情况

  • 随机选择基准:避免固定选择首/尾元素。
  • 三数取中法:选择首、中、尾元素的中位数作为基准。

快速排序 vs. 归并排序

特性快速排序归并排序
时间复杂度平均 O(nlog⁡n)O(nlogn)稳定 O(nlog⁡n)O(nlogn)
空间复杂度O(log⁡n)O(logn)(递归栈)O(n)O(n)(需额外空间)
稳定性不稳定稳定
适用场景通用排序,内存敏感需稳定性或链表排序

快速排序的优势

  1. 原地排序(In-place)
    仅需少量额外内存(递归栈空间)。
  2. 缓存友好
    顺序访问数组元素,提高缓存命中率。
  3. 实际效率高
    常数因子较小,性能通常优于其他 O(nlog⁡n)O(nlogn) 算法。

应用场景

  • 大规模数据排序(如数据库查询优化)。
  • 编程语言内置排序实现(如 C++ std::sort、Python list.sort)。
  • 需要快速但不要求稳定性的场景。

总结

快速排序通过分治策略高效分区,在大多数情况下实现极快的排序速度。尽管最坏时间复杂度为 O(n2)O(n2),但通过随机化基准选择可有效规避,使其成为实际开发中的首选排序算法。理解其核心思想(分区与递归)和优化技巧,是掌握算法设计与分析的关键一步。