归并排序的递归和非递归

news2025/1/15 6:27:36

基本思想

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

面试官:说说你对归并排序的理解?如何实现?应用场景?

 归并排序其实就是等分分割数组,直到分割后的数组有序再将这分割的两个数组归并到一个新的数组里,再拷贝到原数组中。这里就与二叉树的后序遍历极为相似,分割的数组只有一个数时就默认有序了,再合并之后一步步的递归回去。 

代码(递归)

void _MergeSort(int* arr, int left, int right,int* tmp)
{
	if (left == right)//不可能分割出不存在的区间
		return;
	int midi = (left + right) / 2;
	_MergeSort(arr, left, midi, tmp);//分割归并左部分
	_MergeSort(arr, midi + 1, right, tmp);//分割归并右部分

	//归并(begin1和end2之间的数)即left和right之间的数
	int i = 0;//临时数组下标
	int begin1 = left, end1 = midi;
	int begin2 = midi + 1, end2 = right;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
			tmp[i++] = arr[begin1++];
		else
			tmp[i++] = arr[begin2++];
	}
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}

	memcpy(arr+left, tmp, sizeof(int) * i);//归并一次就拷贝一次

}
void MergeSort(int* arr, int left, int right)
{
	int* tmp = (int*)malloc(sizeof(int*) * (right + 1));
	_MergeSort(arr, 0, right,tmp);
	free(tmp);
}

代码1(非递归)

