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

八大排序算法核心解析与实战实现

八大排序涵盖插入、希尔、选择、堆、冒泡、快速、归并及计数排序。文章详细解析了各算法的核心思想、代码实现及性能分析。重点对比了时间复杂度、空间复杂度、稳定性及适用场景。通过 Hoare、快慢指针、挖坑等分区策略深入讲解快速排序,结合递归与非递归方式实现归并与快速排序。内容适合希望系统掌握排序算法底层逻辑的开发者参考。

人间过客发布于 2026/3/23更新于 2026/9/960 浏览
八大排序算法核心解析与实战实现

前言

排序是数据处理中最基础且重要的操作之一。其核心目标是将一组数据元素按照特定关键字(如数值大小、字母顺序)重新排列成有序序列,以满足升序或降序的要求。排序广泛应用于数据库查询优化、任务调度、数据可视化等场景。

概念辨析

在深入具体算法前,我们需要明确几个关键分类标准:

比较排序 vs 非比较排序

  • 比较排序:依赖元素间的大小比较(>、<、==)来确定顺序。适用于任意可比较类型的数据。理论下限为 O(n log n)。
  • 非比较排序:利用元素值的特性(如范围、频率)直接计算位置。通常仅适用于整数或可映射类型,时间复杂度可达 O(n)。

稳定排序 vs 非稳定排序

  • 稳定排序:相等元素的相对顺序在排序后保持不变。适用于多关键字排序场景(如先按分数排,再按姓名排)。
  • 非稳定排序:相等元素的相对顺序可能改变。适用于仅需关注主关键字的场景。

内部排序 vs 外部排序

  • 内部排序:数据完全加载到内存中处理,速度快。
  • 外部排序:数据量超过内存容量,需借助外存(硬盘),通过分块归并完成。

插入排序

直接插入排序

思想:将未排序元素逐个插入到已排序部分的正确位置,类似整理扑克牌。

实现:

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

属性:

  • 时间复杂度:最坏 O(n²),最好 O(n)
  • 空间复杂度:O(1)
  • 稳定性:稳定
  • 适用:小规模数据、基本有序数据

希尔排序

思想:插入排序的改进版。通过分组插入,优先处理距离较远的元素,减少移动次数。

实现:

