【分治】归并排序:局部有序到整体有序
核心思想先把数组不断二分直到每个子数组只有一个元素再把相邻的有序子数组两两合并最终得到完整的有序数组。1. 问题定义给定长度为n的数组A[0...n-1]要求将数组按非递减顺序排列A[0]≤A[1]≤⋯≤A[n−1] A[0] \le A[1] \le \cdots \le A[n-1]A[0]≤A[1]≤⋯≤A[n−1]示例输入[5, 2, 8, 3, 1, 6, 4] 输出[1, 2, 3, 4, 5, 6, 8]归并排序使用分治思想最好、平均和最坏时间复杂度均为O(n log n)。2. 核心操作合并两个有序数组假设已有两个升序数组左[2, 5, 8] 右[1, 3, 6, 9]由于两个数组内部已经有序最小元素一定在两个数组当前未处理部分的首部。每次比较左右首元素把较小者写入辅助数组。三个指针i指向左区间尚未处理的第一个元素j指向右区间尚未处理的第一个元素k指向辅助数组下一个写入位置。合并步骤步骤比较取出结果数组12与11[1]22与32[1, 2]35与33[1, 2, 3]45与65[1, 2, 3, 5]58与66[1, 2, 3, 5, 6]68与98[1, 2, 3, 5, 6, 8]7左侧已空复制9[1, 2, 3, 5, 6, 8, 9]小的拿出来从数组删掉大的在数组留下继续跟后面的比两个数组共有n个元素每个元素只被处理常数次因此合并时间为O(n)。3. 分治框架归并排序包含三个阶段Divide分解从中点把数组分成左右两半Conquer解决递归地对左右两半排序Combine合并线性合并两个有序子数组。当区间长度为0或1时区间天然有序可以直接返回。是否MergeSort(A, left, right)left right?区间天然有序返回计算 mid递归排序左区间递归排序右区间合并两个有序区间当前区间有序4. 完整执行过程以[5, 2, 8, 3, 1, 6, 4]为例。分解阶段[5, 2, 8, 3, 1, 6, 4] ├── [5, 2, 8, 3] │ ├── [5, 2] │ │ ├── [5] │ │ └── [2] │ └── [8, 3] │ ├── [8] │ └── [3] └── [1, 6, 4] ├── [1, 6] │ ├── [1] │ └── [6] └── [4]合并阶段[5] [2] - [2, 5] [8] [3] - [3, 8] [2, 5] [3, 8] - [2, 3, 5, 8] [1] [6] - [1, 6] [1, 6] [4] - [1, 4, 6] [2, 3, 5, 8] [1, 4, 6] - [1, 2, 3, 4, 5, 6, 8]递归负责制造“局部有序”归并负责把两个局部有序区间变成更大的有序区间。5. 伪代码MERGE-SORT(A, left, right): if left right: return mid left (right - left) / 2 MERGE-SORT(A, left, mid) MERGE-SORT(A, mid 1, right) MERGE(A, left, mid, right) MERGE(A, left, mid, right): i left j mid 1 k left while 左右区间都还有元素: if A[i] A[j]: temp[k] A[i] i else: temp[k] A[j] j k 复制左区间剩余元素 复制右区间剩余元素 将 temp[left...right] 写回 A[left...right]6. C 语言实现下面的实现只申请一次辅助数组供整个递归过程复用。#includestdio.h#includestdlib.h/** * 合并两个有序区间 * A[left...mid] 和 A[mid1...right] */staticvoidmerge(int*A,int*temp,intleft,intmid,intright){intileft;intjmid1;intkleft;while(imidjright){if(A[i]A[j]){// 相等时优先取左侧元素保证稳定性。temp[k]A[i];}else{temp[k]A[j];}}while(imid){temp[k]A[i];}while(jright){temp[k]A[j];}for(intpleft;pright;p){A[p]temp[p];}}staticvoidmergeSortRecursive(int*A,int*temp,intleft,intright){if(leftright){return;}// 防止 left right 发生整数溢出。intmidleft(right-left)/2;mergeSortRecursive(A,temp,left,mid);mergeSortRecursive(A,temp,mid1,right);merge(A,temp,left,mid,right);}/** * 成功返回 1内存分配失败或参数无效返回 0。 */intmergeSort(int*A,intn){if(n1){return1;}if(ANULL){return0;}int*tempmalloc((size_t)n*sizeof(int));if(tempNULL){return0;}mergeSortRecursive(A,temp,0,n-1);free(temp);return1;}测试代码staticvoidprintArray(constint*A,intn){for(inti0;in;i){printf(%d%c,A[i],in-1?\n: );}}intmain(void){intarr1[]{5,2,8,3,1,6,4};intarr2[]{1};intarr3[]{9,8,7,6,5};intarr4[]{3,1,3,-2,0};intn1sizeof(arr1)/sizeof(arr1[0]);intn2sizeof(arr2)/sizeof(arr2[0]);intn3sizeof(arr3)/sizeof(arr3[0]);intn4sizeof(arr4)/sizeof(arr4[0]);if(mergeSort(arr1,n1))printArray(arr1,n1);if(mergeSort(arr2,n2))printArray(arr2,n2);if(mergeSort(arr3,n3))printArray(arr3,n3);if(mergeSort(arr4,n4))printArray(arr4,n4);return0;}输出1 2 3 4 5 6 8 1 5 6 7 8 9 -2 0 1 3 37. 正确性说明可以用数学归纳法证明。基础情况区间长度为0或1时天然有序。归纳假设假设所有长度小于n的数组都能被归并排序正确排序。归纳步骤对于长度为n的数组左右子数组长度都小于n根据归纳假设递归后左右子数组分别有序merge每次选择两个区间中尚未处理的最小元素写入辅助数组的元素始终保持非递减顺序合并结束后整个长度为n的区间有序。因此归并排序能够正确排序任意长度的数组。8. 复杂度分析时间复杂度递推式为T(n)2T(n/2)O(n) T(n) 2T(n/2) O(n)T(n)2T(n/2)O(n)每层所有合并操作共处理n个元素工作量为O(n)每次把规模减半递归树深度为O(log n)总时间为O(n) × O(log n) O(n log n)。情况时间复杂度最好情况O(n log n)平均情况O(n log n)最坏情况O(n log n)即使数组已经有序标准归并排序仍会完成全部拆分与合并。空间复杂度辅助数组O(n)递归栈O(log n)总体空间复杂度O(n)。9. 算法性质性质结论原因原地排序否标准实现需要辅助数组稳定排序是相等时优先取左侧元素最坏时间保证O(n log n)划分方式不依赖输入排列适合链表是链表合并可以通过修改指针完成适合外部排序是可以顺序读取并合并大文件稳定性若两个元素关键字相同排序后仍保持原来的相对次序则算法稳定。排序前[3a, 1, 3b] 排序后[1, 3a, 3b]合并时使用if(A[i]A[j])相等时先取左侧元素因此保持稳定。如果改为可能破坏稳定性。10. 边界情况输入输出说明[][]空数组直接返回[1][1]单元素天然有序[1, 2, 3][1, 2, 3]已排序数组[3, 2, 1][1, 2, 3]逆序数组[2, 2, 1][1, 2, 2]重复元素[-1, 3, -5][-5, -1, 3]负数11. 与其他排序算法对比算法平均时间最坏时间额外空间稳定性选择排序O(n²)O(n²)O(1)不稳定插入排序O(n²)O(n²)O(1)稳定归并排序O(n log n)O(n log n)O(n)稳定快速排序O(n log n)O(n²)通常O(log n)不稳定12. 与逆序对计数的关系归并排序还可以在合并阶段统计逆序对。当右侧当前元素小于左侧当前元素时左区间尚未处理的所有元素都与它构成逆序对因此可以一次增加mid−i1 mid - i 1mid−i1无序数组 ↓ 不断二分 单元素区间天然有序 ↓ 从下向上两两合并 比较左右当前元素 ├─ 左 右取左元素 └─ 左 右取右元素 ↓ 复制剩余元素 ↓ 得到更大的有序区间 ↓ 最终数组有序先递归制造局部有序再利用线性归并得到整体有序递推式为T(n)2T(n/2)O(n)所以时间复杂度为O(n log n)空间复杂度为O(n)

