归并排序详解(递归与非递归)

news2024/11/24 13:57:51

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

一、递归实现归并排序

1.算法描述

● 把长度为n的输入序列分成两个长度近似为n/2([begin,mid]  [mid + 1,end] )的子序列

● 对这两个子序列再分别进行归并排序,将区间分解到不可在分为止

● 依次将两个排序好的子序列合并成一个最终的排序序列 

方法: 开辟新数组,用双指针选出两个子序列中的较小元素尾插在新数组中

 2.动图演示

3.核心步骤 

(1)图示

(2)思考

为什么要将区间拆分成[begin,mid]  [mid + 1,end] ?

上面介绍过我们的算法步骤是把长度为 n 的输入序列分成两个长度近似为 n/2 的子序列,在此前提下,除了分成[begin,mid]  [mid + 1,end]外,还有一种分法:[begin,mid-1] [mid,end],为什么不用这种分法?

mid = (begin + end)/2,所以mid是偶数

示例:用两种不同的分法分解区间[0,9]

当所要分解的区间是 [偶数,偶数],上述的两种分法都行得通,但是当区间是[偶数,偶数+1]的情况,[begin,mid-1] [mid,end]这种分法就行不通。

5.参考代码

void _MergeSort(int* arr, int* tmp, int begin, int end)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;//偶数
	//[begin,mid]  [mid+1,end] ——>[偶数,偶数] [偶数+1,偶数+1]
	_MergeSort(arr,tmp, begin, mid);
	_MergeSort(arr, tmp, mid + 1, end);
	
	//左右区间有序就可以归并了
	//归并
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
		{
			tmp[i++] = arr[begin1++];
		}
		else
		{
			tmp[i++] = arr[begin2++];
		}
	}
	//剩下的尾插在tmp数组
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	//将一定长度的tmp复制到arr中
	memcpy(arr + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}
//归并
void MergeSort(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		return;
	}
	_MergeSort(arr,tmp, 0, n - 1);

	free(tmp);
	tmp = NULL;
}

不同排序对一百万个随机数进行排序的效率: (毫秒)

二、非递归实现归并排序

1.算法描述

● gap表示每组归并的数据个数

● i 表示每组归并的起始位置

● 两层循环,一层循环用来归并数组中的数据,另一层扩大归并的数据个数

2.核心步骤

(1)图示

 (2)思考

为什么要归并一次拷贝一次?

● 如果整体拷贝,在上述的“第二组的都越界不存在”的情况下,虽然跳出循环,但在整体拷贝的时候还会把越界的部分拷贝回去

● 但如果归并一次拷贝一次,在“第二组的都越界不存在”的情况下,直接跳出循环,不会将越界的部分拷贝到数组中

3.参考代码 

//归并(非递归)
void MergeSortNonR(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		return;
	}
	//gap表示每组要归并数据的个数
	int gap = 1;
	while (gap  < n)
	{    //i表示每组归并的起始位置
		for (int i = 0; i < n; i += 2 * gap)//个数于区间的转化关系
		{//[bagin1,end1] [bagin2,end2]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;

			//第二组的都越界不存在,那么第二组不用归并了,跳出循环
			if (begin2 > n)
				break;

			//第二组的begin2没越界,end2越界了,需要修正区间,再归并
			if (end2 > n)
				end2 = n - 1;

			//归并
			int j = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					tmp[j++] = arr[begin1++];
				}
				else
				{
					tmp[j++] = arr[begin2++];
				}
			}
			//剩下的尾插在tmp数组
			while (begin1 <= end1)
			{
				tmp[j++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = arr[begin2++];
			}
			//将一定长度的tmp复制到arr中
            //归并一次拷贝一次
			memcpy(arr + i, tmp + i, (end2 - i + 1) * sizeof(int));
		}
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

三、算法分析 

归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(N*logN),代价是需要额外的内存空间。

特性总结:

(1) 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在此版中的外排序问题

(2) 时间复杂度:O(N*logN)  相当于一个满二叉树

(3) 空间复杂度:O(N)  开辟了一个额外的数组

(4) 稳定性:稳定

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

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

相关文章

