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

C 语言快速排序详解:从基础到非递归实现

快速排序作为 C 标准库内置的高效排序算法,其核心在于分治思想与基准值选取。详细拆解了 Hoare 分区方案的实现逻辑,通过双指针扫描完成区间划分。针对极端数据场景,阐述了三数取中法如何规避最坏时间复杂度,以及小区间内采用堆排序替代递归的优化策略。此外,还展示了利用显式栈模拟递归调用的非递归实现,有效防止深层递归引发的栈溢出问题。内容包含完整代码示例与关键步骤注释,便于理解算法演进过程。

战神发布于 2026/3/16更新于 2026/9/957 浏览
C 语言快速排序详解:从基础到非递归实现

C 语言快速排序详解:从基础到非递归实现

快速排序是 C 标准库中内置的高效排序算法,其核心在于分治思想与基准值选取。本文详细拆解了 Hoare 分区方案的实现逻辑,通过双指针扫描完成区间划分。针对极端数据场景,阐述了三数取中法如何规避最坏时间复杂度,以及小区间内采用堆排序替代递归的优化策略。此外,还展示了利用显式栈模拟递归调用的非递归实现,有效防止深层递归引发的栈溢出问题。

一、快速排序(初阶)

1. 算法思想

快速排序有一个 key 值,称为基准元素。在第一次快速排序结束后,这个 key 的位置会发生改变,其他元素位置也会相应调整。最终,在 key 的左边都是小于 key 的数,右边都是大于 key 的数。此时 key 的顺序就被排好了,后续不需要再动。

接着将整个数组以 key 分成左右两个区间,并在这两个区间循环执行上述步骤。直到区间不可再分(区间只剩一个元素),排序结束。

简单来说,快速排序就是不断将比 key 小的数放左边,把比 key 大的数放右边,最后完成排序。

2. 实现思路

(1)定 key 值

第一步确定 key 值,通常理解为第一个数字(后续会改进)。后续的排序围绕这个基准元素进行。

(2)大小交换

使用两个指针,一个从左往右走,一个从右往左走。

  • 右指针向左走,直到遇到比 key 小的数停下。
  • 左指针向右走,直到遇到比 key 大的数停下。
  • 两指针停下后,交换对应的值,大的换到右边,小的换到左边。
(3)循环

两指针继续移动,满足条件时继续交换,直到相遇,循环停止。

(4)交换 key

将 key 与两指针相遇点的值进行交换。此时 key 左边都小于它,右边都大于它,第一轮快速排序结束。

(5)分割区间

将 key 左右的区间分割开来,分别对这两个区间重复第一轮的排序步骤。

(6)结束

当每个区间分割成只剩下一个元素时,跳出循环。所有区间处理完毕,排序完成。

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) { // 当左右小人相遇时就停止循环
        while (left < right && a[right] >= a[key]) // 右小人向左走,直到找到比 key 小的值
            right--;
        while (left < right && a[left] <= a[key]) // 左小人向右走,直到找到比 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 值固定为数组的第一项。如果数组是完全有序的,会出现性能问题。

若数组有序,右指针向左走找不到比 key 小的值,会一直走到 key 处,导致左右指针相遇。分割后 key 单独一个区间,右边所有数一个区间。每次循环只排好一个数,时间复杂度退化为 O(N^2)。

因此,当数组顺序有序或几乎有序时,key 容易取到极值,算法变慢。

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;
    }
}

3. 实现代码(中阶)

封装成函数使用,先调用 FindKey 获取基准值下标,再交换到首项。

void QuickSort1(int* a, int left, int right) {
    if (left >= right)
        return;
    
    int L = left;
    int R = right;
    int key = FindKey(a, left, right);
    Swap(&a[key], &a[left]);
    key = left;
    
    while (left < right) {
        while (left < right && a[right] >= a[key])
            right--;
        while (left < right && a[left] <= a[key])
            left++;
        Swap(&a[left], &a[right]);
    }
    Swap(&a[right], &a[key]);
    key = right;
    
    QuickSort1(a, L, key - 1);
    QuickSort1(a, key + 1, R);
}

三、快速排序(高阶)

1. 仍存在的问题

由于快速排序由递归实现,每递归一次就多一半的区间。在倒数第 1、2 层递归时,区间内只有几个数,之前的办法效率不高且增加了递归开销。

2. 优化(小区间优化)

当区间个数小于一定阈值(如 10)时,采用插入排序或堆排序,可以减少大量递归,提高效率。

3. 实现代码(高阶)

包含堆排序辅助逻辑。

(1)三数取中函数

见上文 FindKey。

(2)主要的快速排序代码

