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

快速排序详解:分治策略与优化实现

快速排序是一种基于分治思想的高效排序算法。详细解析了 Hoare 版本、前后指针法及非递归实现,重点阐述了三数取中法和小区间优化策略以规避最坏情况。通过代码示例展示了基准值选择对性能的影响,并分析了时间与空间复杂度,帮助读者深入理解算法核心逻辑与工程实践中的注意事项。

活在当下发布于 2026/3/23更新于 2026/10/676 浏览
快速排序详解:分治策略与优化实现

快速排序

快速排序是 Hoare 于 1962 年提出的一种二叉树结构的交换排序方法。其核心思想是分治:任取待排序序列中的某元素作为基准值,将集合分割成两子序列,左子序列所有元素均小于基准值,右子序列所有元素均大于基准值,然后对左右子序列重复该过程,直到所有元素排列在相应位置上。

1. Hoare 版本

Hoare 分区方案是快速排序最经典的实现方式。简单来说,就是选定一个基准值(通常选第一个),通过两个指针从两端向中间扫描,将比基准小的放左边,比基准大的放右边。

快速排序 Hoare 分区示意图

以数组 [6, 1, 2, 7, 9, 3, 4, 5, 10, 8] 为例,假设 6 为基准。左边找大,右边找小,找到后互换。一趟下来,6 的左边都比它小,右边都比它大。随后以 6 为分界线,递归处理左右区间,这就像一棵二叉树的构建过程。

快速排序递归划分示意

基础代码实现

void QuickSort(int* a, int left, int right) {
    if (left >= right) return; // 区间不存在时直接返回

    int keyi = left;
    int begin = left;
    int end = right;

    while (begin < end) {
        // 从右向左找小于基准的值
        while (begin < end && a[end] >= a[keyi]) {
            end--;
        }
        // 从左向右找大于基准的值
        while (begin < end && a[begin] <= a[keyi]) {
            begin++;
        }
        Swap(&a[begin], &a[end]);
    }
    // 将基准放到正确位置
    Swap(&a[keyi], &a[end]);

    // 递归排序子数组
    QuickSort(a, left, end - 1);
    QuickSort(a, end + 1, right);
}

为什么右边先走?

这是一个常见的面试考点。如果左边先走,当遇到比基准大的元素停止,而右边还没找到比基准小的元素时,可能会发生 begin == end 的情况,导致死循环或逻辑错误。右边先走能确保 end 最终停在比基准小的位置,保证 Swap 的安全性。

右边先走逻辑分析

2. 算法优化

虽然基本快速排序很快,但在输入数组已经有序或接近有序时,性能会退化为 O(n²)。实际工程中常采用以下两种优化策略。

三数取中法

选择第一个元素作为基准在有序数组下会导致分区极度不平衡。三数取中法通过选取左端、中间和右端三个元素的中值作为基准,能有效避免最坏情况。

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

小区间优化

当区间长度较小时,递归调用的开销可能超过排序本身的代价。此时切换为插入排序效率更高。通常认为区间长度小于 10 时进行优化。

if ((right - left + 1) < 10) {
    InsertSort(a + left, right - left + 1);
}

完整优化代码示例

#include <stdio.h>

void Swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

// 插入排序辅助函数
void InsertSort(int* a, int n) {
    for (int i = 1; i < n; i++) {
        int key = a[i];
        int j = i - 1;
        while (j >= 0 && a[j] > key) {
            a[j + 1] = a[j];
            j--;
        }
        a[j + 1] = key;
    }
}

// 三数取中
int GetMidi(int* a, int left, int right) {
    int midi = (left + right) / 2;
    if (a[left] < a[midi]) {
        if (a[midi] < a[right]) return midi;
        else if (a[left] > a[right]) return left;
        else return right;
    } else {
        if (a[midi] > a[right]) return midi;
        else if (a[left] < a[right]) return left;
        else return right;
    }
}

// 优化后的快速排序
void QuickSort(int* a, int left, int right) {
    if (left >= right) return;

    // 小区间优化
    if ((right - left + 1) < 10) {
        InsertSort(a + left, right - left + 1);
        return;
    }

    // 三数取中
    int midi = GetMidi(a, left, right);
    Swap(&a[left], &a[midi]);

    int keyi = left;
    int begin = left + 1;
    int end = right;

    while (begin <= end) {
        while (begin <= end && a[end] >= a[keyi]) end--;
        while (begin <= end && a[begin] <= a[keyi]) begin++;
        if (begin < end) Swap(&a[begin], &a[end]);
    }
    Swap(&a[keyi], &a[end]);

    QuickSort(a, left, end - 1);
    QuickSort(a, end + 1, right);
}

