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

C 语言快速排序详解与优化实践

快速排序是 C 语言库中常用的高效排序算法。从基础 Hoare 分区法入手,详细解析其递归实现原理。针对最坏情况下的性能退化,介绍了三数取中优化策略及小区间切换插入排序或堆排序的改进方案。此外,还展示了如何通过栈结构将递归转换为非递归实现,避免深层递归导致的栈溢出问题。内容涵盖核心逻辑、代码细节及关键优化点,适合深入理解快速排序机制。

无尘发布于 2026/3/23更新于 2026/10/2289 浏览
C 语言快速排序详解与优化实践

快速排序详解与优化实践

快速排序是 C 标准库中常用的高效排序算法。本文从基础 Hoare 分区法入手,详细解析其递归实现原理,并针对实际场景中的性能瓶颈提供多种优化方案,包括三数取中、小区间切换及非递归实现。

一、基础实现:Hoare 分区法

1. 核心思想

快速排序的核心在于选择一个基准值(key),将数组分为两部分:左边小于 key,右边大于 key。然后对左右区间递归执行相同操作,直到区间不可再分。

2. 实现逻辑

我们使用两个指针,一个从左向右找大数,一个从右向左找小数。当两指针相遇时,交换基准值与相遇点元素,完成一次分区。

具体步骤如下:

  • 定 Key:通常选取第一个元素作为基准。
  • 双指针移动:右指针先向左寻找比 key 小的值,左指针向右寻找比 key 大的值。
  • 交换:找到后交换两指针指向的元素。
  • 循环终止:当左右指针相遇时停止。
  • 归位:将 key 与相遇点元素交换,此时 key 左侧均小于它,右侧均大于它。

3. 代码实现

void QuickSort1(int* a, int left, int right)
{
    if (left >= right) // 区间只有一个或零个元素时结束
        return;

    int key = left; // 确定 key 的值,为第一个元素
    int L = left;
    int R = right; // 记录初始下标,防止后续修改丢失

    while (left < right) // 当左右小人相遇时就停止循环
    {
        // 右小人向左走,直到找到比 key 小的值
        while (left < right && a[right] >= a[key])
            right--;
        // 左小人向右走,直到找到比 key 大的值
        while (left < right && a[left] <= a[key])
            left++;
        Swap(&a[left], &a[right]); // 交换大的值和小的值
    }
    Swap(&a[right], &a[key]); // 最后交换 key 和相遇点对应的值
    key = right; // key 的下标也要改变

    QuickSort1(a, L, key - 1); // 递归 key 的左区间
    QuickSort1(a, key + 1, R); // 递归 key 的右区间
}

二、常见优化策略

1. 三数取中法

如果数组本身有序或接近有序,固定选取首元素作为 key 会导致时间复杂度退化为 O(N^2)。为了避免这种情况,我们可以采用'三数取中'法。

即取数组的开头、中间、结尾三个数,将大小居中的那个定为 key。这样能一定程度上避免取到极值点,提升平均性能。

// 三数取中,返回三个数的中间值下标
int FindKey(int* a, int left, int right)
{
    int mid = (left + right) / 2;
    if (a[left] > a[right])
    {
        if (a[right] > a[mid])
            return right;
        else if (a[mid] > a[left])
            return left;
        else
            return mid;
    }
    else
    {
        if (a[left] > a[mid])
            return left;
        else if (a[mid] > a[right])
            return right;
        else
            return mid;
    }
}

在调用快排前,先通过 FindKey 找到中位数下标,并将其与首元素交换,确保 key 位于首位,随后逻辑同基础版。

2. 小区间优化

由于快速排序是递归实现的,当区间长度较小时,递归调用的开销可能超过排序本身的收益。此时可以切换到插入排序或堆排序。

通常设定阈值(如 10),当区间长度小于该值时,直接调用插入排序或堆排序处理。

void QuickSort1(int* a, int left, int right)
{
    if (left >= right)
        return;

    int g = right - left + 1;
    if (g < 10)
        HeapSort(a + left, g); // 小区间使用堆排序优化
    else
    {
        int key = PartSort1(a, left, right);
        QuickSort1(a, left, key - 1);
        QuickSort1(a, key + 1, right);
    }
}

三、非递归实现

递归版本在数据量过大时可能导致栈溢出。我们可以通过显式使用栈结构来模拟递归过程,实现非递归快速排序。

1. 思路

