当前位置: 首页 > news >正文

归并排序详解:优雅的分治艺术

什么?归并排序?这让博主想起了大学那会被《数据结构与算法》支配的恐惧…
哈哈言归正传,一直想对算法做一个专栏,因为其实工作中很少很少有机会用到算法,倒是很多工具方法底层会使用,工作被各种需求业务“折磨”的让大家很少再花费精力去深入这些底层,更别说很少有人天天刷算法,导致以前在学校为了应付面试和笔试(也包括社招)学习的这些玩意和基础随着时间慢慢消磨殆尽,所以这篇专栏来了!每一篇博主都会详细说明这些算法的设计思想和代码实现,帮助博主和大家共同进步,每篇如果有错误和需要改进的地方欢迎大家提出!下面开始我们第一篇,也是基础的——归并排序。

文章目录

  • 前言
  • 🌟 核心思想
  • ⚙️ Java实现
  • 🔍 时间复杂度分析
  • 📦 空间复杂度
  • ✅ 算法特性
  • ⚡ 优化技巧
  • 💡 实际应用场景
  • 🌐 总结


前言

归并排序是一种基于分治法的高效排序算法,由冯·诺依曼于1945年首次提出。它以其稳定的O(n log n)时间复杂度和优雅的实现方式在算法领域占据重要地位。下面我将详细介绍归并排序的原理,并提供完整的Java实现。


🌟 核心思想

先给出归并排序示意图:
归并排序

归并排序的核心思想可概括为三步:

  1. 分解:将待排序数组递归地分成两个子数组。
  2. 解决:对子数组进行排序(递归排序)。
  3. 合并:将两个有序子数组合并成一个有序数组。
原始数组:[38, 27, 43, 3, 9, 82, 10]分解过程:
1. [38, 27, 43, 3]  [9, 82, 10]
2. [38, 27] [43, 3]  [9, 82] [10]
3. [38] [27] [43] [3] [9] [82] [10]合并过程:
1. [27, 38] [3, 43]  [9, 82] [10]
2. [3, 27, 38, 43]    [9, 10, 82]
最终结果:[3, 9, 10, 27, 38, 43, 82]

⚙️ Java实现

public class MergeSort {// 归并排序主方法public static void mergeSort(int[] arr) {if (arr == null || arr.length <= 1) {return;}int[] temp = new int[arr.length];mergeSort(arr, temp, 0, arr.length - 1);}// 递归排序private static void mergeSort(int[] arr, int[] temp, int left, int right) {if (left < right) {int mid = left + (right - left) / 2; // 防止整数溢出// 递归排序左半部分mergeSort(arr, temp, left, mid);// 递归排序右半部分mergeSort(arr, temp, mid + 1, right);// 合并两个有序子数组merge(arr, temp, left, mid, right);}}// 合并两个有序子数组private static void merge(int[] arr, int[] temp, int left, int mid, int right) {// 复制数据到临时数组System.arraycopy(arr, left, temp, left, right - left + 1);int i = left;      // 左子数组起始索引int j = mid + 1;  // 右子数组起始索引int k = left;     // 合并数组的起始索引// 比较左右子数组元素,将较小者放入原数组while (i <= mid && j <= right) {if (temp[i] <= temp[j]) {arr[k++] = temp[i++];} else {arr[k++] = temp[j++];}}// 复制左子数组剩余元素while (i <= mid) {arr[k++] = temp[i++];}// 复制右子数组剩余元素while (j <= right) {arr[k++] = temp[j++];}}// 测试代码public static void main(String[] args) {int[] arr = {38, 27, 43, 3, 9, 82, 10};System.out.println("排序前: " + Arrays.toString(arr));mergeSort(arr);System.out.println("排序后: " + Arrays.toString(arr));}
}

算法解析

  1. mergeSort()方法:
    • 公共入口方法。
    • 创建临时数组避免在递归过程中重复创建。
    • 调用私有递归方法。
  2. 递归排序:
    • 计算中点:mid = left + (right - left)/2(避免整数溢出)。
    • 递归排序左半部分:left到mid。
    • 递归排序右半部分:mid+1到right。
    • 合并两个有序子数组。
  3. merge()方法:
    • 复制当前段数据到临时数组。
    • 使用三个指针分别追踪:
      • i:左子数组当前位置
      • j:右子数组当前位置
      • k:合并后数组当前位置
    • 比较左右子数组元素,将较小者放入原数组。
    • 处理剩余元素。

