归并排序的递归和非递归实现

归并排序的本质是 "分治":

把大数组拆成小数组,各自排好序后,再合并成一个有序数组。

就像把一堆乱牌分成两半,每半先理好,再把两堆理好的牌一张张比对,按顺序合成一堆 ------ 这就是 "分" 和 "合" 的过程。

下面看一道Leetcode题:

递归实现

java 复制代码
public class Main {
	public static int[] sortArray(int[] nums){
		// 数组长度大于1时才需要排序(长度为0或1默认有序)
		if(nums.length>1){
			mergeSort(nums);
		}
		return nums;
	}

	// 定义最大数组长度
	public static int MAXN = 50001;
	// 辅助数组:用于归并过程中临时存储元素,避免每次合并都新建数组
	public static int[] help = new int[MAXN];

	public static void mergeSort(int[] arr){
		// 从数组的0号位置到最后一个位置(arr.length-1)进行排序
		sort(arr,0,arr.length-1);
	}

	public static void sort(int[] arr,int l,int r){
		// 当区间只有一个元素时(l==r),无需排序,直接返回
		if(l==r){
			return;
		}

		// 计算中间位置(等价于(l+r)/2,但用位运算>>1避免溢出,且效率更高)
		int m = l + ((r - l) >> 1);
		// 递归排序左半区间 [l, m]
		sort(arr,l,m);
		// 递归排序右半区间 [m+1, r]
		sort(arr,m+1,r);
		// 合并左右两个已排序的区间
		merge(arr,l,m,r);
	}

	public static void merge(int[] arr,int l,int m,int r){
		int i = l;       // 辅助数组help的当前填充位置(从l开始)
		int a = l;       // 左区间的遍历指针(从l开始)
		int b = m + 1;   // 右区间的遍历指针(从m+1开始)

		// 1. 双指针遍历左右区间,将较小的元素依次放入help
		while(a <= m && b <= r){
			// 谁小就先放谁,相等时优先放左区间元素(保证稳定性)
			help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
		}

		// 2. 左区间还有剩余元素,全部放入help(右区间已遍历完)
		while(a <= m){
			help[i++] = arr[a++];
		}

		// 3. 右区间还有剩余元素,全部放入help(左区间已遍历完)
		while(b <= r){
			help[i++] = arr[b++];
		}

		// 4. 将help中排序好的元素拷贝回原数组的[l, r]区间
		for(i = l; i <= r; i++){
			arr[i] = help[i];
		}
	}

}

非递归实现

java 复制代码
public class Main {

	public static int[] sortArray(int[] nums){
		if(nums.length>1){
			mergeSort(nums);
		}
		return nums;
	}

	public static int MAXN = 50001;
	public static int[] help = new int[MAXN];

	public static void mergeSort(int[] arr){
		int n = arr.length;
		// step:当前要合并的子数组长度,初始为1(单个元素默认有序),每次翻倍(左移1位等价于×2)
		// 循环条件:step < n(当子数组长度超过数组长度时,排序完成)
		for(int step=1; step < n; step <<= 1){
			int l = 0; // 每次合并的起始位置,从0开始

			// 遍历数组,按当前step划分并合并子数组
			while(l < n){
				// m:左子数组的结束位置(左子数组范围 [l, m],长度为step)
				int m = l + step - 1;
				// 若右子数组起始位置(m+1)已超出数组范围,说明只剩一个子数组,无需合并
				if(m + 1 >= n){
					break;
				}
				// r:右子数组的结束位置(右子数组最大长度为step,取较小值避免越界)
				int r = Math.min(l + (step << 1) - 1, n - 1);

				// 合并 [l, m] 和 [m+1, r] 两个有序子数组
				merge(arr, l, m, r);

				// 移动到下一组子数组的起始位置
				l = r + 1;
			}
		}
	}

	public static void merge(int[] arr,int l,int m,int r){
		int i=l;
		int a = l;
		int b = m+1;
		while(a<=m&&b<=r){
			help[i++]=arr[a]<=arr[b]?arr[a++]:arr[b++];
		}

		while(a<=m){
			help[i++]=arr[a++];
		}

		while(b<=r){
			help[i++]=arr[b++];
		}

		for(i=l;i<=r;i++){
			arr[i]=help[i];
		}
	}

}

代码文件

链接:https://pan.quark.cn/s/fc867494224a

提取码:9nSb

相关推荐
非优秀程序员2 小时前
第05章 播放器大战,暴风、快播与那一夜|《互联网产品恩仇录》
算法
非优秀程序员2 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻3 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
文丑颜不良啊5 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang6 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪6 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息7 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼7 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff8 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