int main() {
    int arr[] = {2, 1, 3};
    QuickSort(arr, 0, 2);
    for (int i = 0; i < 3; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

3. 前后指针法

这是另一种常见的快速排序实现,使用 prev 和 cur 两个指针遍历数组,将小于基准值的元素移动到前面。

前后指针法示意图

排序过程

  1. 定义 prev 指向 left,cur 指向 prev + 1。
  2. cur 向右移动,若 a[cur] 小于基准值,则 prev 前进一步并交换 a[prev] 和 a[cur]。
  3. 最后将基准值交换到 prev 位置。
int PartSort_TwoPointer(int* a, int left, int right) {
    int midi = GetMidi(a, left, right);
    Swap(&a[left], &a[midi]);
    int keyi = left;
    int prev = left;
    int cur = prev + 1;

    while (cur <= right) {
        if (a[cur] < a[keyi] && ++prev != cur) {
            Swap(&a[prev], &a[cur]);
        }
        cur++;
    }
    Swap(&a[prev], &a[keyi]);
    return prev;
}

void QuickSort_TwoPointer(int* a, int left, int right) {
    if (left >= right) return;
    int keyi = PartSort_TwoPointer(a, left, right);
    QuickSort_TwoPointer(a, left, keyi - 1);
    QuickSort_TwoPointer(a, keyi + 1, right);
}

4. 非递归实现(栈模拟)

递归实现简洁,但大规模数据可能导致栈溢出。非递归实现通过显式栈模拟递归过程,避免系统栈开销。

实现思路

  1. 初始化栈,将初始左右下标入栈(注意顺序,先右后左)。
  2. 循环取出栈顶的下标,进行分区操作。
  3. 将分区后的左右子数组下标入栈,继续处理直到栈为空。

非递归快排流程

// 假设已实现栈结构 ST
void QuickSortNonR(int* a, int left, int right) {
    ST st;
    STInit(&st);
    STPush(&st, right);
    STPush(&st, left);

    while (!STEmpty(&st)) {
        int begin = STTop(&st);
        STPop(&st);
        int end = STTop(&st);
        STPop(&st);

        int keyi = PartSort_TwoPointer(a, begin, end);

        if (keyi + 1 < end) {
            STPush(&st, end);
            STPush(&st, keyi + 1);
        }
        if (begin < keyi - 1) {
            STPush(&st, keyi - 1);
            STPush(&st, begin);
        }
    }
    STDestroy(&st);
}

复杂度分析

  • 时间复杂度:平均 O(n log n),最坏 O(n²)。
  • 空间复杂度:最佳 O(log n)(递归栈深度),最坏 O(n)。

总结

快速排序凭借分治策略成为应用最广泛的排序算法之一。理解 Hoare 分区、前后指针法以及非递归实现的差异至关重要。在实际开发中,结合三数取中和小区间优化能有效规避极端数据带来的性能风险,确保算法在工程场景下的稳定性。

目录

  1. 快速排序
  2. 1. Hoare 版本
  3. 基础代码实现
  4. 为什么右边先走?
  5. 2. 算法优化
  6. 三数取中法
  7. 小区间优化
  8. 完整优化代码示例
  9. 3. 前后指针法
  10. 排序过程
  11. 4. 非递归实现(栈模拟)
  12. 实现思路
  13. 复杂度分析
  14. 总结

更多推荐文章

查看全部
  • Llama 3:Meta 新一代开源大语言模型详解
  • 如何使用 PaperXM 免费生成高质量论文实操指南
  • MoltBot 接入钉钉 Stream 流式接口配置详解
  • 无人机智能巡检在城市生命线管理中的应用实践
  • 归并排序的核心思想与进阶应用
  • Web 版 IM 聊天信息加密的三种实现方案
  • 基于 WebRTC+AI 的智能远程控制解决方案
  • Python 豆瓣高分书籍推荐:从入门到进阶
  • C++ 输入输出操作详解:从基础流到文件处理
  • 网络安全学习方向与路线详解
  • 基于 SpringBoot+Vue3 的网上摄影工作室系统设计与实现
  • OpenClaw 卸载指南:Windows macOS Linux npm pnpm
  • SQL Prompt 工具介绍与正版使用建议
  • AI 学习资源整理:工具、课程与实战指南
  • 读李宁《AIGC 自动化编程》:大模型辅助开发的分解与合并心法
  • C++ 笔试算法实战:打怪、字符串分类与城市群计数
  • 论文 AIGC 检测原理与降重工具实测指南
  • 前端 XSS 防护:layui util 与 js-xss 的实用写法
  • 文心一言:百度国产大模型的技术解析与应用
  • 基于 Spring Boot 的 Web 三大核心交互案例详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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