• 【C语言】归并排序



    在这里插入图片描述

    一、什么是归并排序

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

    在这里插入图片描述

    二、归并排序步骤图解

    算法思想:

    归并排序算法有两个基本的操作,一个是分解,另一个是合并。分解是把原数组划分成两个子数组的过程,合并可以将两个有序数组合并成一个更大的有序数组。

    将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素,这时,可以认为只包含一个元素的子表是有序表。将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。

    示例图解步骤:

    对如图的一个数组进行归并排序的步骤
    在这里插入图片描述

    三、归并排序代码实现

    使用归并排序对一个数组a排序,需要另外创建一个数组tmp,用来存放有序子序列再放回去,具体过程如下

    归并过程图解:

    在这里插入图片描述

    1、递归实现

    //归并排序
    void _MergeSort(int* a, int* tmp, int begin, int end)
    {
    	//递归结束条件
    	if (begin >= end)
    	{
    		return;
    	}
    	int mid = (begin + end) / 2;
    	_MergeSort(a, tmp, begin, mid);
    	_MergeSort(a, tmp, mid+1, end);
    
    	// 归并到tmp数据组,再拷贝回去
    	int begin1 = begin;
    	int end1 = mid;
    	int begin2 = mid + 1;
    	int end2 = end;
    	int index = begin;
    	//begin小于end说明还有两部分都还有数据
    	while (begin1 <= end1 && begin2 <= end2)
    	{
    		if (a[begin1] < a[begin2])
    		{
    			tmp[index++] = a[begin1++];
    		}
    		else
    		{
    			tmp[index++] = a[begin2++];
    		}
    	}
    	//由于右边没有数据跳出的上一个循环,将左边剩下的数放入tmp数组对应位置
    	while (begin1 <= end1)
    	{
    		tmp[index++] = a[begin1++];
    	}
    	//由于左边没有数据跳出的上一个循环,将右边剩下的数放入tmp数组对应位置
    	while (begin2 <= end2)
    	{
    		tmp[index++] = a[begin2++];
    	}
    
    	// 拷贝回原数组
    	memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
    }
    
    
    
    void MergeSort(int* a, int n)
    {
    	int* tmp = (int*)malloc(sizeof(int) * n);
    	if (tmp == NULL)
    	{
    		perror("malloc fail");
    		return;
    	}
    
    	_MergeSort(a, tmp, 0, n - 1);
    
    	free(tmp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    2、非递归实现

    非递归实现时当gap的值不同时有许多数组的数据个数不适合当前gap,访问就会越界,比如9个值时当gap==1就会访问到下标为9的下标越界,所以要在代码中加入解决措施。当第一组右边界越界,第二组左边界也一定越界了,所以可分为第二组左边界越界和第二组右边界越界两种情况处理。

    //非递归归并
    void MergeSortNonR(int* a, int n)
    {
    	int* tmp = (int*)malloc(sizeof(int) * n);
    	if (tmp == NULL)
    	{
    		perror("malloc fail");
    		return;
    	}
    
    	int gap = 1;
    	while (gap < n)
    	{
    		for (int i = 0; i < n; i += 2 * gap)
    		{
    			// 归并到tmp数据组,再拷贝回去
    			int begin1 = i;
    			int end1 = i + gap - 1;
    			int begin2 = i + gap;
    			int end2 = i + 2 * gap - 1;
    
    			// 如果第二组不存在,这一组不用归并了
    			if (begin2 >= n)
    			{
    				break;
    			}
    
    			// 如果第二组的右边界越界,修正一下
    			if (end2 >= n)
    			{
    				end2 = n - 1;
    			}
    
    			int index = i;
    			//begin小于end说明还有两部分都还有数据
    			while (begin1 <= end1 && begin2 <= end2)
    			{
    				if (a[begin1] < a[begin2])
    				{
    					tmp[index++] = a[begin1++];
    				}
    				else
    				{
    					tmp[index++] = a[begin2++];
    				}
    			}
    			//由于右边没有数据跳出的上一个循环,将左边剩下的数放入tmp数组对应位置
    			while (begin1 <= end1)
    			{
    				tmp[index++] = a[begin1++];
    			}
    			//由于左边没有数据跳出的上一个循环,将右边剩下的数放入tmp数组对应位置
    			while (begin2 <= end2)
    			{
    				tmp[index++] = a[begin2++];
    			}
    
    			// 拷贝回原数组
    			memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));
    		}
    		gap *= 2;
    	}
    	free(tmp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    四、总结

    归并排序的算法需要另外创建很大的空间,用来存放排序好的子表,然后再放回原线性表中,所以归并排序的空间复杂度比较大。

    • 归并排序时间复杂度为:O(N*logN)
    • 归并排序空间复杂度为:O(N)
    • 归并排序是稳定的排序
    • 归并排序属于外排序,在磁盘中进行
  • 相关阅读:
    TCP为什么是三次握手
    Redis之主从复制,哨兵模式,集群
    Synopsys Sentaurus TCAD系列教程之-- Sdevice《6》Math模块
    Vue——组件高级——传值
    金九银十求职季,美团高频面试题和答案都帮你准备好啦
    阿里云 Flink+Hologres:构建企业级一站式实时数仓
    Docker 部署
    复习Day01:数组part01:701. 二分查找、35. 搜索插入位置、367. 有效的完全平方数、69. x的平方根、74. 搜索二维矩阵
    小程序随机生成文字卡片文案海报,带分享保存
    Nginx更改参数转发请求配置
  • 原文地址:https://blog.csdn.net/zcxyywd/article/details/133491684