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

数据结构:归并排序与分治思想详解

归并排序作为分治法的经典应用,通过递归或迭代方式将数组不断二分直至单元素,再合并有序子数组。该算法时间复杂度稳定在 O(n log n),空间复杂度为 O(n),且具备稳定性,适合处理海量数据及外部排序场景。代码实现需注意区间划分避免死循环及边界条件处理。

深海蔚蓝发布于 2026/3/25更新于 2026/6/518 浏览
数据结构:归并排序与分治思想详解

归并排序:分治思想的完美演绎

基本思想

归并排序(Merge Sort)是分治法(Divide and Conquer)的经典应用,由计算机科学先驱约翰·冯·诺依曼于 1945 年提出。其核心逻辑非常直观:将大问题分解为小问题,解决小问题后合并结果。

算法流程主要包含两个阶段:

  1. 分(Divide):递归地将当前数组分割成两个子数组,直到每个子数组只包含一个元素(此时天然有序)。
  2. 治(Conquer):将两个有序子数组合并成一个新的有序数组。

归并的核心操作是合并两个有序数组。简单来说,就是每次比较两个数组的首元素,将较小者放入新数组,直到所有元素合并完成。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 辅助函数:合并两个有序区间 [begin, mid] 和 [mid+1, end]
void _MergeSort(int* a, int* tmp, int begin, int end) {
    // 递归终止条件:当子数组只有一个元素时,无需再分
    if (begin == end) {
        return;
    }

    // 计算中间点
    int mid = (begin + end) / 2;

    // 注意:这里区间分为 [begin, mid] 和 [mid+1, end]
    // 如果分为 [begin, mid-1] 和 [mid, end] 会出现死循环或越界问题
    _MergeSort(a, tmp, begin, mid);
    _MergeSort(a, tmp, mid + 1, end);

    // 开始归并
    int begin1 = begin, end1 = mid;
    int begin2 = mid + 1, end2 = end;
    int i = begin;

    // 只要两边都有元素,就继续比较
    while (begin1 <= end1 && begin2 <= end2) {
        if (a[begin1] < a[begin2]) {
            tmp[i++] = a[begin1++];
        } else {
            tmp[i++] = a[begin2++];
        }
    }

    // 处理剩余元素:其中一个区间结束后,将另一个区间的剩余元素全部拷贝
    while (begin1 <= end1) {
        tmp[i++] = a[begin1++];
    }
    while (begin2 <= end2) {
        tmp[i++] = a[begin2++];
    }

    // 将合并结果复制回原数组
    memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

// 归并排序入口函数
void MergeSort(int* a, int n) {
    // 分配临时数组空间
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL) {
        perror("malloc failed");
        return;
    }

    // 调用递归函数
    _MergeSort(a, tmp, 0, n - 1);

    // 释放临时数组
    free(tmp);
    tmp = NULL;
}

// 测试案例
int main() {
    int a[] = {2, 3, 5, 4, 7, 1};
    MergeSort(a, 6);
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
        printf("%d ", a[i]);
    }
    return 0;
}

为什么区间划分要注意?

在递归拆分时,我们通常使用 [begin, mid] 和 [mid+1, end]。如果错误地写成 [begin, mid-1] 和 [mid, end],在某些情况下会导致死循环。

假设数组有 10 个数据,下标从 0 到 9。如果取 mid = 4,按错误写法分成 [0, 3] 和 [4, 9]。在 [0, 3] 中 mid-1=1,分成 [0, 1] 和 [2, 3]。看似没问题,但在某些边界条件下,比如 [2, 3] 再次拆分时,若 mid 计算导致区间重叠或无法收敛,就会出问题。最稳妥的方式始终是让中间点归属左半部分,右半部分从 mid+1 开始。

操作时间复杂度说明
分解数组O(log n)递归深度
合并子数组O(n)每层需要遍历所有元素
总复杂度O(n log n)稳定的高效率排序
空间复杂度

归并排序需要 O(n) 的额外空间来存储合并过程中的临时数组。虽然递归调用栈占用 O(log n),但通常临时数组的空间需求占主导地位。

非递归实现

除了递归,归并排序也可以用迭代方式实现。这可以理解为:先进行 1+1 归并,再进行 2+2 归并,以此类推。

