每天学一个算法-快速排序(Quick Sort)

📘 快速排序(Quick Sort)

1. 问题定义

给定数组 (A1...n),目标是将其重排为:

A\[1\]≤A\[2\]≤⋯≤A\[n\]\]\[A\[1\] \\le A\[2\] \\le \\dots \\le A\[n\]\]\[A\[1\]≤A\[2\]≤⋯≤A\[n\]

2. 核心思想

快速排序基于:

🔹 分治(Divide and Conquer)

将问题递归拆解为两个子问题。

在本算法中具体体现为:

  1. 选择一个元素作为枢轴(pivot)
  2. 执行划分(partition)
  3. 对左右子数组递归排序

3. 核心术语(精确定义 + 可理解解释)

🔸 3.1 枢轴(Pivot)

定义:

用于划分数组的参考元素

作用:

将数组分为两部分:

\\text{左区间} \\le pivot \\le \\text{右区间}

理解:

pivot 是"分界值",它最终会被放到正确位置

🔸 3.2 划分(Partition)

定义:

对区间 (Al...r) 重排,使:

∀i\p, A\[j\]≥A\[p\]\]\[ \\forall i \< p,\\ A\[i\] \\le A\[p\] \\quad \\forall j \> p,\\ A\[j\] \\ge A\[p\] \]\[∀i\p, A\[j\]≥A\[p\]

关键点:

  • 只保证"左右关系正确"
  • 不保证内部有序

👉 这是最容易误解的地方

🔸 3.3 递归(Recursion)

定义:

函数调用自身解决子问题

在快排中的作用:

  • 对左区间继续排序
  • 对右区间继续排序

🔸 3.4 原地排序(In-place)

定义:

不依赖额外线性空间

快排特点:

  • 通过交换完成排序
  • 仅使用常数额外空间(不计递归栈)

🔸 3.5 稳定性(Stability)

定义:

相等元素排序后相对顺序是否保持

结论:

快速排序不稳定

4. 算法结构

4.1 主过程

text 复制代码
QuickSort(A, l, r):
    if l < r:
        p = Partition(A, l, r)
        QuickSort(A, l, p-1)
        QuickSort(A, p+1, r)

4.2 划分函数(Lomuto)

text 复制代码
Partition(A, l, r):
    pivot = A[r]
    i = l - 1
    for j = l to r-1:
        if A[j] ≤ pivot:
            i = i + 1
            swap(A[i], A[j])
    swap(A[i+1], A[r])
    return i + 1

5. 核心机制(教学重点)

5.1 三个逻辑区间

在 partition 过程中,数组被划分为:

区间 含义
(Al..iAl..iAl..i) ≤ pivot
(Ai+1..j−1Ai+1..j-1Ai+1..j−1) > pivot
(Aj..r−1Aj..r-1Aj..r−1) 未处理

5.2 循环不变量(Loop Invariant)

在每轮循环开始时成立:

A\[l..i\]≤pivot,A\[i+1..j−1\]\>pivot\]\[ A\[l..i\] \\le pivot,\\quad A\[i+1..j-1\] \> pivot \]\[A\[l..i\]≤pivot,A\[i+1..j−1\]\>pivot

意义:

  • 保证算法过程始终正确
  • 是证明正确性的关键工具

5.3 Partition 的本质

不是排序,而是:

把一个元素(pivot)放到它最终应该在的位置

6. 正确性(简要证明)

6.1 Partition 正确性

循环结束后:

  • 左侧元素全部 ≤ pivot
  • 右侧元素全部 ≥ pivot
  • pivot 被交换到正确位置

6.2 递归正确性

基于归纳法:

  • 子问题规模更小
  • 子问题正确排序
  • 整体有序

7. 时间复杂度

7.1 递推式

T(n)=T(k)+T(n−k−1)+Θ(n)\]\[ T(n) = T(k) + T(n-k-1) + \\Theta(n) \]\[T(n)=T(k)+T(n−k−1)+Θ(n)

7.2 三种情况

情况 复杂度 原因
最优 (n \log n) 划分均匀
最坏 (n^2) 划分极端
平均 (n \log n) 随机分布

🔹 为什么是 (n \log n)

  • 每层处理 (n) 个元素
  • 层数约为 (\log n)

8. 空间复杂度

来源:递归调用栈

情况 空间复杂度
平均 (\log n)
最坏 (n)

9. 枢轴选择对性能的影响

🔸 9.1 固定选择

  • 简单
  • 易退化

🔸 9.2 随机选择

作用:

  • 打乱输入结构
  • 避免极端划分

🔸 9.3 三数取中

降低偏斜划分概率

10. 三路划分(进阶)

动机:

存在大量重复元素时:

  • 普通快排效率下降

解决:

划分为:

\pivot\]\[ \< pivot \\quad = pivot \\quad \> pivot \]\[\pivot

效果:

减少递归规模

11. 与其他排序对比

算法 时间 空间 稳定
快排 平均 (nlog⁡nn \log nnlogn) 原地
归并 (nlog⁡nn \log nnlogn) 额外空间
堆排 (nlog⁡nn \log nnlogn) 原地

12. 常见错误

❌ 错误1:认为 partition 后数组已排序

👉 实际只完成"分区"

❌ 错误2:递归区间写错

错误:

QuickSort(A,l,p)\]\[ QuickSort(A, l, p) \]\[QuickSort(A,l,p)

正确:

QuickSort(A,l,p−1)\]\[QuickSort(A, l, p-1)\]\[QuickSort(A,l,p−1)

❌ 错误3:不理解 i 的含义

i 表示:

复制代码
			$\text{"≤ pivot 区域的右边界"}$

13. 关键结论

  1. 快排核心是 partition
  2. pivot 决定效率
  3. 平均复杂度 (n \log n)
  4. 非稳定排序
  5. 原地算法
相关推荐
linx29532 分钟前
单元九 · 零基础路线图-第 7–9 周·类与 RAII
c语言·开发语言·数据结构·c++·嵌入式硬件·算法
布莱克60534 分钟前
C++ 七大排序算法详解:选择、冒泡、插入、计数、堆、快速与归并排序
数据结构·c++·算法·排序算法
shehuiyuelaiyuehao1 小时前
算法44,模拟算法,数青蛙
算法·哈希算法·散列表
hanlin031 小时前
刷题笔记:力扣第287题-寻找重复数
笔记·算法·leetcode
kevin_kang2 小时前
第02章 对话时间线与延迟分析
算法
科学实验家2 小时前
最小生成树:Prim,kruskal
数据结构·c++·算法
学习智者2 小时前
《玄》IDE v3.6.3重磅发布:全功能修复与性能飞跃
开发语言·c++·ide·算法·中文语言 玄
Jasmine_llq2 小时前
《P10263 [GESP202403 八级] 公倍数问题》
算法·数论·快速 io 优化·埃氏筛(倍数枚举)·线性遍历求和
橘子汽水1682 小时前
Leetcode 322 279 零钱兑换,完全平方数
算法·leetcode
kevin_kang2 小时前
第01章 VoiceAgent 的总体架构与完整流程
算法