void _MergeSort(int* arr, int left, int right, int* tmp)
{

	int gap = 1;//每一组的数据个数
	while (gap < right)
	{
		
		for (int i = 0; i <= right; i += 2 * gap)//两两归并,遍历整个数组
		{
			int j = 0;//临时数组的下标
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			if (end1 > right || begin2 > right)
				break;
			if (end2 > right)
			{
				end2 = right;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
					tmp[j++] = arr[begin1++];
				else
					tmp[j++] = arr[begin2++];
			}
			while (begin1 <= end1)
			{
				tmp[j++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = arr[begin2++];
			}
			memcpy(arr + i, tmp, sizeof(int) * j);//归并一次拷贝一次
		}
		gap *= 2;
	}

}

非递归就要知道该代码原来递归是怎么走的,递归的部分实际上是在一直分割成两部分,直到不能再分割了以后,就开始合并到tmp临时数组里,所以非递归的写法实际就也是分组执行,先是单个数是一组,两两一组进行比较拷贝,原先拷贝好的就可以是一组了,此时每组就有两个数是有序的,再两两一组,两组再进行拷贝,此时拷贝好的一组就有四个有序的数了,再进行四四一组.......

但是这可能存在越界的情况,两组数据拷贝,极有可能就越界,而且存在三种越界的情况:

 而上面的一二两种情况又可以看作一种:多余了已经有序的一组(不到一组)此时该组已经有序了,所以也不用进行归并了,直接放在原数组中就行了,但是你memcpy拷贝数组就得一组组的拷贝了,不能归并完整个数组以后再全部拷贝到原数组。

而第三种情况就是多了不仅仅一组的数据但是却又不够两组,此时就不能说这一组半是有序的,所以就重行划区间,end2就指向最后一个数的下标,将这半组也看成一组,两两拷贝数据成有序。

代码2(非递归) 

void _MergeSort(int* arr, int left, int right, int* tmp)
{
	int gap = 1;//每一组的数据个数
	while (gap < right)
	{
		int j = 0;//临时数组下标
		for (int i = 0; i <= right; i += 2 * gap)//两两归并整个数组
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			if (end1 > right)
			{
				end1 = right;
				begin2 = end2 + 1;//不存在的区间
			}
			else if(begin2 > right)
			{
				begin2 = end2 + 1;//不存在的区间
			}
			else if (end2 > right)
			{
				end2 = right;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
					tmp[j++] = arr[begin1++];
				else
					tmp[j++] = arr[begin2++];
			}
			while (begin1 <= end1)
			{
				tmp[j++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = arr[begin2++];
			}
		}
		memcpy(arr, tmp, sizeof(int) * j);//一次拷贝整个数组数据
		gap *= 2;
	}

}

这就是可以一次拷贝整个数组的情况,即当遇到越界的情况就就调整范围,继续拷贝。

 

 

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

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

相关文章

OpenCV项目开发实战--详细介绍如何进行边缘轮廓检测 (Python/C++)-附源码

使用轮廓检测​​,我们可以检测对象的边界,并轻松在图像中定位它们。它通常是许多有趣应用的第一步,例如图像前景提取、简单图像分割、检测和识别。 因此,让我们使用 OpenCV 来了解轮廓和轮廓检测,并亲眼看看如何使用它们来构建各种应用程序。 轮廓在计算机视觉中的应用

latex2【图片、公式、矩阵】

图片 语法&#xff1a; \includegraphics{排队论模型.png} 看起来很别扭是吧&#xff0c;需要进行“修饰”&#xff1a; 当然&#xff0c;这样也很丑&#xff0c;一般写论文可以用以下的格式&#xff1a; \begin{figure}[H] \caption{问题一模型示意图} \label{paiduimx} …

【企业架构工具】2023 年 18 大企业架构工具

这些流行和新兴的 EA 工具为企业提供了支持企业架构和数字化转型所需的一切。 企业架构系统并不总是必不可少的。据推测&#xff0c;在 1940 年代&#xff0c;国际商业机器公司的一位领导人小托马斯沃森 (Thomas Watson Jr.) 曾说过&#xff1a;“我认为大约有 5 台计算机的全球…

基于SpringBoot+vue的校园疫情防控系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Linux内核网络-拥塞控制系列(一)

谈起网络拥塞控制&#xff0c;大家可能很熟悉八股文中的"加法增大“、”乘法减小“、”慢开始“、“拥塞避免”、“快重传”、“快恢复”等概念。没错&#xff0c;这是一种经典网络拥塞控制算法的基础理论&#xff0c;但在实际的实现时不同的拥塞控制算法&#xff0c;有很…

【Android Camera开发】深入理解相机ISP(图像信号处理)必看文章

​原文&#xff1a;https://blog.51cto.com/u_16081664/6224003 作者&#xff1a;mb64411cc0e9333 凡是和图像领域工作的人&#xff0c;都会经常听到ISP&#xff08;Image Signal Process&#xff0c;图像信号处理&#xff09;&#xff0c;知道ISP对图像质量非常重要。比如华为…

电能管理系统在路店上的应用 安科瑞 许敏

摘要&#xff1a;随着企业改革的不断深入&#xff0c;对现代化用电管理的水平要求越来越高&#xff0c;准确、快速、经济的获得用电回路的各类数据进行用电分析、负荷管理、表计运行状况监测、电费自动结算的基础。同时也是提高企业经济效益的有效手段。近年来技术人员对监控系…

嵌入式程序开发者的数量剧增

随着物联网、智能设备和嵌入式系统的快速发展&#xff0c;嵌入式程序开发领域的需求不断增长&#xff0c;因此嵌入式程序开发者的数量也在剧增。这种趋势在过去几年中已经变得非常明显。 以下是导致嵌入式程序开发者数量剧增的一些主要原因&#xff1a; 我这里刚好有嵌入式、单…

Mac平台下如何制作pkg安装包以及rpath设置

打包工具介绍 Mac平台规范包可以使用Packages工具。下载地址 打包前准备工作 创建一个目录 macProject macProject目录中是以下目录结构 myProject.app└── Contents├── Info.plist├── MacOS├── res├── libmymath.dylib├── Frameworks└── Resources├…

Hive多行转多列,多列转多行

hive中的行列转换包含单行、多行、单列、多列&#xff0c;所以一共有四种组和转换结果。 一、多行转多列 原始数据表 目标结果表 分析&#xff1a;目标表中的a和b是用分组形成&#xff0c;所以groupby字段选用原始表中col1&#xff0c;c、d、e是原始表中的行值&#xff0c;…

数据结构(王道)——线性表的存储结构之循环表

一、循环单链表 定义&#xff1a; 循环单链表代码实现 创建并初始化、判断循环单链表是否为空、判断结点p是否为循环单链表的表尾结点的代码操作。 二、循环双链表 定义&#xff1a; 循环双链表代码实现 创建并初始化、判断循环双链表是否为空、判断结点p是否为循环双链表的…

橙河网络:怎么搭建海外问卷网站呢?

大家好&#xff0c;我是橙河&#xff0c;如果你想要搭建海外问卷网站赚钱&#xff0c;看我这篇文章就行了。 搭建网站&#xff0c;本身并不复杂&#xff0c;自己会敲代码就自己搞&#xff0c;不会就花点钱外包给别人。 搭建好问卷网站以后&#xff0c;重点来了&#xff0c;你需…

Learning Spatial and Spatio-Temporal Pixel

Learning Deformable Kernels for Image and Video Denoising 作者&#xff1a; Xiangyu Xu 商汤科技SenseTime Research 论文思想&#xff1a;一是将传统的双边滤波算法与CNN结合起来&#xff0c;二是用变形卷积来做多帧对齐的问题&#xff0c;三还是在raw上进行处理的。 …

WSL2 忘记用户密码

步骤一&#xff1a;将默认用户切换为root 在Windows里启动命令提示符&#xff0c;输入&#xff1a; ubuntu2004 config --default-user root这就已经将我的ubuntu20.04的默认用户切换为了root&#xff0c; 不同的WSL版本可能命令的第一个符号不一样&#xff0c;区别如下图&am…

Acrel-3000电能管理系统某公司项目中的应用 安科瑞 许敏

摘要&#xff1a;用户对自身用能的管理意识提升&#xff0c;促使用户侧电力配电系统在商业、工业以及民用区域的普及。系统针对用户侧主要的用能节点&#xff0c;设计安装智能仪表&#xff0c;再通过后台系统来实时监控各用能回路的工作状态、用电量、用水量、用气量数数据的采…

自动驾驶商用驶入“快车道”,汽车软件厂商如何“抢市”?

L3级及以上自动驾驶的商业化进程正在驶入“快车道”。 一方面&#xff0c;高阶自动驾驶的相关法规及标准不断出台&#xff0c;为自动驾驶行业的发展注入了“强心剂”。 比如工业和信息化部副部长辛国斌就曾表示&#xff0c;将启动智能网联汽车准入和上路通行试点&#xff0c;…

【嵌入式开发 Linux 常用命令系列 5 -- history 与 “!“ 巧妙配合】

文章目录 history 命令介绍history 命令与 “&#xff01;”运行先前执行的命令先前命令的参数传递给新命令两个或多个参数的处理设置 history 命令显示行数以及时间 上篇文章&#xff1a;嵌入式开发 Linux 常用命令系列 4 – git 常用配置及常用命令 history 命令介绍 histo…

虚拟内存、内存分页、分段、段页式内存管理

虚拟内存 为什么有虚拟内存&#xff1f; CPU是直接操作内存的物理地址。在这种情况下&#xff0c;如果两个程序占用的内存有重叠&#xff0c;要想同时运行两个程序是不可能的。 为啥它会内存有重叠啊&#xff1f;我不理解。难道不是这块内存被这个程序使用之后另外的程序就无…

Java内部类(InnerClass)

文章目录 概述1 什么是内部类2 为什么要声明内部类呢3 内部类的分类 成员内部类1 概述2 创建成员内部类对象 局部内部类1 非匿名局部内部类 匿名内部类 概述 1 什么是内部类 将一个类A定义在另一个类B里面&#xff0c;里面的那个类A就称为内部类&#xff08;InnerClass&#…

React中使用Redux

1.为什么要使用redux redux是一个专门用于状态管理的一个库&#xff0c;和vue中的vuex功能类似。其中核心点就是状态的管理。虽然我们无论在vue还是在react中我们组件间的通行都可以使用消息总线或者父子组件间的消息传递来进行操作。但是如果我们需要A组件的状态在其他十个或者…