使用栈存储待排序区间的左右边界。每次取出一个区间进行分区,若分区后的子区间有效,则将其边界压入栈中。当栈为空时,排序结束。

2. 辅助结构:栈

我们需要一个简单的栈来管理区间范围。

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int STDataType;

typedef struct Stack
{
    STDataType* a;
    int size;
    int capacity;
} Stack;

void StackInit(Stack* ps);
void StackPush(Stack* ps, STDataType data);
void StackPop(Stack* ps);
STDataType StackTop(Stack* ps);
int StackSize(Stack* ps);
int StackEmpty(Stack* ps);
void StackDestroy(Stack* ps);
#include "Stack.h"

void StackInit(Stack* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->capacity = 0;
    ps->size = 0;
}

void StackPush(Stack* ps, STDataType data)
{
    assert(ps);
    if (ps->size == ps->capacity)
    {
        int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
        if (tmp == NULL)
        {
            perror("realloc");
            return;
        }
        ps->a = tmp;
        ps->capacity = newcapacity;
    }
    ps->a[ps->size] = data;
    ps->size++;
}

void StackPop(Stack* ps)
{
    assert(ps && ps->size > 0);
    ps->size--;
}

STDataType StackTop(Stack* ps)
{
    assert(ps && ps->size > 0);
    return ps->a[ps->size - 1];
}

int StackSize(Stack* ps)
{
    assert(ps);
    return ps->size;
}

int StackEmpty(Stack* ps)
{
    assert(ps);
    return ps->size == 0;
}

void StackDestroy(Stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->capacity = 0;
    ps->size = 0;
}

3. 主体逻辑

void QuickSortNonR(int* a, int left, int right)
{
    Stack S;
    StackInit(&S);
    StackPush(&S, right);
    StackPush(&S, left);

    while (!StackEmpty(&S))
    {
        int L = StackTop(&S);
        StackPop(&S);
        int R = StackTop(&S);
        StackPop(&S);

        if (L >= R)
            continue;

        int g = R - L + 1;
        if (g < 10)
            HeapSort(a + L, g);
        else
        {
            int key = PartSort1(a, L, R);
            // 注意顺序,后进先出,先压右区间再压左区间,保证左区间先处理
            if (R - key - 1 > 1)
            {
                StackPush(&S, R);
                StackPush(&S, key + 1);
            }
            if (key - 1 - L > 1)
            {
                StackPush(&S, key - 1);
                StackPush(&S, L);
            }
        }
    }
    StackDestroy(&S);
}

通过上述优化与非递归改造,快速排序在实际工程应用中更加稳健高效。理解这些细节有助于在面对不同数据特征时做出合适的选择。

目录

  1. 快速排序详解与优化实践
  2. 一、基础实现:Hoare 分区法
  3. 1. 核心思想
  4. 2. 实现逻辑
  5. 3. 代码实现
  6. 二、常见优化策略
  7. 1. 三数取中法
  8. 2. 小区间优化
  9. 三、非递归实现
  10. 1. 思路
  11. 2. 辅助结构:栈
  12. 3. 主体逻辑

更多推荐文章

查看全部
  • Arduino BLDC 无刷电机模糊逻辑避障控制实战
  • Dify MCP-Server 插件:将工作流发布为第三方可调用服务
  • MySQL 安装指南:Ubuntu 环境下快速部署与配置
  • 基于 Django 的 4S 店客户管理系统设计与实现
  • Android 性能优化实战指南:核心原理与面试应对策略
  • 二分答案专题实战:木材加工与砍树问题解析
  • Python Selenium 模拟登录实战与自动化技巧
  • GitHub Copilot 学生开发者包申请与激活指南
  • LangChain4j 集成多模型 Provider 方案:OpenAI 与本地模型混合部署
  • 算法题解:740.删除并获得点数
  • 大模型训练原理:从自监督学习到 GPT 架构解析
  • 环形链表检测算法:哈希表与快慢指针法
  • 使用 Spring Session 配合 Redis 管理 HTTP Session
  • clawdbot (openclaw) + discord 机器人部署指南学习教程
  • Java 上机实训:输出语句与命令行参数处理
  • 基于 Python 的旅行数据可视化与分析系统
  • WebPShop 插件:让 Photoshop 完美支持 WebP 格式
  • 25 个简短且实用的 Python 代码片段
  • 昇腾 NPU 部署 Llama 2 模型:性能测试与优化实战
  • F5 刷新时,浏览器前端到底经历了什么?
  • 相关免费在线工具

    • 加密/解密文本

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