归并排序(c语言) 递归非递归 写法

归并排序 (MergeSort)

原理:假设初试序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2(或为1  奇数个情况)的有序子序列;两两在归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2两路并归排序。

递归写法:

void MergeSortMethod1(int* head,int*arr, int left,int right)
{
	if (left >= right)
		return;
	int middle = (left + right) / 2;
	MergeSortMethod1(head, arr, left, middle);
	MergeSortMethod1(head, arr, middle+1, right);
	int x = left, y = middle + 1;
	int i = 0;
	while (x <= middle && y <= right)
	{
		if (head[x] < head[y])
		{
			arr[i] = head[x];
			x++;

		}
		else
		{
			arr[i] = head[y];
			y++;
		}
		i++;
	}
	while (x <= middle)
	{
		arr[i] = head[x];
		x++;
		i++;
	}
	while (y <= right)
	{
		arr[i] = head[y];
		y++;
		i++;
	}
	memmove(head+left, arr, (right - left + 1) * sizeof(head[0]));
}

也很好理解吧       递归思想嘛 任务分解          

下面是非递归写法:

递归改非递归有两种方式,之前提到过了    要么采用栈(队列)辅助  要么采用循环   

这里显然采用循环比较合适 

非递归和递归思路是完全反着的,从归并的定义入手  开始是一一归并 然后是二二归并 ……

所以思路就有了 ,直接先一个一个归并 在两个两个  在四个四个 ……

//非递归写法
// gap是 每次归并单个组的元素个数   1-2-4-8-16…  
void MergeSortMethod2(int* head, int* arr, int left, int right)
{
	int gap = 1;
	while (gap < (right - left + 1))
	{
		
		int j = 0;
		for (j = left; j <= right; j = j + 2 * gap)
		{
			int size = 0;  //记录写入arr的个数
			int left1 = j, right1 = j + gap - 1;
			int left2 = j + gap, right2 = j + 2 * gap - 1;
			int x = left1, y = left2;
			int i = 0;
			while (x<=right&&y<=right&&x <= right1 && y <= right2)
			{
				if (head[x] < head[y])
				{
					arr[i] = head[x];
					size++;
					x++;

				}
				else
				{
					arr[i] = head[y];
					size++;
					y++;
				}
				i++;
			}
			while (x <= right&& x <= right1)
			{
				arr[i] = head[x];
				size++;
				x++;
				i++;
			}
			while (y <= right && y <= right2)
			{
				arr[i] = head[y];
				size++;
				y++;
				i++;
			}
			memmove(head + left1, arr, size * sizeof(head[0]));
		}
		gap = gap * 2;
	}

	free(arr);

}

主要解释一下这里, 就是怎么确定待比较的两个自序列   左右区间

这里的坐标问题就解决了     对于后面子序列比原序列长的话 

其实不用去修订最后两个自序列的左右坐标  这样反而麻烦了   直接在后面归并的时候 加一个条件  他们都得小于 right就行   就避免了 越界的问题     下面两个while就是把那个还没有排完的序列  在写进去

为什么有两个 ?   因为不知道哪个排完了   直接两个在验证一遍就行

下面在介绍一下 增强版本的  非递归  

上面的非递归你发现没     memmove做了很多无用功  每次归并完就要拷到原序列  再继续在原序列上面 拆下来 归并到临时数组  在拷回去    是不是很鸡肋

所以直接采用    反复归并  

思路就是   原序列归并到临时数组     在临时数组归并到 原序列(这次归并 gap*2 才是有用的归并)

这样一下 少了一倍时间      就不用每次拷回去再归并了   

原码:


//封装函数
void Merge(int* destination, int* source, int gap,int left,int right)
{
	int i = 0;
	for (int j = left; j <= right; j = j + 2 * gap)
	{
		int left1 = j, right1 = j + gap - 1;
		int left2 = j + gap, right2 = j + 2 * gap - 1;
		int x = left1, y = left2;
		while (x <= right && y <= right && x <= right1 && y <= right2)
		{
			if (source[x] < source[y])
			{
				destination[i] = source[x];
				x++;

			}
			else
			{
				destination[i] = source[y];
				y++;
			}
			i++;
		}
		while (x <= right && x <= right1)
		{
			destination[i] = source[x];
			x++;
			i++;
		}
		while (y <= right && y <= right2)
		{
			destination[i] = source[y];
			y++;
			i++;
		}
	}
}


//非递归写法2 增强版
void MergeSortMethod3(int* head, int* arr, int left, int right)
{
	int gap = 1;
	while (gap < (right - left + 1))
	{
		Merge(arr, head, gap, left, right);
		gap = gap * 2;
		Merge(head, arr, gap, left, right);
		gap = gap * 2;
	}
	free(arr);
}

   这里就不多解释了  看到这里 说明你已经对这个理解很深了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值