10归并排序算法


一、归并排序

在这里插入图片描述

如果两个数组有序,可以使用归并的方法使他们合并为一个有序数组:
在这里插入图片描述

归并的的思路是:
将数组拆分成若干个有序的子序列(一般是只有一个数的子序列),然后两两合并为有序的序列,最终得到完全有序的序列。
在这里插入图片描述

1.1.归并排序的递归实现

//归并排序的子函数
//这个函数将左右区间进行归并
void _MergeSort(int* a, int left, int right,int*tmp)
{
	if (left >= right)
	{
		return;
	}
	int mid = (left + right) / 2;
	//区间被分成了[left,mid][mid+1,right],如果这两个区间有序,即可以归并
	_MergeSort(a, left, mid, tmp);//对左区间进行递归归并
	_MergeSort(a, mid + 1, right, tmp);//对右区间进行递归归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;//tmp数组的下标
	将两段子区间进行归并,归并结果放在tmp中
	while (begin1 <= end1 && begin2 <= end2)//左右区间有一个结束,循环就结束
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}
	//当遍历完其中一个区间,将另一个区间剩余的数据直接放到tmp的后面
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}	
	//拷贝回去
	for (int i = left; i <= right; i++)
	{
		a[i] = tmp[i];
	}

}
//归并排序
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));//创建一个临时存放数据的数组
	_MergeSort(a, 0, n - 1, tmp);//调用归并函数的子函数
	free(tmp);

}

1.2.归并排序的非递归实现

递归采用的是分治思想,即将大问题化成小问题。通过将一个大的左右区间不断拆分成若干小的有序的左右子区间。
那么非递归就可以先将小区间有序合并,然后再合并大区间,最后将整个数组都合并成有序。
通过 增量 (gap)就可以实现对每个区间的划分:
在这里插入图片描述

不过需要注意一些特殊情况:

  • 比如有六个元素或者五个元素时,当gap==2,可能会出现右区间不存在的情况,此时就不需要对左区间和右区间进行归并了,因为左区间已经有序。直接跳出此次归并即可
    在这里插入图片描述
    在这里插入图片描述

  • 当有7个元素时,右区间的数可能会不全,也就是end指针越界了,此时我们需要将end指针改回最后一个位置,也就是将end指向8

在这里插入图片描述

//归并排序的非递归
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	int gap = 1;//gap一开始为1,每个子区间只有1个数
	while (gap < n)
	{
		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 (begin2 >= n)
			{
				break;//如果右半区间不存在,那就不用归并了
			}
			//归并过程中右半区间算多了
			if (end2 >= n)
			{
				end2 = n - 1;//将右半区间的end2修正到最后
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
			     //将较小的数放入tmp
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}
			//当遍历完其中一个区间,将另一个区间剩余的数据直接放到tmp的后面
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			//拷贝回去
			for (int j = i; j <= end2; j++)//边界修正,防止拷贝进去随机值
			{
				a[j] = tmp[j];
			}
		}
		gap *= 2;//下一趟需合并的子序列中元素的个数翻倍
	}	
	free(tmp);
}

1.3.时间空间复杂度分析

子函数_MergeSort函数的时间复杂度为O(N),因为代码中有2个长度为n的循环(非嵌套),所以时间复杂度则为O(N);
而_MergeSort函数一共被调用了log2N次。
所以时间复杂度为O(NlogN)

归并的空间复杂度就是那个临时的数组和递归时压入栈的数据占用的空间:N+log2N;
所以空间复杂度为: O(N)

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天也要写bug、

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值