[算法]归并排序(C语言实现)

news2024/11/16 1:38:16

一、归并排序的定义      

          归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 

二、归并排序的算法原理

        归并排序的算法可以用递归法和非递归法来实现,在理解的角度来看,归并排序就是一种递归排序。其将一个数组分成均匀的两份小的数组,然后将其分成的两份各自再分,得到四份小的数组,如此重复,直到所分成的小数组没有元素或者只有一个元素为止,这就是分而治之的。当们把数组分好后,再依次进行归并(将两个有序的小数组归并成一个有序数组),只有一个元素的小数组视为有序,第一次归并后,每个有序数组的元素个数就从一变成了二,再次归并有序数组,每个有序数组的个数就从二变成了四,如此重复,直到有序的数组个数等于原数组的个数,此时整个数组完全有序,完成了排序的工作。

下面是归并排序分而治之的演示图:

        上面这个图长得非常像一个二叉树,其实就是回溯这个二叉树进行归并的过程。而归并两个有序数组的方法非常简单,这里就不进行赘述。下面我来使用递归的方法和非递归的方法来实现递归排序。 

归并排序的动态示意图:

三、归并排序的递归法实现

        递归的方法实现归并排序很简单,就是不断递归左子树和右子树进行归并排序,当左右子树都有序后,就对左子树和右子树进行归并排序即可。为了简单起见,排序整数数组。

具体代码如下:

//归并排序递归法
void _MergeSort(int* arr,int* pTempArr,int leftIndex,int rightIndex)
{
	//如果区间内没有元素,停止递归
	//如果区间内只有一个元素,则视为有序,停止递归
	if (leftIndex >= rightIndex)
		return;

	//将当前区间均分为两个区间 
	//[leftIndex,midIndex]和[midIndex + 1,rightIndex]
	int midIndex = (leftIndex + rightIndex) / 2;

	//递归左右子树使左右子树有序,当左右子树有序时进行递归排序
	_MergeSort(arr, pTempArr, leftIndex, midIndex);//递归左子树
	_MergeSort(arr, pTempArr, midIndex + 1, rightIndex);//递归右子树

	//到了这里,说明左右子树有序了
	//归并两个有序数组[leftIndex,midIndex]和[midIndex + 1,rightIndex]
	int key = leftIndex;
	int index1 = leftIndex;
	int index2 = midIndex + 1;

	while(index1 <= midIndex && index2 <= rightIndex)
	{
		if (arr[index1] <= arr[index2])
			pTempArr[key++] = arr[index1++];
		else
			pTempArr[key++] = arr[index2++];
	}

	//归并剩余的元素
	while(index1 <= midIndex)
	{
		pTempArr[key++] = arr[index1++];
	}

	while (index2 <= rightIndex)
	{
		pTempArr[key++] = arr[index2++];
	}

	//将归并好的有序数据转移到待排序的数组中
	memcpy(arr + leftIndex, pTempArr + leftIndex, sizeof(int) * (rightIndex - leftIndex + 1));
}

//归并排序递归法实现
void MergeSort(int* arr,int nums)//传入数组和数组的大小
{
	//为归并两个有序数组临时开辟所需要的空间
	int* pTempArr = (int*)malloc(sizeof(int) * nums);

	//对数组进行归并排序
	_MergeSort(arr, pTempArr, 0, nums - 1);

	//释放临时数组的空间
	free(pTempArr);
}

四、归并排序的非递归方法实现 

        归并排序推荐使用非递归的方法来实现的,因为递归会出现栈溢出的问题,而非递归的方法就不用在意这个问题,迭代不需要像递归那样开辟大量栈空间。但是非递归的方法实现起来有一点的困难。

在上面的分而治之的图中:

        迭代的方式不需要分而治之,实现归并排序可以跳过的过程,直接,我们从图中可以看到,第一层有序数组进行归并排序时,每个有序数组的元素个数为1;当第二层有序数组进行归并排序时,每个有序数组的元素个数为2;当第三层进行归并排序时,每个有序数组的元素个数为4,我们发现,每次归并排序完成后,其每个将要归并的有序数组的元素个数是上一层的两倍,于是我们可以使用迭代的方式进行递归。