void MergeSortNonR(int* a, int n) {
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL) {
        perror("malloc failed");
        return;
    }

    // gap 是当前归并子数组的大小,从 1 开始,每次翻倍
    int gap = 1;
    for (int i = 0; i < n; i += 2 * gap) {
        int begin1 = i, end1 = i + gap - 1;
        int begin2 = i + gap, end2 = i + 2 * gap - 1;

        // 第二组完全越界,跳过本次合并
        if (begin2 >= n) {
            break;
        }

        // 第二组的 begin2 没越界,但 end2 越界了,需要修正边界
        if (end2 >= n) {
            end2 = n - 1;
        }

        int j = i;
        while (begin1 <= end1 && begin2 <= end2) {
            if (a[begin1] < a[begin2]) {
                tmp[j++] = a[begin1++];
            } else {
                tmp[j++] = a[begin2++];
            }
        }

        while (begin1 <= end1) {
            tmp[j++] = a[begin1++];
        }
        while (begin2 <= end2) {
            tmp[j++] = a[begin2++];
        }

        // 将合并结果复制回原数组
        memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));
    }
    free(tmp);
    tmp = NULL;
}

非递归实现要点

  1. gap 参数:控制当前归并的子数组大小,初始为 1,每次迭代翻倍。
  2. 边界处理:这是最容易出错的地方。数组长度往往不是 2 的幂次方,必须处理以下两种情况:
    • 当第二个子数组完全越界(begin2 >= n),说明没有可合并的数据,直接跳出循环。
    • 当第二个子数组部分越界(end2 >= n),将其右边界修正为 n - 1。
  3. 迭代过程:
    • 第 1 轮:1+1 归并 → 每个子数组大小为 1
    • 第 2 轮:2+2 归并 → 子数组大小为 2
    • 第 k 轮:2^k + 2^k 归并 → 子数组大小为 2^k

每一层的时间复杂度都是 O(n),一共 log n 层,所以总时间复杂度依然是 O(n log n)。空间复杂度同样是 O(n)。

归并排序特性总结

  1. 稳定排序:
    • 当两个元素相等时,归并排序优先选择左子数组的元素。
    • 保持相等元素的原始相对位置不变。
    • 适用于对稳定性有要求的场景(如多关键字排序)。
  2. 时间复杂度:
    • 最优、平均、最差均为 O(n log n)。
    • 不受输入数据影响,性能非常稳定。
  3. 空间复杂度:
    • O(n) 额外空间。
    • 递归实现还需 O(log n) 的栈空间。
  4. 外排序优势:
    • 归并排序是少数能高效处理外部存储(如硬盘)数据的排序算法。
    • 特别适合处理超过内存容量的海量数据。
    • 工作流程:将大文件分割为能放入内存的小块 -> 内存中排序每个小块 -> 合并已排序的小块。

结语

归并排序是分治思想的完美体现,通过'分而治之'的策略达到 O(n log n) 的高效排序。尽管它需要 O(n) 的额外空间,但在现代计算机系统中,这种空间换时间的策略往往是值得的。

它的核心优势在于稳定可靠、性能不受数据分布影响,以及强大的外排序能力。这使得它在数据库系统、大数据处理等场景中发挥着不可替代的作用,是每个程序员必须掌握的经典算法之一。

目录

  1. 归并排序:分治思想的完美演绎
  2. 基本思想
  3. 为什么区间划分要注意?
  4. 空间复杂度
  5. 非递归实现
  6. 非递归实现要点
  7. 归并排序特性总结
  8. 结语
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 自主机器人核心技术学习指南
  • 宇树 G1 机器人二次开发:基于 FAST-LIO 的建图与 RViz 配置指南
  • 多模态大模型原理与跨模态应用实战
  • Java InputStream 和 OutputStream 实现类详解
  • FastAPI:Python 高性能 Web 框架深度解析
  • C++ STL 容器与 Qt 容器对比及选型建议
  • Windows 7 兼容 Python 3.9+ 安装与配置指南
  • Kubernetes 多版本同步更新:修复 Go 安全漏洞并升级编译器
  • 五大经典排序算法详解:插入、希尔、冒泡、选择与堆排序
  • Java static 关键字:静态与非静态成员访问规则详解
  • Ubuntu 20.04 快速安装 Miniconda 指南
  • 五分钟构建动态知识图谱:利用大模型提取实体关系与数据对话
  • 今天 AI 热榜五大重点方向:平台生态、群体智能与评测体系
  • spdlog 日志库嵌入式 Linux C++使用指南
  • AI 辅助 Java 开发实战:构建高可用电商系统核心架构
  • M2LOrder 服务优化:API 响应压缩与 WebUI 资源懒加载
  • Python Django 在线音乐播放平台项目实战
  • 无人机路径规划算法详解
  • GTC2026 前瞻:Rubin 平台与 AI 工厂体系
  • AIGC 技术发展与多模态应用实践指南

相关免费在线工具

  • 加密/解密文本

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