相关新闻

争夺注意力:视觉复杂性在竞争性亲社会众筹平台中的作用

争夺注意力:视觉复杂性在竞争性亲社会众筹平台中的作用

在竞争性的亲社会众筹(如公益捐赠、医疗筹款等)平台中,用户面对海量项目争夺有限的注意力,“视觉复杂性”成为影响项目能否脱颖而出的关键信号。相关研究主要从以下几个维度揭示其作用机制:视觉复杂性的双重维度与差异…

2026/9/24 23:24:27 阅读更多 →
具有量子态操纵的稳固单电子存储器研发进展与展望!

具有量子态操纵的稳固单电子存储器研发进展与展望!

具有量子态操纵的稳固单电子存储器近期取得了里程碑式突破,特别是复旦大学周鹏-刘春森团队于2026年7月17日在《Science》发表的“量子闪存(Quantum Flash)”成果,首次在室温下实现了单电子的非易失性存储与量子态工程化操控&#…

2026/9/23 16:49:39 阅读更多 →
5分钟部署Hermes Agent接入飞书:Python原生+SQLite轻量级AI办公Agent实战

5分钟部署Hermes Agent接入飞书:Python原生+SQLite轻量级AI办公Agent实战

1. 项目概述:为什么“5分钟部署Hermes Agent接入飞书”不是营销话术,而是真实可落地的工程实践Hermes Agent不是又一个跑几行命令就卡死的AI玩具。它是由Nous Research实验室主导开发、在GitHub上获得35.7k星标、拥有317位贡献者持续迭代的成熟Agent框架…