🔍 时间复杂度分析

操作时间复杂度
分解O(log n)
合并O(n)
总复杂度O(n log n)

归并排序在任何情况下的时间复杂度都是稳定的O(n log n),这是它的最大优势。

📦 空间复杂度

  • O(n):需要额外的空间存储临时数组
  • 不是原地排序算法(in-place)

✅ 算法特性

  1. 稳定性:保持相等元素的原始顺序(关键:合并时left[i] <= right[j])
  2. 适用场景:大数据量排序、链表排序、外部排序
  3. 不适用场景:内存受限的小数据量排序

⚡ 优化技巧

  1. 小数组切换插入排序:当子数组长度较小时使用插入排序。

归并排序即使处理小规模数据仍需递归调用、分割合并等操作,其时间复杂度虽为O(n log n),但‌递归栈管理、函数调用等额外开销的常数因子较大‌,插入排序在小规模数据(如n≤15)时实际运行时间接近O(n)。数据量≤15时,元素局部有序概率高,实际操作次数接近线性,且其‌简单比较交换操作的常数因子极低‌,在阈值范围内性能显著优于归并排序。
ex: 常数因子指算法时间复杂度表示式中的固定系数(如O(2n)中的2),简单交换位移不需要递归方法调用也不需要分配空间,自然开销低。

private static final int INSERTION_THRESHOLD = 15;private static void mergeSort(int[] arr, int[] temp, int left, int right) {if (right - left <= INSERTION_THRESHOLD) {insertionSort(arr, left, right);return;}// 其余代码不变
}private static void insertionSort(int[] arr, int left, int right) {for (int i = left + 1; i <= right; i++) {int key = arr[i];int j = i - 1;while (j >= left && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}
}
  1. 边界检查优化:在合并前检查左子数组最大值是否小于等于右子数组最小值。
// 在merge方法开始处添加
if (arr[mid] <= arr[mid + 1]) {return; // 已经有序,无需合并
}
  1. 避免重复复制:通过交换输入数组和临时数组的角色减少复制操作

💡 实际应用场景

  1. 大数据处理:MapReduce等分布式计算框架的底层排序
  2. 外部排序:处理超过内存容量的数据集
  3. 稳定排序需求:数据库排序、订单记录处理
  4. 链表排序:归并排序是链表最高效的排序方式(链表归并排序仅需修改节点指针指向,实现‌O(1)空间复杂度‌的原址排序)

🌐 总结

归并排序以其稳定高效的特性,成为处理大规模数据的首选算法之一。虽然需要额外空间,但它的分治思想在算法设计中具有重要地位,是理解递归和分治策略的经典案例。

学过Java的小伙伴,Arrays.sort()Collections.sort()‌底层就是使用优化后的归并排序呦,感兴趣的小伙伴可以再深入研究下。

相关文章:

  • 31.Python编程实战:自动化批量压缩与解压文件
  • 地址解析协议(ARP):交换机中的深度解析与实战指南
  • 扁平风格职场商务通用PPT模版分享
  • 企业级RAG系统架构设计与实现指南(基于Java技术栈)
  • 【技巧】win10和ubuntu互相挂在共享文件夹
  • Python爬虫实战:研究cssselect相关技术
  • Master PDF Editor:全能PDF编辑工具
  • 医院重症监护系统 ICU重症病房管理系统 重症监护软件
  • 根据 LiDAR 株高数据计算植被生物量
  • DeviceNet转Modbus RTU网关在玻璃制造中的关键应用
  • QT第一课 —— 设置CMake路径
  • 大语言模型的局限性与RAG基本框架和工作流实例
  • Oracle中10个索引优化
  • 【MySQL数据库 | 第三篇】DDL数据库操作
  • SCADA|信创KingSCADA4.0与KingSCADA3.8的几点不同
  • 高效文献阅读翻译软件推荐:精准翻译,助力科研提速
  • 51LA数据分析遇瓶颈?免费统计工具——悟空统计
  • python基本语法元素
  • 无刷直流电机控制系统仿真建模
  • R语言 | 如何使用R书写html文档?
  • wordpress ux theme/上海有哪些优化网站推广公司
  • wordpress static page/网页优化seo广州
  • 网站代理怎么赚钱/北京网站优化策略
  • 苏州网站设计公司价格/海外营销
  • 深圳最好的网站建设/做网站用哪个软件
  • 常见的网站建设技术有哪些/seo合作