归并思想的经典算法---归并排序

上一篇博客简单讲了一下分而治之的快速排序,那这篇讲一下另一种重要的算法思想归并思想,搬出了归并思想的经典算法示例归并排序

一、思路分析

归并与分治的不同点在于核心不同,分治的核心在于分,而归并的核心在于合并,归并不管你这一刀往哪一划,我只在最终结果的合并把你给排好。

什么是归并排序
思路:归并排序重在最后的合并,划分直接中间切一刀就可以了。
第一步:以中间下标分开左右两数组
第二步:分别调用递归归并排序
重点
合并步骤:
想象有两条排好序的队伍,要通过一个门,一次过一个。
也就是每条队伍的第一个互相比较,谁小谁先过,总有一条队伍的头部会改变。
要解决的问题是,过去了的元素放哪。
解决方法:调用System.arraycopy(arr, p, helper, p, r-p+1);拷贝进来的原数组。
由于是递归调用,那么每次进来要排序的数组的长度都不一样,所以拷贝的长度使用 r-p+1来控制
例如,左边进来5个元素成为一个数组,在原数组中下标是0,1,2,3,4
拷贝的数量:4-0+1 = 5;
右边进来4个元素成为一个数组,在原数组中下标是5,6,7,8
拷贝的数量:8-5+1 =4;
但是长度肯定不会超过arr.length。因为每次递归调用都是上一次的一半或者一半+1进来

实现步骤
第一步:定义三个指针
left、arrIndex 初始都指向进来的数组的左边的头部,也就是p
right指向进来的数组右边的头部,也就是mid+1;
要注意的是,arrIndex记录的是原数组的下标指向,并非辅助空间数组的

第二步:进入循环,循环结束的条件是左边的数组走完了,或者右边的数组走完了
判断:辅助数组left 与 辅助数组 right 所指向的元素谁小,谁小谁现在走
走去哪?
去覆盖掉原数组arrIndex所指向的位置的元素
然后
arrIndex ++;
left/right ++;
无论辅助数组中的left、right元素谁走,
arrIndex肯定都是要走的,
不然会覆盖掉上一次排好序的元素

第三步:循环结束后
循环结束时,要么left走到头了,要么是right走到头了,要么大家都走到头了。
后面两种不需要特别处理,
第三种不用过多解释,
第二种情况下,right剩下没到头的元素肯定是比前面都要大的,直接不处理就行了,因为在原数组中,就已经是在自己的位置上了。
但是如果是第一种情况呢?right走完了,但是left还没走完,
这种情况就需要另外再进行一次循环,将左边剩下的元素覆盖到原数组的最右边
怎么放呢?
当循环结束的时候,arrIndex前面的元素都是已经排序好了的。
当前指向的肯定是没有被覆盖过的那个。因为每次覆盖后,arrIndex都会+1
所以,此时再进行一次判断循环
当left<=mid的时候,就说明左边没走完
然后将辅助数组中lefe所指向的元素去覆盖掉原数组arrIndex所指向的位置的元素
然后arrIndex、left都要++,继续判断一下还有没有剩下的元素
如果没有了,就退出循环,传进来的原数组就排好序了。

还有一点要弄明白
在代码:

