【好记性不如烂笔头】排序算法之归并排序(二)


前言

  前文学习了归并算法,并最终用递归的方式实现了归并算法,但是最后测试贴了一张图,递归的实现还是比较耗时的,接下来学习一下优化的写法。


一、回顾递归实现归并

  思路是将数组不断的拆分成两部分,然后将两部分数据进行合并,拆分是递归进行的,这样基于递归的特性,先递进在归来,在归来的时候开始合并操作。

先递进后归来


二、为啥不建议用递归

  按照上个图的描述,先递进,后归来。合并在归来时完成,那递进不就浪费了一些性能了嘛?而且递归是自己调用自己,会有栈溢出隐患。
在这里插入图片描述

三、改进方案思路

  递归的方式让我触底了,才能得到左侧为1个数,右侧为1个数的两个数组,才能满足条件,那我可不可以直接就让它满足条件呢?画个图理下思路:

假设一个数组:
在这里插入图片描述

第一次干的事:
在这里插入图片描述

第一次操作之后的数组:
在这里插入图片描述

第二次干的事:
在这里插入图片描述

第二次操作之后的数组:
在这里插入图片描述

第三次干的事:
在这里插入图片描述

第三次操作之后的数组:
在这里插入图片描述
这不就完成排序了嘛?其实不然,我做这个数组比较特殊,最后一个是最大的,其实还有一次操作:

在这里插入图片描述

四、思路是有了,但是该怎么实现呢?

  老规矩,先画图,画图才能明晰思路:

代码思路

整理下:

  • 我需要一个S,每次乘以2,因为是左右两组比较嘛
    • 边界:S一定小于N
    • 边界:S一定小于N-L
    • 越界:S>N/2,下一次R组的长度可能不够会有越界现象
  • 我需要一个L,代表左组的初始位置。
  • 我需要一个R,代表右组的初始位置。

上次的merge(int[] arr, int L, int M, int R),有一个M,M参与了左组,也参与了右组,这次既然还要用这个方法,那么这次:

  • 我需要一个M,左组的时候L --> M,右组的时候M+1 --> R
    • 边界:M一定小于N
    • 边界:R要么是N-1,要么是M+S

五、代码实现

	public static void mergeSort2(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		int N = arr.length;
		int S = 1;
		while (S < N) {
			int L = 0;
			while (L < N) {
				int M = L + S - 1;
				if (M >= N) {
					break;
				}
				int R = Math.min(M + S, N - 1);
				merge(arr, L, M, R);
				L = R + 1;
			}
			if (S > N / 2) {
				break;
			}
			S <<= 1;
		}
	}

	public static void merge(int[] arr, int L, int M, int R) {
		// 我需要一个数组存放数据
		int[] narr = new int[R - L + 1];
		// 我需要三个指针,分别指向三个数据的头
		int i = 0;
		int p1 = L;
		int p2 = M + 1;
		// 我需要一个循环体,结束条件是其中一个越界了,这里写的是循环条件哦,不是结束条件
		// p1,p2都在各自的范围内
		while (p1 <= M && p2 <= R) {
			narr[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
		}
		// p1 越界,p1添加完了,剩下数据的就是p2
		while (p2 <= R) {
			narr[i++] = arr[p2++];
		}
		// p2 越界,p2添加完了,剩下数据的就是p1
		while (p1 <= M) {
			narr[i++] = arr[p1++];
		}
		// 合并后的narr数据,要放到原来的数组arr里的
		for (int j = 0; j < narr.length; j++) {
			// L为左侧数据的开始,从L开始放数据
			arr[L + j] = narr[j];
		}
	}

六、总结

  好像还是比较慢,哈哈,可能是因为我加的东西太多了,测试时间中包含了生成数组、数组复制、冒泡排序、归并排序、排序后两个数组的比较),还是快了0.3秒的嘛。
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泪梦殇雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值