将分区逻辑封装为 PartSort1。

// 快速排序 hoare 版本
int PartSort1(int* a, int left, int right) {
    int key = FindKey(a, left, right);
    Swap(&a[key], &a[left]);
    key = left;
    
    while (left < right) {
        while (left < right && a[right] >= a[key])
            right--;
        while (left < right && a[left] <= a[key])
            left++;
        Swap(&a[left], &a[right]);
    }
    Swap(&a[right], &a[key]);
    return right; // 返回最后 key 的下标,方便分割
}
(3)堆排序

用于小区间优化。

// 插入排序
void InsertSort(int* a, int n) {
    for (int i = 0; i < n - 1; i++) {
        int end = i;
        int tmp = a[end + 1];
        while (end >= 0) {
            if (tmp < a[end]) {
                a[end + 1] = a[end];
                --end;
            } else {
                break;
            }
        }
        a[end + 1] = tmp;
    }
}

// 向下调整
void AdJustDown(int* a, int parent, int size) {
    int child = 2 * parent + 1;
    while (child <= size - 1) {
        if (child + 1 <= size - 1 && a[child + 1] > a[child])
            child++;
        if (a[child] > a[parent]) {
            Swap(&a[child], &a[parent]);
            parent = child;
            child = 2 * parent + 1;
        } else {
            break;
        }
    }
}

// 堆排序
void HeapSort(int* a, int sz) {
    int i;
    for (i = (sz - 1 - 1) / 2; i >= 0; i--)
        AdJustDown(a, i, sz);
    for (i = sz - 1; i > 0; i--) {
        Swap(&a[0], &a[i]);
        AdJustDown(a, 0, i);
    }
}
(4)快速排序的框架
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. 实现思路

使用一个栈来实现。将区间的左、右范围分别存在栈中。取出一个区间后,存下该区间分割后的两个子区间(前提是区间存在)。当栈为空且无法存入新数据时,排序完成。

3. 实现代码

(1)栈的实现(Stack.h)
#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);
(2)栈的实现(Stack.c)
#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. C 语言快速排序详解:从基础到非递归实现
  2. 一、快速排序(初阶)
  3. 1. 算法思想
  4. 2. 实现思路
  5. (1)定 key 值
  6. (2)大小交换
  7. (3)循环
  8. (4)交换 key
  9. (5)分割区间
  10. (6)结束
  11. 3. 实现代码
  12. 二、快速排序(中阶)
  13. 1. 存在的问题
  14. 2. 优化(三数取中)
  15. 3. 实现代码(中阶)
  16. 三、快速排序(高阶)
  17. 1. 仍存在的问题
  18. 2. 优化(小区间优化)
  19. 3. 实现代码(高阶)
  20. (1)三数取中函数
  21. (2)主要的快速排序代码
  22. (3)堆排序
  23. (4)快速排序的框架
  24. 四、快速排序(非递归)
  25. 1. 问题
  26. 2. 实现思路
  27. 3. 实现代码
  28. (1)栈的实现(Stack.h)
  29. (2)栈的实现(Stack.c)
  30. (3)快速排序主体

更多推荐文章

查看全部
  • 集团企业数字化:低代码如何实现多系统统一管理?
  • MCP 协议详解:与 Function Call 的区别及使用方法
  • AI 在前后端开发中的差异化应用:从 MVP 到高并发架构
  • Ubuntu 下安装 OpenClaw 搭建 AI 助理
  • 大模型入门:原理、训练流程与应用场景
  • 大疆无人机开发入门:MSDK、PSDK 与上云 API 技术解析
  • 基于 AI 算法的全网比价系统设计与实现
  • Llama-3.2-3B 开箱体验:Ollama 部署与多语言对话实测
  • 大模型分布式训练与高效调参技术实战
  • 基于 Microi 吾码的服务器虚拟化资源管理方案
  • 国内信息安全专业 A 及 A+ 类高校排名详解
  • 用 ComfyUI 搭图像生成流水线:从节点到工作流
  • LeetCode 最长公共前缀解题思路与代码实现
  • Spring Boot 开发入门:从零搭建第一个 Web 项目
  • llama.cpp 性能基准测试与参数调优实战指南
  • ToDesk、顺网云、青椒云云电脑 AIGC 性能实测与对比
  • Python 爬虫项目:爬取百度贴吧帖子,保存标题与评论内容
  • OpenClaw 集成 GLM 模型实现免费联网搜索
  • Java 算法实战:随机打乱数组顺序的实现思路与代码
  • Qwen3 与 Qwen Agent 智能体开发实战:接入 MCP 工具

相关免费在线工具

  • 加密/解密文本

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