下面我将画图来演示这个过程: 

        归并排序的非递归法需要注意的是数组最后的几组有序数组元素的归并,视情况进行特殊处理。

其情况有以下几种:

1、倒数第二组有序数组和倒数第一组有序数组匹配进行归并。

此时又分为两种情况:

倒数第一组有序数组的个数和倒数第二组的有序数组的个数相同,归并的数组大小是对称的。

倒数第一组有序数组的个数比倒数第二组有序数组的个数少,归并的数组大小不是对称的

2、倒数第二组有序数组和倒数第三组有序数组匹配进行归并,而倒数第一组有序数组没有可以匹配归并的有序数组,此时倒数第一组有序数组不需要进行归并操作。

具体代码如下: 

//归并两个有序数组到新数组中
void MergeArray(int* pTempArr, int* arr,int leftIndex, int midIndex, int rightIndex)
{
	//归并有序数组[leftIndex,midIndex]和[midIndex + 1,rightIndex]
	int index1 = leftIndex;
	int index2 = midIndex + 1;
	int key = leftIndex;


	while (index1 <= midIndex && index2 <= rightIndex)
	{
		if (arr[index1] <= arr[index2])
			pTempArr[key++] = arr[index1++];
		else
			pTempArr[key++] = arr[index2++];
	}

	while (index1 <= midIndex)
	{
		pTempArr[key++] = arr[index1++];
	}

	while (index2 <= rightIndex)
	{
		pTempArr[key++] = arr[index2++];
	}
}

//归并排序非递归
void MergeSortNonR(int* arr,int nums)
{
	//为归并有序数组临时开辟所需要的空间
	int* pTempArr = (int*)malloc(sizeof(int) * nums);

	int gap = 1; //每组有序数组的元素个数
	while (gap < nums)
	{
		int leftIndex = 0; //归并的有序数组的起始位置(下标的左边界)

		// 每一层归并后的有序数组下标的分组
		// 0 1 2 3 4 5 6 7 8 9 10
		// [0 1] [2 3] [4 5] [6 7] [8 9] 10
		// [0 1 2 3] [4 5 6 7] [8 9 10]
		// [0 1 2 3 4 5 6 7] [8 9 10]
		// [0 1 2 3 4 5 6 7 8 9 10]

		// nums - leftIndex 得到leftIndex以及往后的元素的个数
		// nums - leftIndex >= 2 * gap 可以保证归并到的有序数组都是对称的
		while (nums - leftIndex >= 2 * gap)
		{
			int rightIndex = leftIndex + 2 * gap - 1;
			int midIndex = (leftIndex + rightIndex) / 2;

			//归并有序数组[leftIndex,midIndex]和[midIndex + 1,rightIndex]
			MergeArray(pTempArr, arr, leftIndex, midIndex, rightIndex);
			
			//将归并好的元素转移到待排序的数组中
			memcpy(arr + leftIndex, pTempArr + leftIndex, sizeof(int) * (rightIndex - leftIndex + 1));

			//归并下两组有序数组
			leftIndex += 2 * gap; 
		}

		//处理不对称的归并和没有归并的有序数组可以匹配的情况
		//如果满足 nums - leftIndex > gap 
		//说明leftIndex以及后面的元素个数大于gap个
		//需要进行归并排序,只不过归并排序的区间并不对称
		if (nums - leftIndex > gap)
		{
			//这里分成的两个区间由于不对称,所以不能使用左右下标相除的方法算出中间下标
			//归并排序[leftIndex,leftIndex + gap - 1] [leftIndex + gap,nums - 1]
			MergeArray(pTempArr, arr, leftIndex, leftIndex + gap - 1, nums - 1);
			memcpy(arr + leftIndex, pTempArr + leftIndex, sizeof(int) * (nums - leftIndex));
		}
		//如果剩余元素不足gap个,不需要进行归并排序

		//进行下一层的归并排序
		gap *= 2;
	}

	//释放临时数组的空间
	free(pTempArr);
}

