跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

快速排序原理与实现详解

快速排序是一种分治排序算法,由 Tony Hoare 提出。核心思想是选取基准值将数组划分为小于和大于基准的两部分,递归处理子区间。平均时间复杂度为 O(N log N),最坏情况 O(N²)。该算法不稳定。通过三数取中、小区间插入排序等优化可提升性能。支持递归与非递归实现。

小熊软糖发布于 2026/3/22更新于 2026/6/2520 浏览
快速排序原理与实现详解

快速排序简介

快速排序(Quick Sort)是算法界的经典之作,由 Tony Hoare 在 1962 年提出。凭借分治思想与高效的平均性能,它成为众多编程语言标准库中的默认排序算法。

相比于冒泡、选择等基础排序,快排更像一位策略型指挥官:先选出一个基准值(key),通过一趟划分将比基准小的元素放左边,大的放右边,再递归处理子区间。得益于这种思路,它在大多数情况下能以 O(N log N) 的时间复杂度完成任务,且空间开销较小。当然,快排并非完美,极端情况下可能退化为 O(N²),且不是稳定排序。

基本思想

  1. 在数组中选择一个基准值(key);
  2. 将数组划分为两部分:左边比 key 小,右边比 key 大;
  3. 递归排序左右两部分;
  4. 最终得到有序序列。

快速排序的实现

划分的核心在于将数组分为小于和大于基准的两部分。常见的划分方式有三种,本质一致但实现细节不同。

Hoare 版本

以数组最左边的值为基准。定义两个下标 L 与 R,分别从两侧开始查找。R 先走,找到比 key 小的值停下;L 接着走,找到比 key 大的值停下,交换两者位置。重复直到相遇,最后交换相遇点与 key 的值。

为什么 R 先走? 如果 L 先走,当 L 停在比 key 大的位置时,若此时 R 还没动或停在比 key 大的位置,交换后可能导致基准值被换到错误的一侧。R 先走能保证相遇点一定指向比 key 小(或等于)的位置,确保基准值归位正确。

int QuickSortPart(int* a, int left, int right) {
    int key = left; // 选择最左边为基准索引
    while (left < right) {
        // R 先走,找比 key 小的值
        while (left < right && a[right] >= a[key]) right--;
        // L 后走,找比 key 大的值
        while (left < right && a[left] <= a[key]) left++;
        Swap(&a[left], &a[right]);
    }
    Swap(&a[left], &a[key]); // 将基准值放到正确位置
    return left; // 返回基准值最终位置
}

单次划分完成后,递归处理左右区间即可。

void QuickSort(int* a, int left, int right) {
    if (left >= right) return; // 递归终止条件
    int mid = QuickSortPart(a, left, right);
    QuickSort(a, left, mid - 1);   // 递归排序左半部分
    QuickSort(a, mid + 1, right);  // 递归排序右半部分
}

此外还有挖坑法和前后指针法,Hoare 版本因性能优异常被标准库采用。

快速排序的优化

三数取中法选择基准值

如果基准值总是接近最小或最大值,会导致性能退化。三数取中法选取首、尾、中间三个数的中位数作为基准,使基准更接近真实中点。

int GetMidIndex(int* a, int left, int right) {
    int mid = left + (right - left) / 2;
    if (a[mid] > a[left]) {
        if (a[mid] < a[right]) return mid;
        else if (a[left] < a[right]) return right;
        else return left;
    } else {
        if (a[left] < a[right]) return left;
        else if (a[mid] < a[right]) return right;
        else return mid;
    }
}

小区间转插入排序

数据量较小时,递归开销反而大于收益。当区间长度小于阈值(如 10)时,改用插入排序效率更高。

void QuickSortOptimized(int* a, int left, int right) {
    if (left >= right) return;
    if (right - left + 1 <= 10) {
        InsertSort(a + left, right - left + 1);
        return;
    }
    int mid = QuickSortPart(a, left, right);
    QuickSortOptimized(a, left, mid - 1);
    QuickSortOptimized(a, mid + 1, right);
}

其他优化

  • 三路划分:针对大量重复元素,将数组分为 <key、=key、>key 三部分。
  • 自省排序:当递归过深时切换为堆排序,防止最坏情况。

非递归实现

递归依赖系统栈,非递归则使用显式栈模拟。每次将待处理的 left 和 right 压栈,出栈后划分,再将新区间入栈。

void QuickSortNonR(int* a, int left, int right) {
    Stack st;
    StackInit(&st);
    StackPush(&st, left);
    StackPush(&st, right);
    
    while (!StackEmpty(&st)) {
        right = StackTop(&st); StackPop(&st);
        left = StackTop(&st); StackPop(&st);
        
        if (left >= right) continue;
        
        int mid = QuickSortPart(a, left, right);
        StackPush(&st, mid + 1);
        StackPush(&st, right);
        StackPush(&st, left);
        StackPush(&st, mid - 1);
    }
    StackDestroy(&st);
}

性能分析

时间复杂度

  • 平均:O(N log N),假设基准值每次都能平分区间。
  • 最坏:O(N²),当数组完全有序且基准值选择不当时发生,但可通过优化避免。

空间复杂度

  • 递归:O(log N),取决于递归树深度。
  • 非递归:同样为 O(log N),取决于栈的大小。

稳定性

  • 不稳定:相同元素的相对顺序在交换过程中可能改变。

总结

快速排序通过'选基准、分区间、递归处理'的分治思想,实现了高效排序。其核心优势在于平均时间复杂度接近 O(N log N) 且额外空间开销小,因此广泛应用于标准库中。虽然存在最坏情况和不稳定的缺点,但通过三数取中、小区间插排、三路划分等优化手段,在实际场景中表现依然出色。递归与非递归两种实现各有适用场景,前者直观,后者更适合对栈溢出敏感的工程环境。

目录

  1. 快速排序简介
  2. 基本思想
  3. 快速排序的实现
  4. Hoare 版本
  5. 快速排序的优化
  6. 三数取中法选择基准值
  7. 小区间转插入排序
  8. 其他优化
  9. 非递归实现
  10. 性能分析
  11. 时间复杂度
  12. 空间复杂度
  13. 稳定性
  14. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Copilot 指令文件全解析:copilot-instructions.md、AGENTS.md 与 .instructions.md 区别
  • AI 绘画的商业应用:广告、插画与游戏设计
  • 毕业论文降低 AI 检测率的实用方法与技巧
  • LLaMA Factory 大语言模型训练与微调实战指南
  • Hunyuan-MT-7B-WEBUI 翻译 HuggingFace 模型卡片能力评测
  • 链表实现解析:结构体与数组两种方式对比
  • OpenClaw 接入自定义模型及 WebUI 智能操作实战
  • David Beazley 开源:基于实战的 Python 极速入门指南
  • OpenClaw 多飞书机器人配置指南
  • OpenClaw 接入微信实现 AI 自动回复实战指南
  • MedGemma-1.5-4B 实战:医学影像多模态理解与 Web 集成
  • SD-Trainer 快速上手:AI 绘画模型微调实战
  • Antigravity 工具实测:集成 Gemini 3 与 Claude 4.5 的 AI 编程 IDE
  • C++ 二叉搜索树详解:增删查改与 Key/Value 场景实现
  • OpenClaw 接入通义千问、文心一言与 DeepSeek 配置指南
  • OpenClaw Webhook 配置与使用指南
  • 大模型前沿论文精选:AIOS、FlashFace、SDXS 等 9 篇
  • VSCode 中 GitHub Copilot 安装与实战指南
  • 使用 Java 实现简单高效的任务调度框架
  • Claude Code 本地化接入魔搭社区指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online