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

归并排序:原理、代码实现与复杂度分析

归并排序采用分治策略,通过递归将数组拆分为最小单元后再有序合并。详细解析了其核心逻辑、Java 代码实现及递归调用栈机制。算法在时间复杂度上稳定为 O(n log n),空间复杂度为 O(n)。重点阐述了双指针合并技巧与栈帧压弹过程,帮助读者深入理解分治思想在实际编码中的应用。

PentesterX发布于 2026/3/29更新于 2026/10/487 浏览
归并排序:原理、代码实现与复杂度分析

文章配图

文章配图

归并排序详解

一、核心思路

归并排序(Merge Sort)是典型的分治算法。我们可以这样理解它的逻辑:

  • 快速排序:整个数组有序 = 基准元素有序 + 左侧子数组有序 + 右侧子数组有序。
  • 归并排序:整个数组有序 = 左半部分有序 + 右半部分有序 + 两个有序子数组合并。

简单来说,就是把大问题拆解成小问题,解决小问题后,再把结果合并起来。

1. 边界处理

在递归过程中,我们需要先定义好终止条件,避免无效计算:

  • array == null:空数组无需排序。
  • left == right:只有一个元素时,天然有序,直接返回。
  • left > right:区间非法,直接返回。

2. 合并操作

当递归回到上一层时,左右两个子数组都已经有序了。此时只需要执行一次标准的'双指针合并'操作,将两个有序数组合并为一个更大的有序数组。

3. 算法本质

从底层的最小单元(单个元素)开始,自底向上地不断合并,直到最终形成一个完整的有序数组。

二、代码实现

下面是基于 Java 的完整实现。注意看 merge 函数中的双指针逻辑,这是归并排序的关键。

public static void mergeSort(int[] array) {
    if (array == null || array.length < 2) return;
    mergeSortFunc(array, 0, array.length - 1);
}

private static void mergeSortFunc(int[] array, int left, int right) {
    // 递归终止条件:区间内只有一个元素或非法区间
    if (left >= right) return;
    
    int mid = left + ((right - left) >> 1); // 防止溢出的写法
    
    // 1. 递归拆分左边
    mergeSortFunc(array, left, mid);
    // 2. 递归拆分右边
    mergeSortFunc(array, mid + 1, right);
    // 3. 合并两个有序区间
    merge(array, left, right, mid);
}

private static void merge(int[] array, int left, int right, int mid) {
    int s1 = left;      // 左区间起始指针
    int s2 = mid + 1;   // 右区间起始指针
    int[] tmpArr = new int[right - left + 1]; // 临时数组用于存储合并结果
    int k = 0;

    // 当两个区间都有数据时,比较大小放入临时数组
    while (s1 <= mid && s2 <= right) {
        if (array[s1] <= array[s2]) {
            tmpArr[k++] = array[s1++];
        } else {
            tmpArr[k++] = array[s2++];
        }
    }

    // 将剩余的数据拷贝到临时数组
    while (s1 <= mid) {
        tmpArr[k++] = array[s1++];
    }
    while (s2 <= right) {
        tmpArr[k++] = array[s2++];
    }

    // 将临时数组的内容拷回原数组对应位置
    for (int i = 0; i < tmpArr.length; i++) {
        array[i + left] = tmpArr[i];
    }
}

三、递归调用栈解析

理解归并排序,必须理解它的递归过程。这不仅仅是代码的执行,更是内存中栈帧的变化。

1. 执行流程

递归就像剥洋葱。第一次调用进入第二层,第二层等第三层...直到触底(left >= right)。然后开始一层层 return,在回溯的过程中执行合并操作。

2. 栈帧变化

每次函数调用都会在调用栈上压入一个新的栈帧(Stack Frame),包含局部变量、参数和返回地址。

  • 向下递归:栈帧不断压入,深度增加。到达最底层时,栈空间占用最大。
  • 向上回溯:函数执行完毕弹出栈帧,同时触发合并逻辑。

值得注意的是,在归并排序的二叉树结构中,每一层不仅要处理左边的递归到底再回来,还要处理右边的递归到底再回来。只有当左右都处理完,当前层的栈帧才会弹出。

栈帧空间细节
  • 递归函数栈帧:每个栈帧的大小是常数级的。树的高度是 log(n),所以这部分的空间是 O(log n)。
  • 合并函数栈帧:merge 函数内部会开辟一个大小为 right - left + 1 的临时数组。虽然这个数组随着递归层级上升而变大,但它是在栈顶临时开辟,用完即释放。在最顶层调用时,这个临时数组大小为 n。

四、复杂度分析

1. 空间复杂度

空间复杂度关注的是执行过程中的最大瞬时占用空间。

  • 最底层:递归深度最深,但合并数组很小(接近 0)。总空间 ≈ O(log n)。
  • 最顶层:递归深度为 1,但需要合并整个数组,临时数组大小为 n。总空间 ≈ n。

在整个过程中,最大的瞬时空间出现在最顶层合并时,因此归并排序的空间复杂度为 O(n)。

2. 时间复杂度

时间复杂度主要取决于合并操作的次数。

  • 每层工作量:无论递归到哪一层,所有节点合并数据的总长度都是 n。
  • 层数:二叉树的高度是 log(n)。
  • 总计:n * log(n)。

所以,归并排序的时间复杂度稳定为 O(n log n),无论最好、最坏还是平均情况。


总结:归并排序是一种稳定的排序算法,适合对稳定性有要求且数据量较大的场景。虽然它需要额外的 O(n) 空间,但其时间效率非常可靠。

目录

  1. 归并排序详解
  2. 一、核心思路
  3. 1. 边界处理
  4. 2. 合并操作
  5. 3. 算法本质
  6. 二、代码实现
  7. 三、递归调用栈解析
  8. 1. 执行流程
  9. 2. 栈帧变化
  10. 栈帧空间细节
  11. 四、复杂度分析
  12. 1. 空间复杂度
  13. 2. 时间复杂度

更多推荐文章

查看全部
  • ClawX 可视化 AI 智能体工具介绍与使用指南
  • JavaScript 原子读和写操作详解
  • DeepSeek-R1 大模型基于 MS-Swift 框架的部署推理与微调实践
  • 华为 OD 技术面:C++ 核心八股题解析
  • 网络层:IP 协议、NAT 技术与 ICMP 协议详解
  • Python 爬虫入门:抓取豆瓣电影 Top250 数据
  • AI 前端详解:概念、场景与接入原理
  • Verilog 语法详解:从入门到精通
  • SQL Server 2016 及 Management Studio 安装指南
  • Qwen3.5 大模型单 GPU 高效部署与股票筛选实战
  • 大模型应用:如何指导 Agent 像人一样思考及思维链范式解析
  • C++ STL 常用容器入门与实战指南
  • C++ STL 算法:常用操作与易错点
  • JavaScript 响应对象 Response 详解
  • AI 大模型与 Agent 重塑医疗行业:智能诊疗助手项目实战
  • Web 自动化测试入门:Selenium 核心原理与实战脚本
  • GLM-4-9B 及 CodeGeeX4-ALL-9B 支持 Ollama 本地部署
  • 开源大模型最佳实践:基于 LoRA 微调 Chat-甄嬛示例教程
  • 汽车雷达多径环境下幽灵目标检测技术解析
  • Python 十大实用技巧:爬虫、自动化与数据处理

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online