归并排序详解

今天我来讲一下归并排序以及归并排序的非递归实现形式,首先提交以下代码

普通归并

cpp 复制代码
void _MergeSort(int* arr, int* tmp, int left, int right)
{
	if (left >= right)
		return;
	//归
	int begin = left;
	int end = right;
	int mid = begin + (end - begin) / 2;
	//[begin,mid][mid+1,right]
	_MergeSort(arr, tmp, begin, mid);
	_MergeSort(arr, tmp, mid + 1, right);
	//并
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;

	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		while (begin2 <= end2 && arr[begin1] >= arr[begin2])
		{
			tmp[i] = arr[begin2];
			begin2++;
			i++;
		}
		while (begin1 <= end1 && arr[begin2] > arr[begin1])
		{
			tmp[i] = arr[begin1];
			begin1++;
			i++;
		}

	}
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));

}
void MergeSort(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc");
		return;
	}
	_MergeSort(arr, tmp, 0, n - 1);
	free(tmp);
	tmp = NULL;
}

非递归

cpp 复制代码
void MergeSortNon(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc");
		return;
	}
	int begin = 0;
	int end = n - 1;
	int mid = begin + (end - begin) / 2;

	//[begin1,end1][begin2,end2]
	//end1越界
	//begin2越界
	//end2越界

	for (int gap = 1; gap < n; gap *= 2)
	{
		for (int j = 0; j < n; j += 2 * gap)
		{
			int begin1 = j;
			int end1 = begin1 + gap - 1;
			int begin2 = end1 + 1;
			int end2 = begin2 + gap - 1;
			int i = j;
			if (end1 >= n|| begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			
				while (begin1 <= end1 && begin2 <= end2)
				{
					while (begin2 <= end2 && arr[begin1] >= arr[begin2])
					{
						tmp[i] = arr[begin2];
						begin2++;
						i++;
					}
					while (begin1 <= end1 && arr[begin2] > arr[begin1])
					{
						tmp[i] = arr[begin1];
						begin1++;
						i++;
					}

				}
				while (begin1 <= end1)
				{
					tmp[i++] = arr[begin1++];
				}
				while (begin2 <= end2)
				{
					tmp[i++] = arr[begin2++];
				}
				memcpy(arr + j, tmp + j, sizeof(int) * (2 * gap));
			}
		/*memcpy(arr, tmp, sizeof(int) * n);*/

			
	}
	
	


	free(tmp);
	tmp = NULL;
}

所谓归并排序,自然是分为归和并,我简单演示一下过程

如此图,从中间拆分,然后得到的每组数据再从中间拆分,直到只剩一个数据,这其实就已经有序了,因为一个数据本身就是有序,然后是并,每次选一个最小值插入新数组,这样可以保证局部有序,直至完成归并。

显然,这是一个分而治之的思想,要用递归实现,递归实现的主要逻辑是区间的拆分,后续有序数组的合并过程和我们之前讲的有序链表合并相似,但略有不同,不同点在于归并排序需要新建一个新数组。

然后来讲一下非递归,与上一讲快速排序不同的是,归并的非递归并没有用到栈这一数据结构,其实想想也容易明白,虽然我们这里的黑核心仍然是拆分区间,但拆分后我们还需要合并,你拿栈去搞那拆分一次出栈,回来合并的时候你也找不到之前区间了对吧,所以我们这里直接给他改成循环。

当然你说非要用栈行不行呢?其实是可以的,只不过你得用两个栈拿来存储区间,这里就不多介绍了。

那么非递归的核心又是什么呢?

看一下这张图,我们直接前后取相同数进行归并,不从中间拆开,那这样,就需要定义一个变量gap拿来指明每一组的数据个数,比如90 23合并,那90自己就是一组,gap就等于1,同理,往下就不过多赘述了。

其实和递归代码区别不大,但细节方面还是需要处理,首先gap取值,一定不能>=n吧,你比原数组都长,哪来这么多元素呢?那你能等于n吗?等于1的时候是11合并,合并成两个元素的有序数组,那你等于n呢?合并成2n个元素的有序数组?哪来这么多元素给你合并,其次,怎样算是一次gap合并完成呢?就是从前往后都归并一次对吧,那你是不是要在写一层循环,用于标注这次合并是否整体结束,再往这个循环里走,你怎么确定合并各个小组之间的起始和终止?要利用gap的关系进行标注。

到这里看似没啥问题了,但其实还是不行,gap我们设置的是倍乘增加,这种只适用于有2的次方个元素个数的数组,否则会越界,这个越界还要处理三种情况,第一是end1越界,第二是begin2,第三是end2,前两种是一种情况,后者则自己一组,为什么呢?当你end1越界的时候,begin2和end2肯定越界了,end1越界了意味着什么呢?小组成员不够呗,我拿10个元素的数组举例,你最后一步肯定begin1==0end1>10,此时这个数组已经有序了,通过上一组82合并已经合并好了,然后再往下才会出现这情况,同理begin2越界也是如此,所以直接break就好。那第三种情况咋办呢?这就是上边说的82合并,第一组没问题,第二组元素不够,那你第二组有多少就给多少呗,直接把end2置为n-1即可

相关推荐
科技观察哨1 小时前
国产纳米定位平台与精密位移台量产能力图谱(2026):Z轴压电、XYθZ、六自由度并联与光刻/检测/装配选型指南
网络·人工智能·算法
鶴哥只手遮天2 小时前
《OpenFOAM 源码深度剖析》系列-第 3 章 字典系统、IO 体系与对象注册表
算法·开源
鶴哥只手遮天2 小时前
《OpenFOAM 源码深度剖析》系列-第 2 章 wmake:OpenFOAM 自研构建系统与编译配置体系
算法·开源
用户81849100742162 小时前
我把工位上的“AI牛马”养成了:一个技术人的 AiPy 实战手记
算法
Tisfy2 小时前
LeetCode 3727.最大交替平方和:排序 (双指针可选)
算法·leetcode·题解·贪心·数组·排序
鶴哥只手遮天2 小时前
《OpenFOAM 源码深度剖析》系列-第 9 章 有限体积离散框架:`fvMatrix`、`fvSchemes` 与 `fvSolution`
算法·开源
鶴哥只手遮天2 小时前
《OpenFOAM 源码深度剖析》系列-第 7 章 网格与拓扑体系:`polyMesh`、`primitiveMesh` 与 patch
算法·开源
鶴哥只手遮天2 小时前
《OpenFOAM 源码深度剖析》系列-第 5 章 数值基元、维度系统与 `Field` 体系
算法·开源
鶴哥只手遮天2 小时前
《OpenFOAM 源码深度剖析》系列-第 10 章 离散算子与插值格式:`grad` / `div` / `laplacian` / `ddt` 的实现
算法·开源