【数据结构1.0】归并排序

本博客以排升序为例介绍归并排序! 

目录

1.归并排序的递归写法

2.归并排序的非递归写法

 3.归并排序特性


 我们先看一个问题:有两组均为升序的数组要合并到一组数组当中,要求合并后的数组也是升序的,怎么办?

办法:从头开始遍历两组数组,找“小”尾插到合并数组当中。举例如图:

这其实是归并的思想! 


归并排序的基本思想:

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

动图展示归并排序:

1.归并排序的递归写法

利用递归不断将需排序数组分解成左右子数组,直到左右子数组元素个数为1停止。再利用归并思想合并子数组到额外空间上,将合并好的子数组从额外空间拷贝回需排序数组相应位置。

void _MergeSort(int* a, int* tmp, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int mid = (begin + end) / 2;
	_MergeSort(a, tmp, begin, mid);
	_MergeSort(a, tmp, mid + 1, end);
	//分解成[begin mid]和[mid+1 end]两部分归并
	int i = begin,n1 = begin, n2 = mid+1;
	while (n1<=mid&&n2<=end)
	{
		if (a[n1] > a[n2])
		{
			tmp[i++] = a[n2++];
		}
		else
		{
			tmp[i++] = a[n1++];
		}
	}
	while (n1<=mid)
	{
		tmp[i++] = a[n1++];
	}
	while (n2<=end)
	{
		tmp[i++] = a[n2++];
	}
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
//归并排序排升序
void MergeSort(int* a, int begin, int end)
{
	int* tmp = (int*)malloc(sizeof(int) * (end - begin + 1));
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	_MergeSort(a, tmp, begin, end);
}

调用试试:

int main()
{
	int a[] = { 1,2,8,9,7,5,33 };
	MergeSort(a, 0, sizeof(a) / sizeof(a[0])-1);
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

没问题吧。。。 

2.归并排序的非递归写法

非递归其实也很简单,拿这组需排序数组举例来说:

每组1个元素,每两组利用归并思想归并:10和6归并;7和1归并;3和9归并;4和2归并。

每组2个元素,每两组利用归并思想归并:6、10和1、7归并; 3、9和2、4归并。

每组4个元素,每两组利用归并思想归并:1、6、7、10和2、3、4、9归并。

归并完就ok了!

当然,每两组归并的时候是归并到额外空间上的,所以每两组归并完成后记得将这两组数据拷贝回需排序数组相应位置!

void MergeSortNonr(int* a,int begin,int end)
{
	int sum = end - begin + 1;
	int* tmp = (int*)malloc(sizeof(int) * sum);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	int gap = 1;
	while (gap < sum)
	{
		for (int j = begin; j < sum; j += 2 * gap)
		{
			int n = j;
			//[begin1 end1][begin2 end2]两部分归并
			int begin1 = j, end1 = j + gap - 1;
			int begin2 = j + gap, end2 = j + 2 * gap - 1;
			if (end1 >= sum || begin2 >= sum)//[begin2 end2]这部分完全越界了,不用归并
			{
				break;
			}
			if (end2 >= sum)//[begin2 end2]这部分部分越界,修正end2即可继续归并
			{
				end2 = sum - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[n++] = a[begin1++];
				}
				else
				{
					tmp[n++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[n++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[n++] = a[begin2++];
			}
			memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));
		}
		gap *= 2;
	}
}

调用一下吧: 

int main()
{
	int a[] = { 1,2,8,9,7,5,33 };
	MergeSortNonr(a, 0, sizeof(a) / sizeof(a[0])-1);
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

没问题滴。。。

 3.归并排序特性

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2.不管哪种写法时间复杂度均为:O(N*logN) 。

3. 不管哪种写法都要用到大小相同于需排序数组大小的额外空间,所以空间复杂度:O(N)。

4.归并排序可以稳定也可以不稳定,关键在于代码的实现。如果[begin mid]和[mid+1 end]两部分归并的时候遇到相同的数据让[begin mid]那个数据尾插到额外空间的话就是稳定的;反之,如果让[mid+1 end]那个数据尾插到额外空间的和就是不稳定的。

谢谢您的阅读!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

X_chengonly

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

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

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

打赏作者

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

抵扣说明:

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

余额充值