C语言实现归并排序(Merge Sort)

目录

一、递归实现归并排序

[1. 归并排序的基本步骤](#1. 归并排序的基本步骤)

2.动图演示

3.基本思路

4.代码

二、非递归实现

1.部分代码

2.代码分析

修正后代码:

归并过程打印

性能分析

复杂度分析


归并排序是一种高效的排序算法,采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序的基本思想是将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

一、递归实现归并排序

1. 归并排序的基本步骤

  1. 分解:将数组分解成两个较小的子数组,直到子数组的大小为1。
  2. 递归进行排序并合并:递归地对子数组进行排序,并将已排序的子数组合并成一个大的有序数组,直到合并为1个完整的数组。

2.动图演示

3.基本思路

归并排序是采用分治法的一个非常典型的应用。其基本思想是:将已有序的子序合并,从而得到完全有序的序列,即先使每个子序有序,再使子序列段间有序。

那么如何得到有序的子序列呢?当序列分解到只有一个元素或是没有元素时,就可以认为是有序了,这时分解就结束了,开始合并。

4.代码

void _MergSort(int* a, int left,int right,int* tmp)
{
	if (left >= right)
		return;
	int mid = left + (right - left) / 2;

	_MergSort(a, left, mid, tmp);
	_MergSort(a, mid+1, right, tmp);

	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int i = left;
	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++];
	}
	for (int j = 0; j <= right; j++)
	{
		a[j] = tmp[j];
	}
}

void MergSort(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	_MergSort(a,0, n-1, tmp);
	free(tmp);
}

二、非递归实现

归并排序的非递归算法并不需要借助栈来完成,我们只需要控制每次参与合并的元素个数即可,最终便能使序列变为有序:

1.部分代码

void MergSortNonF(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	
	int gap = 1;
	while (gap < n)
	{
		for (int j = 0; j < n; j += 2 * gap)
		{
			//*****注意边界处理*******
			int begin1 = j, end1 = j + gap - 1;
			int begin2 = j + gap, end2 = j + gap * 2 - 1;
			printf("[%d %d] [%d %d]", begin1, end1, begin2, end2);
			int i = j;
			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++];
			}

			
		}
		printf("\n");
		memcpy(a, tmp, n * sizeof(int));
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

2.代码分析

这是一个有问题的代码,当我们存放的数据是2的次方时,可以正常排序,为了便于观察,我们把它的下标给打印下来

如果我们存放的数据不是2的次方个就会出现一些越界。

情况一:第二组部分越界

当最后一个小组进行合并时,第二个小区间存在,但是该区间元素个数不够gap个,这时我们需要在合并序列时,对第二个小区间的边界进行控制。

情况二:第二组全部越界

当最后一个小组进行合并时,第二个小区间不存在,此时便不需要对该小组进行合并。

情况三:第一组end1越界

当最后一个小组进行合并时,第二个小区间不存在,并且第一个小区间的元素个数不够gap个,此时也不需要对该小组进行合并。(可与情况二归为一类)

只要把控好这三种特殊情况,写出归并排序的非递归算法便轻而易举了。

修正后代码:

void MergSortNonF(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	
	int gap = 1;
	while (gap < n)
	{
		for (int j = 0; j < n; j += 2 * gap)
		{
			//*****注意边界处理*******
			int begin1 = j, end1 = j + gap - 1;
			int begin2 = j + gap, end2 = j + gap * 2 - 1;
			
			//第一组越界
			if (end1 >= n)
			{
				printf("[%d %d]", begin1,n-1);
				break;
			}

			//第二组全部越界
			if (begin2 >= n)
			{
				printf("[%d %d]", begin1, end1);
				break;
			}

			//第二组部分越界,越界之前的那一部分依然要归
			if (end2 >= n)
			{
				//修正end2
				end2 = n - 1;
			}

			printf("[%d %d] [%d %d]", begin1, end1, begin2, end2);

			int i = j;
			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+j, tmp+j, (end2-j+1)* sizeof(int));
		}
		printf("\n");
		//memcpy(a, tmp, n * sizeof(int));
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

归并过程打印

性能分析

复杂度分析

  • 时间复杂度:归并排序的时间复杂度为O(n log n),其中n是数组的长度。这主要是由于归并排序将问题分解为两个子问题(分解),递归地解决它们(递归),然后将解决方案合并(合并)。
  • 空间复杂度:归并排序的空间复杂度为O(n),其中n是数组的长度。这是因为归并排序在合并过程中需要与原数组同样大小的额外空间来存放临时数组。

归并排序是稳定的排序算法,即相等的元素在排序后的序列中仍然保持原来的顺序。这个特性在某些应用中非常重要。


如有错误,劳烦各位指正

相关推荐
Mar_mxs1 分钟前
算法葫芦书(笔试面试)
人工智能·算法·自然语言处理
SEU-WYL3 分钟前
基于深度学习的图像修复算法
人工智能·深度学习·算法
大柏怎么被偷了4 分钟前
【C++算法】哈希表
算法
十雾九晴丶15 分钟前
buuctf [ACTF2020 新生赛]Include
笔记·算法·学习方法
羚通科技30 分钟前
人员个体检测、PID行人检测、行人检测算法样本
大数据·人工智能·算法·计算机视觉·音视频
专心搞代码1 小时前
排序----希尔排序
数据结构·算法·排序算法
张焚雪1 小时前
关于神经网络的一个介绍
人工智能·深度学习·神经网络·算法·机器学习
AutoAutoJack1 小时前
C# 委托(Delegate)二
开发语言·数据结构·算法·架构·c#
悲伤小伞1 小时前
C/C++—有关日期类的OJ题
c语言·数据结构·c++·笔记·算法
徐霞客3201 小时前
数据结构2——单链表
数据结构·链表