五、总结 

        归并排序的时间复杂度为O(nlogn),空间复杂度为O(N),因为归并有序数组需要额外开辟空间,所以其排序的性能仅次于快排,但是归并排序稳定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1967208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024新版python安装教程【附图片】

Python的安装步骤因操作系统而异&#xff0c;但大致可以分为下载、安装和验证三个主要步骤。以下是Windows系统中Python的详细安装步骤&#xff1a; Windows系统 下载Python安装包 访问Python官网&#xff08;https://www.python.org/&#xff09;。点击页面头部的“Download…

【优秀python django系统案例】基于python的医院挂号管理系统,角色包括医生、患者、管理员三种

随着信息技术的迅猛发展&#xff0c;传统的医院挂号管理方式面临着效率低下、排队时间长、信息不对称等诸多问题。这些问题不仅影响患者的就医体验&#xff0c;也加重了医院工作人员的负担。在此背景下&#xff0c;基于Python的医院挂号管理系统应运而生。该系统旨在通过信息化…

OZON饰品产品什么好卖,OZON热销饰品有哪些

在OZON平台上&#xff0c;饰品产品的销售情况受多种因素影响&#xff0c;包括市场需求、季节变化、消费者偏好以及流行趋势等。以下是一些可能热销的OZON饰品产品类别及具体推荐&#xff1a; OZON热销饰品地址&#xff1a;D。DDqbt。COm/74rDTop1 发带套装 Утика Ком…

Idea常用快捷键:设置自动导包、格式化、抽取方法

Idea设置自动导包 【File】→【Setting】(或使用快捷键【Crlt Shift S】)打开Setting设置。点击【Editor】→【General】→【Auto Import】。勾选自定导包的选项&#xff0c;并确定&#xff0c;如下&#xff1a; Addunambiguousimportsonthefly&#xff1a;添加明确的导入 …

CSP-J 2022基础知识答案与解析

1.以下哪种功能没有涉及 C语言的面向对象特性支持&#xff1a;&#xff08; &#xff09;。 (2 分) A.C中调用 printf 函数 B.C中调用用户定义的类成员函数 C.C中构造一个 class 或 struct D.C中构造来源于同一基类的多个派生类 解析&#xff1a;printf是继承自C的&#…

vue2学习 -- 核心语法(二)

文章目录 1. 绑定样式1.1 绑定class1.2 绑定style 2. 渲染2.1 条件渲染2.2 列表渲染key的作用 3. 监视数据3.1 vue监视数据的原理_对象3.2 vue监视数据的原理_数组 4. 收集表单数据5. 过滤器6. 指令6.1 内置指令6.2 自定义指令 7. 生命周期 1. 绑定样式 1.1 绑定class 三种写…

基于LK光流提取算法的图像序列晃动程度计算matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 光流的概念 4.2 基于LK光流算法的图像序列晃动程度计算 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &…

前方高能!2024 MongoDB中国用户大会正式官宣!

前方重磅&#xff01; 2024 MongoDB 中国用户大会 正式官宣&#xff01; 聆听最新产品发布与主题演讲 get行业头部客户案例的精彩分享 与各届优秀同行们现场交流⼼得 与资深MongoDB⼯程师面对面答疑解惑 ⼀起把MongoDB的酷炫周边收⼊囊中&#xff01; …… 欢迎所有Mong…

告别单一渠道,全渠道整合营销让企业营销力MAX!

现阶段&#xff0c;企业纷纷积极寻求新的道路&#xff0c;以拓展国际市场、增强品牌影响力。今天咱们来聊聊全渠道营销那点事儿&#xff0c;特别是对那些想出海闯荡一番的中国企业来说&#xff0c;这可是个必备神器&#xff01;NetFarmer专门企业搞定数字化出海&#xff0c;现在…

室内养猫空气净化器哪个好?真实室内养猫空气净化器使用评价

作为一个养猫多年的猫奴&#xff0c;家里有两只可爱的小猫咪&#xff1a;小白和小花。虽然相处起来很开心&#xff0c;但也给生活带来了一些小麻烦。感受一下40度高温的养猫人&#xff0c;给掉毛怪疏毛浮毛飘飘&#xff0c;逃不过的饮水机&#xff0c;各个角落&#xff0c;多猫…

