算法基础知识 - 10.排序算法-快速排序详解

快速排序(Quick Sort)详解

快速排序(Quick Sort)是由计算机科学家托尼·霍尔(Tony Hoare)于1959年提出的一种分治算法,被誉为"20世纪十大算法之一"。它以极高的平均效率著称,是实际应用中最常用的排序算法(如Python的sorted()和C++的std::sort()底层实现)。


1. 核心思想

快速排序采用"分而治之"(Divide and Conquer)策略:

  1. 选取基准(Pivot):从数组中选择一个元素作为基准值。
  2. 分区(Partition):将数组分为两部分:
    • 左边部分的所有元素 ≤ 基准值
    • 右边部分的所有元素 ≥ 基准值
  3. 递归排序:对左右子数组重复上述过程,直到子数组长度为1或0。

2. 算法步骤(示例)

以数组 [10, 80, 30, 90, 40, 50, 70] 为例,选择最后一个元素(70)作为基准:

初始状态
[10, 80, 30, 90, 40, 50, 70]
↑(i) ↑(pivot)

分区过程

  1. 初始化 i = -1,从左到右遍历:
    • 10 < 70 → i++,交换10和自身 → [10, 80, 30, ...](i=0)
    • 80 > 70 → 跳过
    • 30 < 70 → i++,交换80和30 → [10, 30, 80, 90, ...](i=1)
    • 90 > 70 → 跳过
    • 40 < 70 → i++,交换80和40 → [10, 30, 40, 90, 80, 50](i=2)
    • 50 < 70 → i++,交换90和50 → [10, 30, 40, 50, 80, 90](i=3)
  2. 最后交换 i+1 和基准 → 交换80和70 → [10, 30, 40, 50, 70, 90, 80]

结果
左子数组 [10, 30, 40, 50](均≤70)
右子数组 [90, 80](均>70)
递归处理这两个子数组。


3. 代码实现
基础版本(Python)
def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]  # 选择中间元素作为基准
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quick_sort(left) + middle + quick_sort(right)
原地分区版本(更高效)
def partition(arr, low, high):
    pivot = arr[high]  # 选择最后一个元素作为基准
    i = low - 1        # i是小于基准的区域的边界
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i + 1], arr[high] = arr[high], arr[i + 1]  # 将基准放到正确位置
    return i + 1

def quick_sort_inplace(arr, low=0, high=None):
    if high is None:
        high = len(arr) - 1
    if low < high:
        pi = partition(arr, low, high)  # 分区索引
        quick_sort_inplace(arr, low, pi - 1)
        quick_sort_inplace(arr, pi + 1, high)

4. 时间复杂度分析
情况时间复杂度说明
最优O(n log n)每次分区都能均分数组
平均O(n log n)随机数据下的表现
最差O(n²)每次分区极度不平衡(如已排序数组)

数学推导

  • 每次分区操作耗时O(n)。
  • 理想情况下递归树高度为log n,总时间 = n × log n。

5. 空间复杂度
  • 最优:O(log n)(递归栈深度,平衡分区时)。
  • 最差:O(n)(极度不平衡分区时)。

6. 稳定性
  • 不稳定排序:分区交换可能改变相等元素的相对顺序。
    示例:[3₁, 2, 3₂, 1] → 选择3₂为基准时,3₁可能被交换到3₂之后。

7. 基准(Pivot)选择优化

基准的选择直接影响效率,常见策略:

  1. 随机选择:避免最坏情况(如 arr[random.randint(low, high)])。
  2. 三数取中法:选第一个、中间、最后一个元素的中位数。
  3. 双基准快排(Dual-Pivot Quick Sort):JDK的Arrays.sort()采用此优化。

8. 实际应用场景
  1. 大规模数据排序:平均效率远超插入/选择排序。
  2. 标准库实现:如C++的std::sort、Java的Arrays.sort()
  3. 需要缓存友好性:顺序访问内存,减少缓存未命中。

9. 与其他排序算法对比
算法平均时间复杂度最差时间复杂度空间复杂度稳定性
快速排序O(n log n)O(n²)O(log n)不稳定
归并排序O(n log n)O(n log n)O(n)稳定
堆排序O(n log n)O(n log n)O(1)不稳定

10. 常见问题

Q1:为什么快速排序比归并排序更快?
A1:虽然两者都是O(n log n),但快排序的常数因子更小,且是原地排序(归并排序需额外空间)。

Q2:如何避免最坏情况O(n²)?
A2:

  • 随机化基准选择。
  • 小数组时切换为插入排序(如JDK的混合排序策略)。

Q3:快速排序的递归会栈溢出吗?
A3:极端情况下可能(如完全逆序数组+最左基准选择),可通过尾递归优化或迭代实现避免。


11. 总结
  • 核心优势:平均情况下最快的通用排序算法。
  • 关键优化:合理选择基准 + 小数组切换插入排序。
  • 适用场景:大规模随机数据排序(但需注意最坏情况)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值