C语言讲解归并排序

本文详细介绍了归并排序的递归和非递归版本,包括其基本思想、保证子序列有序的方法、流程图以及代码示例。重点讲解了递归过程中的temp数组使用和非递归版本的gap控制策略。
摘要由CSDN通过智能技术生成

归并排序:归并排序有递归版和非递归版,在学习归并排序之前,我希望你对合并两个有序数组有一定的了解,这可以帮助你理解归并排序。

归并排序递归版

归并排序递归版的思想:需要一个temp数组,先将有序的最小子序列两两归并到temp数组,得到有序的子序列段,接着把有序的子序列段拷贝回原数组。然后把子序列段看成子序列,再两两归并到temp数组,得到更大的子序列段,再拷贝回原数组;重复上述过程即可。

怎么保证子序列段有序呢?

最小子序列开始归并,有序最小子序列中只有一个数据。就相当于把待排序的整体数据分解为一个一个的数据,然后两两归并,两两归并完之后得到的子序列段中有两个数据,再把两个数据组成的子序列段看成子序列,再次两两归并,就能保证子序列段有序了。

归并排序大概流程图:

归并排序递归版代码:递归版本的归并排序需要一定的二叉树的知识支撑理解,需要掌握二叉树的几个遍历;这里更像是后续遍历,此处不在赘述。递归版本的归并排序不用担心越界问题。

void SubMergeSort(int* arr, int* temp, int begin, int end)
{
	if (begin >= end)//当begin大于end时区间不存在,相等时子序列段中只有一个数据。
		return;
	int mid = (end + begin) / 2;//递归分解整个待排序的数组
	// [begin, mid][mid+1, end]
	SubMergeSort(a, tmp, begin, mid);//递归左半区间
	SubMergeSort(a, tmp, mid+1, end);//递归右半区间
	// 归并到temp数据组,再拷贝回去
	int begin1 = begin, end1 = mid;//begin1到end1是一个子序列
	int begin2 = mid+1, end2 = end;//begin2到end2是一个子序列
	int index = begin;//index用于控制归并到temp数组中的位置
	while (begin1 <= end1 && begin2 <= end2)//归并过程,选小的尾插
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}
   //两组数据归并,总有一组数组没有全部进入temp数组,把剩余数据全部尾插到temp数组中
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}
	// 拷贝回原数组,拷贝回原数组也可以用循环。
	memcpy(arr + begin, temp + begin, (end - begin + 1)*sizeof(int));
}

void MergeSort(int* arr, int len)//arr表示待排序的数组,len表示数组长度
{
	int* temp = (int*)malloc(sizeof(int) * len);//归并两个子序列需要一个temp
	if (temp == NULL)
	{
		printf("malloc fail");
		return;
	}
	SubMergeSort(arr, temp, 0, len - 1);//实现排序的部分
	free(temp);//堆内存申请的空间一定要记得释放
}

归并排序非递归版

归并排序非递归版的思想:

非递归版本的归并排序的思想和递归版的差不多,区别在于少了递归分解待排序数组区间这一步,而是定义一个变量gap代表子序列中数据的个数,gap一开始等于1,代表一个数据和一个数据归并,归并完之后gap扩大两倍,此时gap等于2,代表两个数据和两个数据归并,重复上述过程,控制gap最大不超过数据个数的一半

非递归版代码:

void SubMergeSort(int* arr, int* temp, int len)
{
	int gap = 1;
	while (gap < len)
	{
		for (int i = 0; i < len; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			int index = i;
			if (end1 >= len)//end1越界,说明第二组数据不存在,这组数据可以不用排了
				break;
			if (begin2 >= len)//begin2越界,说明第二组数据不存在,这组数据可以不用排了
				break;
			if (end2 >= len)//end2越界,调整end2
				end2 = len - 1;
			//归并到temp数组
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] <= arr[begin2])
				{
					temp[index++] = arr[begin1++];
				}
				else
				{
					temp[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				temp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[index++] = arr[begin2++];
			}
			//拷贝回原数组
			memcpy(arr + i, temp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
}

void MergeSortNoR(int* arr, int len)
{
	int* temp = (int*)malloc(sizeof(int) * len);
	if (temp == NULL)
	{
		printf("malloc fail");
		return;
	}
	SubMergeSort(arr, temp, len);
	free(temp);
}

看到这了我得给你点个赞,希望可以帮助到你哦。

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值