The Forest Enemy Pack(2D动画角色游戏模型)

这个包包含14个适用于platformer和2d rpg游戏的动画角色。 动画总帧数:1785 用于动画的所有精灵都具有透明背景,并准备有1500x1200和750x600两种尺寸。 对于每个角色,你也可以找到具有单独身体部位的精灵表,这样你就可以轻松地制作自己的动画。它们有PNG和PSD格式。 示例场…

苏东坡传-读书笔记八

孤馆灯青&#xff0c;野店鸡号&#xff0c;旅枕梦残。渐月华收练&#xff0c;晨霜耿耿&#xff0c;云山摛锦&#xff0c;朝露漙漙。世路无穷&#xff0c;劳生有限&#xff0c;似此区区长鲜饮。微吟罢&#xff0c;凭征鞍无语&#xff0c;往事千端。 翻译如下 孤独的客栈里&#…

Springboot整合RedisTemplate以及业务工具类示例

docker安装Redis参考我另一篇博客Docker安装Redis及持久化 一、Get-Started 依赖 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency><groupId>org.springframework.boot</groupId>…

C# 类型转换之显式和隐式

文章目录 1、显式类型转换2. 隐式类型转换3. 示例4. 类型转换的注意事项5. 类型转换的应用示例总结 在C#编程中&#xff0c;类型转换是一个核心概念&#xff0c;它允许我们在程序中处理不同类型的数据。类型转换可以分为两大类&#xff1a;显式类型转换&#xff08;Explicit Ca…

ctfshow-web入门-命令执行(web118详解)Linux 内置变量与Bash切片

输入数字和小写字母&#xff0c;回显 evil input 查看源码&#xff0c;发现这里会将提交的参数 code 传给 system 函数 使用 burpsuite 抓包进行单个字符的模糊测试 fuzz&#xff1a; 发现过滤掉了数字和小写字母以及一些符号&#xff0c;下面框起来的部分是可用的 结合题目提…

手把手教你:如何在51建模网免费下载3D模型?

作为国内领先的3D互动展示平台&#xff0c;51建模网不仅汇聚了庞大的3D模型资源库&#xff0c;供用户免费下载&#xff0c;更集成了在线编辑、格式转换、内嵌展示及互动体验等一站式功能&#xff0c;为3D创作者及爱好者搭建起梦想与现实的桥梁。 如何在51建模网免费下载3D模型…

恶意软件是什么意思?常见的恶意软件类型

您可能听说过很多有关恶意软件感染和运行服务器的危险的信息。但是&#xff0c;您可能还不清楚这在现实生活中意味着什么&#xff0c;或者该如何处理。让我们来了解一下&#xff1a;当人们谈论恶意软件时&#xff0c;他们真正指的是什么&#xff1f; 恶意软件是恶意软件的缩写&…

VehicleSPY的安装与使用

VehicleSPY介绍 Vehicle Spy 是美国英特佩斯公司的一款集成了诊断、节点/ECU仿真、数据获取、自动测试和车内通信网络监控等功能的工具&#xff0c;Vehicle Spy软件支持的应用场景很多&#xff0c;无法一一列举&#xff0c;以下是一些常见的应用&#xff1a; 总线监控&#x…

从百数教学看产品设计:掌握显隐规则,打造极致用户体验

字段显隐规则允许通过一个控件&#xff08;如复选框、单选按钮或下拉菜单&#xff09;来控制其他控件&#xff08;如文本框、日期选择器等&#xff09;和标签页&#xff08;如表单的不同部分&#xff09;的显示或隐藏。 这种规则通常基于用户的选择或满足特定条件来触发&#…

记一次阿里云服务器java应用无法响应且无法远程连接的问题排查

问题表现 java服务无响应&#xff0c;无法远程链接到服务器。 今天中午12点多&#xff0c;应用直接崩溃。后续进入到服务器&#xff0c;发现java进程都不在了&#xff0c; 排查过程 先安装atop工具 安装、配置并使用atop监控工具 等下次再出现时看相关时间点日志&#xff…

慢病精准预测:大模型 + 多模态融合