2026/9/23 8:50:09 阅读更多 →

最新新闻

Qt aarch64 静态交叉编译全流程解析:从工具链到部署

Qt aarch64 静态交叉编译全流程解析:从工具链到部署

1. 项目概述与整体方案解读1.1 为什么要做 Qt aarch64 静态交叉编译做嵌入式 Linux 图形应用开发的工程师,几乎都会撞上同一个问题:板子拿到手,交叉编译工具链装好了,程序也写好了,结果往板子上一跑,不是报…

2026/9/24 23:24:14 阅读更多 →
STM32启动流程:从复位向量到main函数之间的秘密

STM32启动流程:从复位向量到main函数之间的秘密

说实话,这个东西我纠结过很久。学 C 语言的时候,老师只告诉我们程序从main开始,从main结束,谁也不会去问一句:在这之前发生了什么?直到我拿到第一块 STM32 开发板,打开一个示例工程,…

2026/9/24 23:24:14 阅读更多 →
HTOOL HT06近场探头:EMC工程师的EMI精准定位利器

HTOOL HT06近场探头:EMC工程师的EMI精准定位利器

1. 这不是玩具,是EMC工程师的“听诊器”——HTOOL HT06近场探头套件到底在解决什么问题?你有没有遇到过这样的场景:产品过了初版功能测试,一进EMC实验室就栽了——辐射发射(RE)在300MHz附近超标8dB&#xf…

2026/9/24 23:24:14 阅读更多 →
HT06近场探头实战指南:精准定位EMI噪声源

HT06近场探头实战指南:精准定位EMI噪声源

1. 这不是普通探头,是EMC工程师的“听诊器”和“显微镜”你拆开一台刚过不了辐射骚扰测试的电源模块,板子上密密麻麻全是器件,开关管、MOSFET驱动、DC-DC电感、输入滤波电容……哪个在“尖叫”?哪个在“漏电”?哪个在通…

2026/9/24 23:24:14 阅读更多 →
京东后端实习一面复盘:八股文背得再熟,不如理解底层原理

京东后端实习一面复盘:八股文背得再熟,不如理解底层原理

面试结束当晚,我坐在图书馆把整个一面的过程像放电影一样过了一遍,越想越清醒。这趟京东后端实习一面,从自我介绍到项目拷打,前后大概五十分钟,面完我就知道自己大概率挂了。但说实话,这场面试比我看半个月八股文都有价值,它把我那些"以为会了"的知识点全打回了原形。…

2026/9/24 23:24:14 阅读更多 →
Win10 文件内容搜索全攻略:从索引开启到命令行实战

Win10 文件内容搜索全攻略:从索引开启到命令行实战

你肯定遇到过这种事:文件叫"未命名文档",或者某次随手存了个"111"命名的 Word,隔了三个月只记得里面写过"项目预算"四个字,在 Win10 里用搜索框一搜,结果空空如也。绝大多数人以为 Win1…

2026/9/24 23:23:14 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →