数据结构进阶篇 之 【并归排序】(递归与非递归实现)详细讲解

在这里插入图片描述
都说贪小便宜吃大亏,但吃亏是福,那不就是贪小便宜吃大福了吗

一、并归排序 MergeSort

1.基本思想

2.实现原理

3.代码实现

4.归并排序的特性总结

二、非递归并归排序实现

三、完结撒❀

–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–

一、并归排序 MergeSort

1.基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序核心步骤:

在这里插入图片描述

2.实现原理

其实先还是运用了递归大事化小的原理,假设我们要对数组arr[i]进行排序,我们可以将该数组均分为两个数组(从中间划分)arr[0] ~ arr[i/2],arr[i/2+1] ~ arr[i],要使arr数组有序可以先将arr[0] ~ arr[i/2]和arr[i/2+1] ~ arr[i]有序,而arr[0] ~ arr[i/2]和arr[i/2+1] ~ arr[i]有序之后可以从头到尾比较两个数组范围内的每一个值,按照要求所排的顺序往新开辟的另一个数组tmp中拷贝,最后再从tmp数组中拷贝到arr数组中完成排序,这就是归并过程。

而要想arr[0] ~ arr[i/2]和arr[i/2+1] ~ arr[i]有序,可以先让arr[0] ~ arr[i/4]和arr[i/4+1]~arr[i/2]有序,arr[i/2+1] ~ arr[mini](中间位置)和arr[mini+1] ~ arr[i]有序,而arr[0] ~ arr[i/4]有序就需要…

这样就可以用递归来实现,数组一直向下递归“分割”,当数组被分为一个数据的时候就可以返回进行合并

所以把并轨排序递归实现按照二叉树来看,完成排序的核心要求就是递归“分割”后,要实现上层数组有序就先要实先下层数组也为有序,完成最后一层数据有序向上回溯即可完成整个数组有序

返回合并过程动态图解:
在这里插入图片描述

3.代码实现

void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin == end)
	{
		return;
	}

	int min = (end + begin) / 2;

	_MergeSort(a, begin, min, tmp);
	_MergeSort(a, min+1, end, tmp);

	//并归
	int begin1 = begin, end1 = min;
	int begin2 = min + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}

		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}

	memcpy(a+begin, tmp+begin, sizeof(int)*(end - begin + 1));//注意
}

//并归排序 O(N*logN)
void MergeSort(int* a, int n)
{
	assert(a);

	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

	_MergeSort(a, 0, n - 1, tmp);

	free(tmp);
	tmp = NULL;
}

4.归并排序的特性总结

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2. 时间复杂度:O(N*logN)

在这里插入图片描述

3. 空间复杂度:O(N)

4. 稳定性:稳定

二、非递归并归排序实现

对于归并的非递归实现我们直接利用循环就可以解决。

我们可以将要并归排序的数组直接看成1个数据为一组往下进行并归,并归后再两个数据为一组往下进行并归,直到将数组并归完成为止,见下图:

在这里插入图片描述
此过程与递归过程中的回溯过程一样,只是不再需要进行递归分解

这个看起来容易,但实现起来并不简单,我们需要注意许多细节在这里

如何通过一次循环来完成一层数据的并归呢?

以第一层一个数据为一组为例,我们先要完成对数据的控制,开始我们就先要控制10,6一起进行并归,而后再控制7,1进行并归,直到第一层结束为止

完成上述代码实现之后,我们只需在上述代码外再套一层循环来控制gap,即可实现整个代码逻辑

但需要注意的是并不是所有数据最终都会“组队成双”实现归并,如下:

加粗样式
上面数组中最后一组便是一个例子

我们把每次组队并归的两组中,第一组的队头为begin1,队尾为end1,第二组的队头为begin2,队尾为end2
因为begin1不可能越界,那么出现上面问题一共有三种情况:

1.end1越界

当end1越界的话,那么表示这次组队肯定是没有第二组,并且第一组也部分越界,而第一组如果是一个数据的好不用进行并归排序,如果超过一个数据说明在之前已经并归排序完毕,数据也是有序的,所以也不再需要进行并归排序。end1越界直接跳出循环不进行并归排序即可。

2.begin2越界

当begin2越界,情况其实与end1越界一样,不同的可能就是第一组数据都没有越界,没有第二组数据,这时也不需要进行并归排序,直接跳出循环即可。

3.end2越界

当end2越界,表明有第一组数据,第二组数据越界,此时只要将第二组数据的范围缩小到原数组的队尾即可,也就是end2 = n - 1,然后再将两组数据进行并归排序即可(并归排序的实现只要求所给的两组数据有序,不要求数据数量是否相等)。

代码实现:

//并归排序非递归
void MergeSortNonR(int* a, int n)
{
	assert(a);

	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

	int gap = 1;

	while (gap < n)
	{
		for (int j = 0; j < n; j+=gap*2)
		{
			int begin1 = j, end1 = begin1 + gap - 1;
			int begin2 = begin1 + gap, end2 = begin2 + gap - 1;

			if (end1 >= n || begin2 >= n)
			{
				break;
			}

			if (end2 >= n)
			{
				end2 = n - 1;
			}

			int i = j;

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[i++] = a[begin1++];
				}

				else
				{
					tmp[i++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[i++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[i++] = a[begin2++];
			}

			memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));
		}
		gap *= 2;
	}

	free(tmp);
	tmp = NULL;
}

三、完结撒❀

如果以上内容对你有帮助不妨点赞支持一下,以后还会分享更多编程知识,我们一起进步。
最后我想讲的是,据说点赞的都能找到漂亮女朋友❤
在这里插入图片描述

  • 68
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 36
    评论
插入排序和归并排序是两种常见的排序算法。 插入排序是一种简单直观的排序算法,其基本思想是将待排序的元素插入到已排序的序列中,从而得到一个新的有序序列。具体实现时,从第二个元素开始,将当前元素与已排序的序列从后往前比较,找到合适的位置将其插入。代码如下: ```python def insertion_sort(arr): for i in range(1, len(arr)): key = arr[i] j = i - 1 while j >= 0 and arr[j] > key: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr ``` 归并排序是一种分治法的经典排序算法,其基本思想是将待排序的序列拆分成两个子序列,分别对两个子序列排序,然后将两个有序的子序列合并成一个有序序列。具体实现时,不断归地将序列拆分成两个子序列,直到拆分到单个元素,然后再将子序列合并。代码如下: ```python def merge_sort(arr): if len(arr) <= 1: return arr mid = len(arr) // 2 left_arr = merge_sort(arr[:mid]) right_arr = merge_sort(arr[mid:]) return merge(left_arr, right_arr) def merge(left_arr, right_arr): result = [] i = j = 0 while i < len(left_arr) and j < len(right_arr): if left_arr[i] < right_arr[j]: result.append(left_arr[i]) i += 1 else: result.append(right_arr[j]) j += 1 result.extend(left_arr[i:]) result.extend(right_arr[j:]) return result ``` 以上是使用Python实现插入排序和归并排序的代码。这两种排序算法在实际运用中都有各自的优缺点,具体使用哪种算法要根据实际情况和需求来决定。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值