核心思想先把数组不断二分直到每个子数组只有一个元素再把相邻的有序子数组两两合并最终得到完整的有序数组。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)
企业数字化 ERP 产品动态
相关推荐
极空间NAS部署道理鱼全栈媒体管理指南 1. 为什么“道理鱼”在极空间NAS上值得专门折腾一次?“道理鱼”这个名字乍一听像某款冷门国产App,但实际它是个在小众音乐爱好者圈子里悄悄发酵了两年的全栈媒体管理工具——不是播放器,不是下载器,而是把本地音乐库、MV视频、有声… · 2026/9/13 11:52:43
安卓逆向工程实战:Apktool解包、Smali修改与APK签名全流程详解 1. 项目概述:为什么我们需要深入理解Apktool?在安卓应用开发、安全研究乃至日常的玩机折腾中,你或多或少都遇到过需要“窥探”一个APK文件内部结构的场景。可能是想学习某个优秀应用的界面布局实现,可能是需要汉化一个没有提供本地… · 2026/9/13 23:59:05
冲绳科学技术大学院大学研究者们找到了让AI视觉学习更聪明的秘密 这项由冲绳科学技术大学院大学(OIST)主导的研究,以预印本形式发布于2026年7月,论文编号为arXiv:2607.04044,有兴趣深入了解技术细节的读者可以通过该编号检索完整原文。教一个孩子认识世界,有两种截然不同的… · 2026/9/21 3:45:18
Python保留小数的6种实战方案:精度、性能与场景选型 1. 为什么“保留小数”这件事,远比你想象的更棘手刚学Python时,我写过一行代码:print(round(2.675, 2)),满心期待看到2.68,结果屏幕上赫然跳出2.67。那一刻我盯着终端发了两分钟呆——不是代码写错了,是浮点… · 2026/9/26 12:44:21
男性健身App怎么选?2026年5个硬指标横评与决策路径拆解 文章目录一、行业背景:男性健身需求正在从"跟风练"走向"按需选"二、硬指标一:AI计划定制能力(个性化引擎)三、硬指标二:增肌针对性与动作库覆盖(器械居家)四、硬指标三&… · 2026/9/26 12:44:21
2026 论文重复率 AI 率双高?一站式降AI率工具实测解析 一、前言:2026 高校论文审核新难题随着高校学术审核体系不断升级,知网、维普等主流检测平台全面上线AIGC 智能检测功能,当代毕业生的论文写作与修改迎来双重考验。以往论文仅需攻克重复率超标问题,如今还要规避 AI 写作痕迹检测风… · 2026/9/26 12:44:21
ax调度:从任务编排到系统治理的完整指南 1. 先把“ax调度”聊明白:它到底解决什么问题?第一次看到“ax”这个名字的时候,大多数人第一反应是:这到底是个库、一套规范,还是一家公司的内部代号?我刚开始接触的时候也一样,翻完文档才意识到… · 2026/9/26 12:44:21
800G/1.6T光模块耦合困局:C-Lens柱面透镜选型与装配实战 1. 从800G/1.6T的封装困局说起:C-Lens到底解决了什么问题这两年做光模块的兄弟应该都有同感,速率从400G往800G、1.6T走,最难受的不是DSP芯片,也不是Driver,而是光学封装。通道数从4路变8路再变16路,单通道速… · 2026/9/26 12:44:21
高斯泼溅终于不用插件了:Blender 5.3原生导入PLY、SPZ与USD Gaussian Splats成为Blender核心PointCloud数据,可进入Geometry Nodes并与普通模型混合渲染;但5.3仍处Alpha阶段,导出、色彩和变换问题尚未解决。
**先说预测:**Gaussian Splats真正进入制作流程的标志,不是扫描结果能… · 2026/9/26 12:44:15
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46