public static void sort(int[] arr, int p, int r) {
		if (p < r) {
			int midIndex = p + ((r-p)>>1);
			// 两边分别归并排序
			sort(arr, p, midIndex);
			sort(arr, midIndex + 1, r);
			// 归并排序
			merge(arr, p, midIndex, r);
		}

递归调用了归并排序的算法,原理就是不断的将数组一半一半的切开,进行归并排序
每次规模都会减少1/2,这个跟二分查找有相似之处,复杂度为O(lgn)

二、代码实现

// 归并排序
public class Demo33 {
	static int[] helper;
	public static void main (String[] args) {
		int[] arr = Util.RandomIntArr(10, 1, 20);
		Util.printlnArr(arr);
		helper = new int[arr.length];
		sort(arr, 0, arr.length-1);
		Util.printlnArr(arr);
	}
	
	public static void sort(int[] arr, int p, int r) {
		
		if (p < r) {
			int midIndex = p + ((r-p)>>1);
			// 两边分别归并排序
			sort(arr, p, midIndex);
			sort(arr, midIndex + 1, r);
			// 归并排序
			merge(arr, p, midIndex, r);
		}
	}
	
	public static void merge(int[] arr, int p, int mid, int r) {
		// 拷贝数组
		System.arraycopy(arr, p, helper, p, r-p+1);
		// 初始化三指针:left的头指针, right的头指针, 原数组的指针
		int left = p; // 左边的头指针
		int right = mid+1; // 右边的头指针
		int arrIndex = p;  // 指向原数组的当前位置
		// 进入循环
		while (left <= mid && right <= r) {
			// 在辅助空间中比对两个值的大小
			if (helper[left] < helper[right]) {
				arr[arrIndex] = helper[left];  // 将小的值替换掉原数组所在位置的值
				left ++;
				arrIndex ++;
			}else {
				arr[arrIndex] = helper[right]; // 将小的值替换掉原数组所在位置的值
				right ++;
				arrIndex ++;
			}
		}
		// 当右边的走完了,左边还没走完
		while (left <= mid) {
			arr[arrIndex] = helper[left];
			left ++;
			arrIndex ++;
		}
	}
}

运行效果
在这里插入图片描述

三、强烈建议

看不懂的强烈建议结合前面的思路分析和代码一起看,动手敲,敲2次应该就能够理解了。敲3次肯定能记住了,敲5次就肯定有自己的理解方法了。
手上功夫,别做一个走马观花的人。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 算法10-12~10-14是关于归并排序的。 归并排序是一种基于分治思想排序算法,它将待排序的序列分成两个子序列,对每个子序列进行排序,然后将两个已排序的子序列合并成一个有序序列。 算法10-12是归并排序的递归实现,它将待排序的序列不断地分成两个子序列,直到每个子序列只有一个元素,然后将这些子序列两两合并,直到最终得到一个有序序列。 算法10-13是归并排序的非递归实现,它使用迭代的方式将待排序的序列分成若干个子序列,然后将相邻的子序列两两合并,直到最终得到一个有序序列。 算法10-14是归并排序的优化实现,它使用插入排序来处理长度较小的子序列,以提高排序效率。具体来说,当子序列的长度小于等于一定阈值时,使用插入排序来排序,否则使用归并排序来排序。 ### 回答2: 归并排序是一种基于分治思想排序算法。该算法的核心思想是将待排序的序列不断分割成更小的子序列,直到每个子序列只有一个元素,然后将这些子序列逐一合并,直到整个序列有序。因此,归并排序分为两个主要过程,分别是分割过程和合并过程。 分割过程:归并排序首先将待排序的序列均分成两个子序列,然后递归地对子序列进行分割,直到每个子序列只有一个元素。 合并过程:将两个有序的子序列合并成一个有序的序列。此时需要定义两个指针 i 和 j 分别指向两个子序列的起始位置,比较两个指针所指向的元素的大小,将较小的元素放到结果数组中并将指针向后移动,直到有一个子序列的指针到达了序列的末尾,然后将另一个子序列中未处理的元素依次放入结果数组中。 归并排序的时间复杂度为 O(N*logN),其中 N 为序列的长度。因此,归并排序是一种性能较好且稳定的排序算法,但由于其需要使用临时数组来辅助排序,因此空间复杂度为 O(N)。 总之,归并排序是一种高效且稳定的排序算法,适用于各种规模的序列。在实际应用中,可以使用多线程或并发编程来加速归并排序的执行过程,提高排序效率。 ### 回答3: 归并排序是一种常见的排序算法,采用了分治的思想,可以在最坏情况下也达到O(nlogn)的时间复杂度。它将一个大问题拆分成小问题,然后逐个解决小问题。下面是归并排序的基本流程: 1. 将待排序的序列按照中间位置分为两个子序列,分别排序。 2. 合并两个有序的子序列,形成一个新的有序序列。 具体实现时,我们可以使用递归或迭代两种方式。下面以递归方式来说明归并排序的实现。 算法10-12:递归实现归并排序 1. 将序列按中间位置分为左右两个子序列。 2. 对左右子序列分别递归调用归并排序。 3. 合并左右子序列。 算法10-13:合并两个有序序列 1. 定义一个新序列,长度为左右子序列之和。 2. 从两个子序列的头开始比较,将小的元素放入新序列中。 3. 将剩余的元素全部复制到新序列中。 算法10-14:归并排序时间复杂度 1. 分解阶段:将序列分为两个子序列,时间复杂度为O(logn)。 2. 合并阶段:合并两个有序序列,时间复杂度为O(n)。 3. 总时间复杂度为O(nlogn)。 归并排序虽然时间复杂度较低,但空间复杂度为O(n),需要额外的存储空间来存储临时序列。但相比其他排序算法归并排序具有稳定性,适合处理大规模数据的排序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

super--Yang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值