慢病精准预测&#xff1a;大模型 多模态融合 慢病预测算法拆解子解法1&#xff1a;多模态数据集成子解法2&#xff1a;实时数据处理与更新子解法3&#xff1a;采用大型语言多模态模型&#xff08;LLMMs&#xff09;进行深度学习分析 慢病预测更多模态 论文&#xff1a;https:/…

DC-DC 5V1A输出异步升压恒压芯片FP6291-5V/1A(5W)芯片,5V、9V、12V可调输出

FP6291LR-G1:DC-DC异步升压恒压5V/1A&#xff08;5W&#xff09;芯片&#xff0c;2.6-5.5V供电&#xff0c;内置MOS&#xff0c;最高输出5-12V/5-7W&#xff0c;输入限流可调 FP6291是一个电流模式升压DC-DC转换器。它的PWM电路内置0.2Ω功率MOSFET使该调节器高效。内部补偿网…

第4章 第一个程序

第4章 第一个程序 4.1 一个源程序从写出到执行的过程 第一步&#xff1a;编写汇编程序第二步&#xff1a;对源程序进行编译连接第三步&#xff1a;执行可执行文件中的程序 4.2.源程序 汇编语言中包含两种指令&#xff1a;汇编指令 和 伪指令 汇编指令&#xff1a;有对应机器…

挖逻辑漏洞不懂数据权限怎么赚大钱?

从开发的角度看权限漏洞的最后一篇了&#xff0c;也就是数据权限篇&#xff0c;虽然之前的都没什么人看&#xff0c;但想了想还是花时间写完了&#xff0c;也算有始有终吧。 0x00 相比于之前的未授权《一文理解权限类漏洞产生的原因之未授权篇》与功能权限《权限类漏洞解析—…

【实战场景】记一次UAT jvm故障排查经历

【实战场景】记一次UAT jvm故障排查经历 开篇词&#xff1a;干货篇&#xff1a;1.查看系统资源使用情况2.将十进制进程号转成十六进制3.使用jstack工具监视进程的垃圾回收情况4.输出指定线程的堆内存信息5.观察日志6.本地环境复现 总结篇&#xff1a;我是杰叔叔&#xff0c;一名…

20_系统测试与维护

目录 测试基础知识 测试原则 动态测试 静态测试 测试策略 测试阶段 测试用例设计 黑盒测试用例设计 白盒测试用例设计 McCabe度量法 鲁棒性测试 缺陷探测率(Defect Detection Percentage,DDP) 调试 系统维护基础 系统转换 系统维护指标 软件容错技术 嵌入式安…

Java语言开发的一套智慧产科系统源码:产科专科电子病历系统源码

Java语言开发的一套智慧产科系统源码&#xff1a;产科专科电子病历系统源码 系统概述 电子病历系统是以住院病人为中心&#xff0c;面向医生以及护士为主的&#xff0c;涉及临床治疗、护理等业务的临床信息系统&#xff0c;以电子信息技术为手段&#xff0c;实时采集病人在整个…

设计模式-结构型-08-组合模式

文章目录 1、学校院系展示需求2、组合模式基本介绍3、组合模式示例3.1、 解决学校院系展示&#xff08;透明模式1&#xff09;3.2、高考的科目&#xff08;透明模式2&#xff09;3.3、高考的科目&#xff08;安全组合模式&#xff09; 4、JDK 源码分析5、注意事项和细节 1、学校…

33.哀家要长脑子了!

憋说了&#xff0c;感觉好不容易长出来的脑子又缩回去了。。。 1.539. 最小时间差 - 力扣&#xff08;LeetCode&#xff09; 把所有时间排好序&#xff0c;然后计算两两之间的分钟差就好&#xff0c;但是要注意加上最后一个和第一个的判断&#xff0c;因为这个时间是按字典序来…

python-切片、集合

序列是指&#xff1a;内容连续、有序&#xff0c;可使用下标索引的一类数据容器 序列的常用操作 - 切片 切片的语法 序列的常用操作 - 切片 注意切片的范围是左闭右开 为什么使用集合 集合的常用操作 - 修改 集合的常用操作 - 集合长度 集合常用功能总结 集合的特点