归并排序(递归和非递归写法)

前言

  1. 本篇博客主要介绍排序算法中的归并排序(MergeSort),包括递归和非递归的实现。
  2. 代码实现:C语言

导航🗺🌎🏁:

目录

前言

归并排序(递归)

✨基本思想:  

🎆🎆实现步骤:

 🎉🎉🎉递归代码如下:

🔔🔔🔔🔔特性总结:

归并排序(非递归)

✨基本思想:  

🎆🎆基本步骤:

 🎉🎉🎉非递归代码如下:

🥕控制边界,每一次都进行归并(推荐)

🥕🥕控制边界,越界情况不归并



归并排序(递归)

✨基本思想:  

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


🎆🎆实现步骤:

🍋若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

归并过程类似于二叉树的后序遍历,关于二叉树的后序遍历相关知识:💫💫➡传送门

  1. 将长度为N的待排序列二分成左右两个待排序列
  2. 继续将左右两个待排序列二分,直到二分成左右子序列中只有一个数时,可以认为只有一个数的待排子序列有序
  3. 然后对左右两个待排子序列进行归并操作,一般是开辟一个长度为N的数组tmp,用于归并,归并好之后再拷贝回原数组
  4. 依次从最小子序列归并回去,直到完成最后一次归并,算法结束

gif演示:


 🎉🎉🎉递归代码如下:

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

	int mid = (begin + end) / 2;

	//[begin, mid] [mid, end] 分治递归,让子区间有序  类似二叉树的后序遍历
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	//归并 [begin, mid] [mid, end] 到tmp数组
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin1;
	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, (end - begin + 1) * sizeof(int));
}

void MergeSort(int* a, int n)
{
	assert(a);

	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	//assert(tmp);

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

	free(tmp);
}

🔔🔔🔔🔔特性总结:

  1. 归并排序的递归写法类比二叉树的后序遍历还是很好理解的
  2. 时间复杂度:O(N*logN),由于每次递归操作的区间划分基本上都是二分,所以效率会比快排好一点点,但额外开辟的空间也造成了一定的损耗
  3. 空间复杂度:O(N),这也是归并排序的缺点,故归并排序的思考更多的是解决在磁盘中的外排序问题。
  4. 稳定性:稳定

归并排序(非递归)

✨基本思想:  

        由于归并排序的递归写法是类似于二叉树的后序遍历,所以不能通过使用栈和队列进行模拟操作,归并排序的递归改非递归要使用循环操作


🎆🎆基本步骤:

        主要还是要通过控制循环变量来控制模拟递归过程,并且会有很多细节需要控制,特别是边界问题,归并的非递归还是很恶心的😅,总体而言难度较大,过多的细节,我这里也不好演示,下面直接上代码

 🎉🎉🎉非递归代码如下:

🥕控制边界,每一次都进行归并(推荐)

void mergesortnonr(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	int gap = 1;
	while (gap < n)
	{
		printf("gap=%d->", gap);
		for (int i = 0; i < n; i += 2 * gap)
		{
			// [i,i+gap-1][i+gap, i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			// 越界-修正边界
			if (end1 >= n)
			{
				end1 = n - 1;
				// [begin2, end2]修正为不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if (begin2 >= n)
			{
				// [begin2, end2]修正为不存在区间
				begin2 = n;
				end2 = n - 1;
			}
			else if(end2 >= n)
			{
				end2 = n - 1;
			}

			printf("[%d,%d] [%d, %d]--", begin1, end1, begin2, end2);

			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])//等于号保证稳定性
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}

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

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

		printf("\n");
		memcpy(a, tmp, sizeof(int)*n);

		gap *= 2;
	}

	free(tmp);
}

🥕🥕控制边界,越界情况不归并

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}

	// 休息11:48继续
	int gap = 1;
	while (gap < n)
	{
		//printf("gap=%d->", gap);
		for (int i = 0; i < n; i += 2 * gap)
		{
			// [i,i+gap-1][i+gap, i+2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			// end1越界或者begin2越界,则可以不归并了
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			else if (end2 >= n)
			{
				end2 = n - 1;
			}
			//printf("[%d,%d] [%d, %d]--", begin1, end1, begin2, end2);

			int m = end2 - begin1 + 1;
			int j = begin1;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}

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

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

			memcpy(a + i, tmp + i, sizeof(int) * m);
		}

		gap *= 2;
	}

	free(tmp);
}


学习记录:

  • 📆本篇博客整理于2022.7.10
  • 🎓作者:如何写出最优雅的代码
  • 📑如有错误,敬请指正🌹🌹
  • 🥂如果觉得写的不错,看完了别忘了点赞和收藏啊,感谢支持😏😏
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

如何写出最优雅的代码

感谢支持,我将继续努力!

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

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

打赏作者

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

抵扣说明:

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

余额充值