归并排序详解

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

普通归并

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即可

相关推荐
東隅已逝,桑榆非晚10 小时前
算法2. 双指针法-复写零
c++·笔记·学习·算法
云析赢指标公式网411 小时前
文华财经指标源码笔记:WINNER筹码函数、控盘变量与MACD变体的调试记录
java·笔记·算法
CC数分12 小时前
统计学基础|先把问题问对:数据分析的六大类型
大数据·人工智能·算法
地平线开发者13 小时前
CLIP 的训练机制:从对比学习到图文对齐
深度学习·算法·自动驾驶
PaperAccept13 小时前
【研知有术论文发表】材料科学一区期刊,影响因子30+的纳米科技SCI,审稿严格但有效率
大数据·数据结构·科技·量子计算
hetao173383714 小时前
2026-11-02 hetao1733837 的刷题记录
c++·算法
Geektec14 小时前
IMU 选型完全指南:从硬件参数到算法融合的系统工程视角
算法·imu·陀螺仪·加速度计·元器件选型
CQU_JIAKE14 小时前
11.2【A】
算法
Tairitsu_H14 小时前
[C++高阶数据结构·图论下] 最短路径:为什么“挑最小的”一定正确?
数据结构·c++·图论
Godspeed Zhao14 小时前
从零开始学AI21——决策树
算法·决策树·机器学习