前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >归并排序详解

归并排序详解

作者头像
P_M_P
修改2024-01-23 07:54:57
710
修改2024-01-23 07:54:57
举报
文章被收录于专栏:算法P_M_P学习笔记算法

💡基本思想

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

💡图文介绍

💡动图演示

💡过程解释

分解为多个小区间

可以看到这种结构很像一棵完全二叉树,阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。

合并相邻有序子序列

💡代码实现

💡递归实现

分割区间 以每个区间的中间位置为交界处,将一个区间分割为两个区间。

代码语言:javascript
复制
int mid = (begin + end) / 2;
	//[begin,mid] [begin+1,end]
	//先分再合并
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid+1, end, tmp);

返回条件 当区间内只剩下一个元素时,我们可以认为该区间是有序的。

代码语言:javascript
复制
if (begin >= end)
	{
		return;
	}

合并

  1. 合并是在分割完之后进行的,类似于二叉树里面的后序遍历,在递归的回归过程进行合并区间。
  2. 合并时,每次取较小的数尾插到tmp数组里
  3. 合并结束后,将tmp数组拷贝会原数组。
代码语言:javascript
复制
//[begin,mid] [begin+1,end]  归并
	int begin1 = begin, end1 = mid;
	int begin2 = mid+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));

完整代码

代码语言:javascript
复制
void _MergeSort(int* a, int begin, int end,int* tmp)
{
	if (begin >= end)
	{
		return;
	}
	int mid = (begin + end) / 2;
	//[begin,mid] [begin+1,end]
	//先分再合并
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid+1, end, tmp);
	//[begin,mid] [begin+1,end]  归并
	int begin1 = begin, end1 = mid;
	int begin2 = mid+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));
}
//后序递归
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc!");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

💡非递归实现

基本方法: 非递归的实现方法是采用一种顺序合并,就是直接以一个数作为一个区间,然后进行两两合并,一趟结束后,再以两个数作为一个区间,将区间两两合并,以此类推。

代码语言:javascript
复制
int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//[begin1,end1][begin2,end2]

注意:这种区间合并的方法可能会造成越界访问,所以我们需要加判断条件,又因为这里是在一趟合并结束后进行拷贝了,所以当begin2和end1大于等于n时,可以直接跳出这层循环,而当end2>=n时,则将end2=n-1,再进行合并。

代码语言:javascript
复制
//防止越界
			if (end1 >= n || begin2 >= n)
				break;
			if (end2 >= n)
			{
				end2 = n - 1;//直接合并
			}

完整代码

代码语言:javascript
复制
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc!");
		return;
	}
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//[begin1,end1][begin2,end2]
			//防止越界
			if (end1 >= n || begin2 >= n)
				break;
			if (end2 >= n)
			{
				end2 = n - 1;//直接合并
			}
			printf("[%2d,%2d][%2d, %2d] ", 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 (j<n&&begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));//防止越界拷贝
		}
		printf("\n");
		gap *= 2;
	}
	free(tmp);
}

💡总结

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 💡基本思想
  • 💡图文介绍
    • 💡动图演示
      • 💡过程解释
      • 💡代码实现
        • 💡递归实现
          • 💡非递归实现
          • 💡总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档