排序之归并排序

归并排序是递归排序的一种,所以今天我们就来浅谈一下归并排序的递归方式和非递归方式

归并排序的原理

归并排序的原理就是先拆分,然后再归并。可能很抽象,但是假如我们从中间点开始,左边有序,右边也有序,那么将左边和右边归并即可,那么我们怎么知道左右有序呢?那如果左边和右边只剩下一个数了,是不是就相当于有序了呢?如图:
在这里插入图片描述
那么递归我们解决了,归并怎么做到呢?这里我们需要新开一个数组,存放我们已经有序的数,并且在最后一次的时候完整复制回去。如图:只是一趟左递归。

一趟左递归
所以由图可见,我们需要先找中间数,然后递归,然后再归并这个顺序

归并排序的实现(内有详细注释)

void _MergeSort(int *a,int begin,int end,int *tmp)
{
	if (begin >= end)
	{
	//递归结束标志,当只剩下一个数的时候就不需要在递归了
	//一个数的时候已经是有序了
		return;
	}
	int mid = (begin + end) / 2;//找中间数
	//[begin , mid] [mid+1 , end]区间

	//递归分区间
	_MergeSort(a, begin, mid,tmp);//左区间
	_MergeSort(a, mid+1 ,end, tmp);//右区间

	int begin1 = begin;
	int end1 = mid ;//左区间
	int begin2 = mid + 1;
	int end2 = end;//右区间
	int i = begin;//i是我们新建数组的下标
	//假设在右半边,开始的下标不是0,所以这里i不能为0
	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));
	//为什么要加begin,是因为每次开始不一定都是从0开始的,所以需要找每次开头的数组位置,所以要加begin
}
void MergeSort(int* a, int n)
{
	//为什么要重写一个_MergeSort而不直接递归呢?
	//因为我们要开空间来递归用,但是递归的话每次都会开空间!不能每次都开空间!
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	_MergeSort(a,0,n-1,tmp);//具体实现
	free(tmp);

}

归并排序的非递归方式

归并排序是后序递归方式,所以如果使用平常的栈或队列是不行的!栈是可以完成递归,但并不能做到归并。所以我们需要利用循环的方式来实现。

实现方式:因为我们提到一个的时候是有序,所以我们可以按分组的方式来gap=1的时候,一个一个归并,gap=2的时候两个两个归并…
在这里插入图片描述

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	

	int gap = 1;//从1开始分
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			//为什么是i += 2 * gap?
			//因为以begin1-end1 和begin2-end2这个区间已经归完了
			//我们需要跳到下一个区间内
			int begin1 = i;
			int end1 = i + gap - 1;
			//为什么-1?因为是下标
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;

			if (end1 >= n || begin2 >= n)
			{
				//会存在越界情况
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			int j = begin1;
			//tmp的下标
			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) * (end2 - i + 1));
		}
		gap *= 2;//由图可得每次gap都是2倍增加
	}
	free(tmp);

}

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值