【数据结构初阶之归并排序】:归并排序的递归和非递归实现(用C语言实现,附图分析)

归并排序

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

递归实现归并排序

根据归并排序的思想我们可以采用分而治之,即用递归来解决,把问题不断分割成子问题,分割到不可分割为止。而递归实现归并排序类似于二叉树的后序遍历,我们需要先解决小区间再到大区间;
所以我们的思路如下:我们先求出序列的中间元素下标,然后以中间元素为中心,把序列分为左右区间;接着又对左右区间进行递归分割,分割到不能分割为止就进行排序,排序后就不断递归返回就可以了。
图片分析如下:
在这里插入图片描述
代码实现如下:

#include <stdio.h>
#include <stdlib.h>

//递归实现归并排序
void _MergerSort(int* p, int left, int right, int* tmp)
{
    
    
	//当只有一个元素或者区间不存在就不要递归下去了
	if (left >= right)
	{
    
    
		return;
	}
	//求出中间元素的下标
	int midi = (left + right) / 2;
	//对左右区间进行分割区间
	_MergerSort(p, left, midi, tmp);
	_MergerSort(p, midi + 1, right, tmp);

	//对左右区间进行归并排序
	int begin1 = left, end1 = midi;
	int begin2 = midi + 1, end2 = right;
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
    
    
		if (p[begin1] < p[begin2])
		{
    
    
			tmp[i++] = p[begin1++];
		}
		else
		{
    
    
			tmp[i++] = p[begin2++];
		}
	}

	//检查哪个区间有剩余就往新数组里面拷贝
	while (begin1 <= end1)
	{
    
    
		tmp[i++] = p[begin1++];
	}
	while (begin2 <= end2)
	{
    
    
		tmp[i++] = p[begin2++];
	}

	//归并好后,要把归并好的元素拷贝回去原来的数组
	//如果不拷贝回去,后面的归并区间就不是有序的,
	//无法达到左右区间都有序,所以一定要拷贝回原数组
	int j = 0;
	for (j = left;j <= right; j++)
	{
    
    
		p[j] = tmp[j];
	}

}

//归并排序
void MergerSort(int* p, int n)
{
    
    
	//先开辟等同原数组大小的新数组
	//把原数组的元素往新数组中归并
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
    
    
		exit(-1);
	}
	//归并排序子函数
	_MergerSort(p, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}

//打印数组函数
void Print(int* p, int n)
{
    
    
	int i = 0;
	for (i = 0; i < n; i++)
	{
    
    
		printf("%d ", p[i]);
	}
	printf("\n");
}

int main()
{
    
    
   //三组测试用例
	int arr1[] = {
    
     4,1,3,2,9,8,6,5,7 };
	int sz1 = sizeof(arr1) / sizeof(arr1[0]);

	int arr2[] = {
    
     5,8,44,22,0,5,8,1,3 };
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);

	int arr3[] = {
    
     2,5,99,3,8,11,33,62 };
	int sz3 = sizeof(arr3) / sizeof(arr3[0]);

	MergerSort(arr1, sz1);
	Print(arr1, sz1);

	MergerSort(arr2, sz2);
	Print(arr2, sz2);

	MergerSort(arr3, sz3);
	Print(arr3, sz3);
	return 0;
}

代码执行结果:都能排序成功
在这里插入图片描述

非递归实现归并排序

归并排序的递归是有缺点的,如果当数据量太大了,函数不断地开辟栈帧很可能会造成栈溢出,所以为了解决这个问题我们还可以用非递归来实现归并排序,不过非归并排序的过程就和归并实现不同了;
非递归实现不用分左右区间,先是2个2个归并,然后是4个4个归并,然后8个8个归并……所以这个过程不难,但是边界是非常难控制的,我们得对边界进行控制在有效的范围内才行,图片分析如下:
在这里插入图片描述
代码实现如下:

#include <stdio.h>
#include <stdlib.h>

//非递归实现归并排序
void MergerSortNonR(int* p, int n)
{
    
    
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
    
    
		exit(-1);
	}
	int gap = 1;
	int i = 0;
	while (gap < n)
	{
    
    
	    //以间隔为gap的两区间进行归并
		for (i = 0; i < n; i += 2 * gap)
		{
    
    
			//利用i来产生区间
			//[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;
			int j = i;
			//修改边界,有可能越界
			//i不可能越界,控制好end1和end2就可以了
			if (end1 >= n)
			{
    
    
				end1 = n - 1;
			}
			//如果begin2越界,end2必定越界,所以修改end2就可以
			if (end2 >= n)
			{
    
    
				end2 = n - 1;
			}
			//对两区间进行归并
			while (begin1 <= end1 && begin2 <= end2)
			{
    
    
				if (p[begin1] < p[begin2])
				{
    
    
					tmp[j++] = p[begin1++];
				}
				else
				{
    
    
					tmp[j++] = p[begin2++];
				}
			}

			while (begin1 <= end1)
			{
    
    
				tmp[j++] = p[begin1++];
			}
			while (begin2 <= end2)
			{
    
    
				tmp[j++] = p[begin2++];
			}
		}
		//当以gap为间隔归并完后要拷贝回原数组
		int j = 0;
		for (j = 0; j < n; j++)
		{
    
    
			p[j] = tmp[j];
		}
		gap = gap * 2;
	}
	free(tmp);
	tmp = NULL;
}

void Print(int* p, int n)
{
    
    
	int i = 0;
	for (i = 0; i < n; i++)
	{
    
    
		printf("%d ", p[i]);
	}
	printf("\n");
}

int main()
{
    
    
    //三组测试用例
	int arr1[] = {
    
     4,1,3,2,9,8,6,5,7 };
	int sz1 = sizeof(arr1) / sizeof(arr1[0]);

	int arr2[] = {
    
     5,8,44,22,0,5,8,1,3 };
	int sz2 = sizeof(arr2) / sizeof(arr2[0]);

	int arr3[] = {
    
     2,5,99,3,8,11,33,62 };
	int sz3 = sizeof(arr3) / sizeof(arr3[0]);

	printf("非递归归并排序结果如下:\n");
	Print(arr1, sz1);
	MergerSortNonR(arr1, sz1);
	Print(arr1, sz1);
	printf("\n");

	Print(arr2, sz2);
	MergerSortNonR(arr2, sz2);
	Print(arr2, sz2);
	printf("\n");

	Print(arr3, sz3);
	MergerSortNonR(arr3, sz3);
	Print(arr3, sz3);
	printf("\n");

	return 0;
}

代码执行如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_56311933/article/details/121664655