void ShellSort(int* a, int n) {
    int gap = n;
    while (gap > 1) {
        gap = gap / 3 + 1;
        for (int i = 0; i <= n - gap - 1; i++) {
            int end = i;
            int tmp = a[end + gap];
            while (end >= 0) {
                if (a[end] > tmp) {
                    a[end + gap] = a[end];
                    end -= gap;
                } else {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }
}

属性:

  • 时间复杂度:取决于增量序列,Knuth 增量约为 O(n^1.3)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

选择排序

简单选择排序

思想:每次从未排序部分选出最小元素,放到已排序末尾。

实现:

void SelectSort(int* a, int n) {
    int begin = 0, end = n - 1;
    while (begin < end) {
        int minn = begin, maxx = begin;
        for (int i = begin + 1; i <= end; i++) {
            if (a[i] < a[minn]) minn = i;
            if (a[i] > a[maxx]) maxx = i;
        }
        Swap(&a[minn], &a[begin]);
        if (maxx == begin) maxx = minn;
        Swap(&a[maxx], &a[end]);
        begin++; end--;
    }
}

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

属性:

  • 时间复杂度:始终 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

堆排序

思想:利用堆结构(完全二叉树)高效选择最大/最小值。

实现:

void AdjustDown(int* a, int parent, int n) {
    int maxChild = (parent << 1) + 1;
    while (maxChild < n) {
        if (maxChild + 1 < n && a[maxChild + 1] > a[maxChild]) {
            maxChild += 1;
        }
        if (a[parent] >= a[maxChild]) return;
        Swap(&a[parent], &a[maxChild]);
        parent = maxChild;
        maxChild = (parent << 1) + 1;
    }
}

void HeapSort(int* a, int n) {
    for (int i = (n - 1 - 1) >> 1; i >= 0; i--) {
        AdjustDown(a, i, n);
    }
    int end = n - 1;
    while (end > 0) {
        Swap(&a[0], &a[end]);
        end--;
        AdjustDown(a, 0, end + 1);
    }
}

属性:

  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

交换排序

冒泡排序

思想:重复比较相邻元素,将较大元素逐步'冒泡'到末尾。

实现:

void BubbleSort(int* a, int n) {
    for (int i = 1; i <= n - 1; i++) {
        int flag = 1;
        for (int j = 1; j <= n - i; j++) {
            if (a[j - 1] > a[j]) {
                Swap(&a[j - 1], &a[j]);
                flag = 0;
            }
        }
        if (flag) break;
    }
}

属性:

  • 时间复杂度:最坏 O(n²),最好 O(n)
  • 稳定性:稳定

快速排序

思想:分治法。选基准值将数组分为两部分,递归排序。

分区策略:

  1. Hoare 对撞指针:左右指针向中间扫描,相遇处放基准。
  2. Lomuto 快慢指针:快指针遍历,慢指针标记小于基准区边界。
  3. 挖坑法:填坑挖坑,最后填入基准。

实现(递归):

int PartSort3(int* a, int left, int right) {
    int mid = GetMid(a, left, right);
    Swap(&a[left], &a[mid]);
    int key = a[left];
    int hole = left;
    int begin = left, end = right;
    while (begin < end) {
        while (begin < end && a[end] >= key) end--;
        a[hole] = a[end]; hole = end;
        while (begin < end && a[begin] <= key) begin++;
        a[hole] = a[begin]; hole = begin;
    }
    a[hole] = key;
    return hole;
}

void QuickSort(int* a, int left, int right) {
    if (left >= right) return;
    int key = PartSort3(a, left, right);
    QuickSort(a, left, key - 1);
    QuickSort(a, key + 1, right);
}

非递归实现:使用栈模拟递归调用过程,避免栈溢出风险。

属性:

  • 时间复杂度:平均 O(n log n),最坏 O(n²)
  • 空间复杂度:O(log n)
  • 稳定性:不稳定

归并排序

思想:分治法。将数组不断分割直到长度为 1,然后合并有序子数组。

实现(递归):

void _MergeSort(int* a, int* tmp, int begin, int end) {
    if (begin >= end) return;
    int mid = begin + ((end - begin) >> 1);
    _MergeSort(a, tmp, begin, mid);
    _MergeSort(a, tmp, mid + 1, end);
    
    int begin1 = begin, end1 = mid;
    int begin2 = mid + 1, end2 = end;
    int pos = begin;
    
    while (begin1 <= end1 && begin2 <= end2) {
        if (a[begin1] <= a[begin2]) tmp[pos++] = a[begin1++];
        else tmp[pos++] = a[begin2++];
    }
    while (begin1 <= end1) tmp[pos++] = a[begin1++];
    while (begin2 <= end2) tmp[pos++] = a[begin2++];
    memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

void MergeSort(int* a, int n) {
    int* tmp = (int*)malloc(n * sizeof(int));
    if (!tmp) { perror("malloc fail"); return; }
    _MergeSort(a, tmp, 0, n - 1);
    free(tmp);
}

属性:

  • 时间复杂度:始终 O(n log n)
  • 空间复杂度:O(n)
  • 稳定性:稳定

计数排序

思想:非比较排序。统计每个元素出现次数,按计数顺序输出。

实现:

void CountSort(int* a, int n) {
    int minn = a[0], maxx = a[0];
    for (int i = 0; i < n; i++) {
        if (a[i] < minn) minn = a[i];
        if (a[i] > maxx) maxx = a[i];
    }
    int range = maxx - minn + 1;
    int* count = (int*)calloc(range, sizeof(int));
    if (!count) { perror("calloc fail"); return; }
    
    for (int i = 0; i < n; i++) count[a[i] - minn]++;
    
    int pos = 0;
    for (int i = 0; i < range; i++) {
        while (count[i]--) a[pos++] = i + minn;
    }
    free(count);
}

属性:

  • 时间复杂度:O(n + k)
  • 空间复杂度:O(n + k)
  • 稳定性:稳定

性能总结

排序算法时间复杂度 (平均)空间复杂度稳定性适用场景
直接插入O(n²)O(1)稳定小规模、基本有序
希尔O(n^1.3)O(1)不稳定中等规模
简单选择O(n²)O(1)不稳定小规模
堆排序O(n log n)O(1)不稳定大规模、空间受限
冒泡O(n²)O(1)稳定教学、小规模
快速排序O(n log n)O(log n)不稳定通用、大规模
归并排序O(n log n)O(n)稳定大规模、链表
计数排序O(n + k)O(n + k)稳定范围有限整数

注意:实际应用中需根据数据特征(规模、分布、稳定性要求)选择合适的算法。

目录

  1. 前言
  2. 概念辨析
  3. 比较排序 vs 非比较排序
  4. 稳定排序 vs 非稳定排序
  5. 内部排序 vs 外部排序
  6. 插入排序
  7. 直接插入排序
  8. 希尔排序
  9. 选择排序
  10. 简单选择排序
  11. 堆排序
  12. 交换排序
  13. 冒泡排序
  14. 快速排序
  15. 归并排序
  16. 计数排序
  17. 性能总结

更多推荐文章

查看全部
  • Java 多线程与并发核心机制详解
  • Git 多人协作全流程实战:分支协同与冲突解决
  • AR 健身教练应用实践:基于 Rokid CXR-M SDK 的落地方案
  • Python 爬虫入门与分布式架构原理
  • VSCode Copilot 登录异常排查与修复实战
  • 2025 年开源软件架构图生成工具指南
  • Java Web 前端入门:HTML 核心知识点总结
  • 谷歌 Gemini 3 免费使用渠道与接入方式指南
  • 2026 春晚机器人三强专利布局与资本路径分析
  • 后端视角下的前端基础:HTML、CSS 与 JavaScript
  • 轻小说机翻机器人:快速搭建日语小说翻译工具
  • MySQL 数据库核心操作:创建、修改与备份实战指南
  • Python 初学者推荐的 4 款代码编辑器
  • VSCode 精准禁用 Copilot 代码补全:按语言与场景配置
  • Docker 安装配置 Neo4j 图数据库指南
  • OpenCode 与 GitHub Copilot 生产环境落地对比评测
  • 语音转文字工具 CapsWriter-Offline 本地及远程使用指南
  • 北京市印发人工智能行动计划 2025 年打造全球影响力策源地
  • 模仿学习原理与代码实战详解
  • 2025 年 DeepSeek 关于赚钱方法与创业十大黄金赛道建议

相关免费在线工具

  • 加密/解密文本

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