七夕节高逼格表白方式,送给你的那个TA(可写字版)

别人都有爱心代码了&#xff0c;咱们开发者们也必须有。今天给大家分享个用 Python 写的爱心代码项目&#xff0c;如果感兴趣或者想给自己的另一半制造小浪漫的同学可以自己上手试一试。 运行结果 ** 温馨提示&#xff1a;篇幅有限&#xff0c;源码已打包文件夹&#xff0c;获…

ECRS工时分析软件:工业工程精益生产的智慧引擎

在工业工程学的广阔领域中&#xff0c;程序分析一直扮演着至关重要的角色。其中&#xff0c;ECRS四大原则——取消、合并、重排、简化&#xff0c;作为程序分析的核心&#xff0c;旨在通过优化生产过程&#xff0c;实现成本的节省和精益生产的目标。如今&#xff0c;随着科技的…

【C++】C++入门知识详解(下)

大家好~我们接着【C】C入门知识详解&#xff08;上&#xff09;-CSDN博客来介绍另一些C入门基础知识。 1.缺省值和缺省参数 缺省参数就是声明或定义函数时为函数的参数指定一个缺省参数。在调用该函数时&#xff0c;如果没有指定实参&#xff0c;则采用该形参的缺省值&#xf…

ChatGPT:GPT,GPT2,GPT3,Prompt

1&#xff0c;GPT 1.1&#xff0c;GPT结构 GPT is short for Generative Pretrained Transformer。其实GPT和BERT的区别就写在他们的脸上。GPT是Generative的&#xff0c;目的就是要生成。它是一个预训练的Transformer&#xff0c;因为目的就是要生成&#xff0c;所以是Decode…

C++ unordered_map

1. unordered系列关联式容器 在C98 中&#xff0c; STL 提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到 &#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好的查询是&#xff0c…

亚马逊VC账号特权及SC升级至VC策略解析——WAYLI威利跨境助力商家

亚马逊的VC(Vendor Central)账号和SC(Selling on Amazon)账号在运营策略上有显著差异&#xff0c;这些差异主要体现在账号安全性、数据获取、广告投放以及费用结构等方面。 VC账号特权 1、高安全性&#xff1a;VC账号相较于SC账号具有更高的安全性。由于亚马逊平台对不同卖家账…

【Python数据结构与算法】递归----上台阶

题目&#xff1a;上台阶 描述 有n级台阶&#xff08;0<n<20&#xff09;&#xff0c;从下面开始走要走到所有台阶上面&#xff0c;每步可以走一级或两级&#xff0c;问有多少种不同的走法。 输入 一个整数n 输出 走法总数 样例输入 4样例输出 5AC代码 def ways(n): …

前端-如何通过docker打包Vue服务成镜像并在本地运行(本地可以通过http://localhost:8080/访问前端服务)

1、下载安装docker&#xff0c;最好在vs code里安装docker的插件。 下载链接&#xff1a;https://www.docker.com/products/docker-desktop &#x1f389; Docker 简介和安装 - Docker 快速入门 - 易文档 (easydoc.net) 2、准备配置文件-dockerfile文件和nginx.conf文件 do…

YOLO:训练自己的样本数据集进行目标检测

作者&#xff1a;CSDN _养乐多_ 本文将介绍如何使用python语言和 ultralytics 库训练自己的数据集&#xff0c;并进行 YOLO 目标检测模型训练和推理的代码。 文章目录 一、样本数据集准备1.1 标注工具1.2 数据集格式1.2.1 图片和标签数据集制作1.2.2 data.yaml制作 二、模型训…

SAP 采购订单审批 Flexible Workflow

目录 1 简介 2 业务数据 1&#xff09;下采购订单&#xff0c;如果订单金额超过 15w 生成 Flexible Workflow 审批 2&#xff09;审批采购订单 - 系统默认主页显示需要审批的采购订单&#xff0c;也可以设置成发邮件提醒 3 后台配置 4 前台主数据定义 1&#xff09;创建…