数据结构(C语言)之对归并排序的介绍与理解

news2025/1/20 18:28:25

目录

一·归并排序介绍:

二·归并排序递归版本:

2.1·递归思路:

2.2·递归代码实现:

三·归并排序非递归版本:

3.1·非递归思路:

3.2·非递归代码实现:

四·归并排序性能分析:


欢迎大佬:

羑悻的小杀马特-CSDN博客羑悻的小杀马特关注c++,c语言,数据结构领域.https://blog.csdn.net/2401_82648291?spm=1011.2266.3001.5343


一·归并排序介绍:

 首先,归并排序可以理解为用分治策略的一种排序算法,这里可以用递归的思想去理解,对一个数组进行不断分割,每次分为两个子数组,直到最后剩下的是一个数据也就是不可再分割,那么就开始对末两个子数组进行归并,然后归回去,在原数组得到有序的数组。(也就是说再每次归并的两个数组一定要是有序的)。

二·归并排序递归版本:

2.1·递归思路:

实现代码的同时,首先要先分割原数组为两个子数组,这里就用到了分割方法,分割的区间为[left,mid][mid+1,right]这样分割,可以避免出现区间循环的问题([偶数,偶数+1])。

(注:其它细节见代码处注释)

2.2·递归代码实现:

//归并的时候要确保每两个区间内数据都是有序的
//这里可以是递归,但是不让它每次都开辟空间,故这里用了一个子函数来完成递归操作

//这里可以假设完成的是最后一次归并操作,通过调用两次子函数假设已经把最后两个区间排好序了,最后
//再对它们归并即可。
void _mergesort(int* a, int* tmp, int begin, int end) {
	if (begin >= end) {
		return;
	}//递归终止条件:多为不断分割区间到只剩下一个数据结束直接归并
	int mid = (begin + end) / 2;
	_mergesort(a, tmp, begin, mid);//这里由于如果选mid-1和mid的话,当区间为【偶数,偶数+1】就会分割死循环
	_mergesort(a, tmp, mid + 1, end);
	int begin1 = begin;
	int begin2 = mid + 1;
	int end1 = mid;
	int end2 = end;
	//由于每次归并都是从原数组归到tmp,而最后又要把tmp对应的位置数据copy回原数组,故当我们归并排序到tmp数组
	//应对应原数组下标放入
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2) {
		if (a[begin1] < a[begin2]) {
			tmp[i++] = a[begin1++];
		}
		else {
			tmp[i++] = a[begin2++];
		}
	}
	if (begin1 > end1) {
		while (begin2 <= end2) {
			tmp[i++] = a[begin2++];
		}
	}
	else {
		while (begin1 <= end1) {
			tmp[i++] = a[begin1++];
		}
	}
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
	//这里开辟的tmp数组,可防止原数组被覆盖,每次归并完为有序的数组copy回原数组原位置

}
//这里递归每次分割,最后成一个数据自然有序,接着每次归并后归回去。
void mergesort(int* a, int n) {
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
		perror(malloc);
		return;
	}
	_mergesort(a, tmp, 0, n - 1);
	free(tmp);
	tmp = NULL;
}

三·归并排序非递归版本:

3.1·非递归思路:

上面这个非递归的归并排序,是先是gap=1,归并当出现gap可以=2的时候再整体归并,这时整个数组并未被gap=1遍历完分好组,也是可以的,下面介绍一种直接被gap遍历完分好组再进行归并的方法:

非递归的话,就是把数组先分为一个个的每个子区间只有一个数据,然后让它们每两个成一对进行归并操作,等这一轮进行完后,从数组首开始给它们两个数据为一个区间,每两个区间就会满足区间内数据均有序,从而再次进行归并操作,依次类推,最后会生成两组有序归并完后得到原数组即为有序的原数组。

这里用gap来记录每组数据个数,通过循环来改变gap,gap定值时候用for循环来确定每次分两组情况。

而这里需要考虑的重点就是越界问题,当分区间的时候无论奇数个还是偶数个数据都会存在越界现象,而如果为奇数个的话,当gap为1的时候,最后会存在越界,而偶数的时候,可能往后面才出现越界,而画图可知道,由于每次第一组的区间首位是i不会越界故越界的是第二组要么是都越界,要么第二个区间的第二个数字越界。(其他细节见源代码注释)

画图解释:

3.2·非递归代码实现:

//这里非递归,可以从每组一个数据开始归并,然后有序,然后每两个就有序了,
// 最后会变为最后的两组要么归并要么舍弃一组
// 接着每组两个归并成四个,依次每组gap个数据调整到最后剩下两组,即再次归并得到最后有序的数组
//画图可知道每次如果出现越界只能是最后两组,而这两组的第一组的end1为i不可能越界
//故可以分数据为偶数个还是奇数个,如果偶数个那么gap为1时不越界但是之后会,为奇数时gap为1最后一组
//越界,然后出现越界肯定是第二组,然后begin2如果越,就break,而end2越界就变为n-1接着归并
//可发现gap跳的时候每次都是跳的2的多少次方,即当剩下的组区间有越界但里面有数据一定是有序的,变为n-1归并
void  mergesortNoR(int* a, int n) {
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
		perror(malloc);
		return;
	}

	for (int gap = 1; gap < n; gap = 2 * gap) {
		for (int i = 0; i < n; i += 2 * gap) {
			//两边闭区间
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			if (begin2 >= n) {
				break;
				//由于最后两组如果出现越界的话end1始终不会越界,一旦越界begin2一定越界
				//那么就防止后面的归并出错,就停止归并
			}
			if (end2 >= n) {
				end2 = n - 1;
				//当最后一次gap+的循环,肯定第二组begin2不越界,越界可能是end2,而前几次的归并
				//已经把最后一次第二组的数据排好序了那么更改end2然后再次归并就可以了
			}
			int i = begin1;
			int start = begin1;
			int last = end2;
			while (begin1 <= end1 && begin2 <= end2) {
				if (a[begin1] < a[begin2]) {
					tmp[i++] = a[begin1++];
				}
				else {
					tmp[i++] = a[begin2++];
				}
			}
			if (begin1 > end1) {
				while (begin2 <= end2) {
					tmp[i++] = a[begin2++];
				}
			}
			else {
				while (begin1 <= end1) {
					tmp[i++] = a[begin1++];
				}
			}
			memcpy(a + start, tmp + start, sizeof(int) * (last - start + 1));


		}
	}

}

四·归并排序性能分析:

复杂度:首先由于归并排序每次是折半归,故它的时间复杂度类似于二叉树为o(n*logn),而由于多开了n个空间的数组作为归并暂存数组用来copy。空间复杂度为:o(n)。

稳定性:首先稳定性就是当用排序算法给数组排序的时候,它里面原本的相同的元素相对位置不变化就称为其的稳定性。对于归并排序而言,每次两个数组归并成一个数组,只要我们改动一下当begin1与begin2对应数字相等,就放入begin1对应的数据,这样顺序就不变了,也可以说归并排序是稳定的。

就是把<改成=。

应用:可用于正常的排序,或者大文件的排序,由于归并排序是在内存中进行,有的时候文件太大无法正常进行,可以把它分为一个个小文件到内存归为有序,最终整合使得大文件也有序。 

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

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

相关文章

day40--Redis(二)实战篇

实战篇Redis 开篇导读 亲爱的小伙伴们大家好&#xff0c;马上咱们就开始实战篇的内容了&#xff0c;相信通过本章的学习&#xff0c;小伙伴们就能理解各种redis的使用啦&#xff0c;接下来咱们来一起看看实战篇我们要学习一些什么样的内容 短信登录 这一块我们会使用redis共…

碳素钢化学成分分析 螺纹钢材质鉴定 钢材维氏硬度检测

碳素钢的品种主要有圆钢、扁钢、方钢等。经冷、热加工后钢材的表面不得有裂缝、结疤、夹杂、折叠和发纹等缺陷。尺寸和允许公差必须符合相应品种国家标准的要求。 具体分类、按化学成分分类 &#xff1a; 碳素钢按化学成分&#xff08;即以含碳量&#xff09;可分为低碳钢、中…

问题:军保卡不允许开立附属卡,不能开展境外交易,不开通云闪付工功能() #其他#经验分享

问题&#xff1a;军保卡不允许开立附属卡&#xff0c;不能开展境外交易&#xff0c;不开通云闪付工功能&#xff08;&#xff09; A&#xff0e;A&#xff1a;正确 B&#xff0e;B&#xff1a;错误 参考答案如图所示

在线渲染3d怎么用?3d快速渲染步骤设置

在线渲染3D模型是一种高效的技术&#xff0c;它允许艺术家和设计师通过互联网访问远程服务器的强大计算能力&#xff0c;从而加速渲染过程。无论是复杂的场景还是高质量的视觉效果&#xff0c;在线渲染服务都能帮助您节省宝贵的时间。 在线渲染3D一般选择的是&#xff1a;云渲染…

React的useState的基础使用

import {useState} from react // 1.调用useState添加状态变量 // count 是新增的状态变量 // setCount 修改状态变量的方法 // 2.添加点击事件回调 // userState实现计数实例import {useState} from react// 使用组件 function App() {// 1.调用useState添加状态变量// coun…

Python下载库

注&#xff1a;本文一律使用windows讲解。 一、使用cmd下载 先用快捷键win R打开"运行"窗口&#xff0c;如下图。 在输入框中输入cmd并按回车Enter或点确定键&#xff0c;随后会出现这个画面&#xff1a; 输入pip install 你想下载的库名&#xff0c;并按回车&…

使用MATLAB的BP神经网络进行数据分类任务(简单版)

BP神经网络&#xff0c;即反向传播&#xff08;Backpropagation&#xff09;神经网络&#xff0c;是一种多层前馈神经网络&#xff0c;它通过反向传播算法来更新网络权重。这种网络结构特别适合于分类和回归任务。 MATLAB环境设置 在开始之前&#xff0c;请确保MATLAB环境已经…

【设计模式深度剖析】【5】【行为型】【迭代器模式】

&#x1f448;️上一篇:策略模式 设计模式-专栏&#x1f448;️ 文章目录 迭代器模式定义英文原话直译如何理解呢&#xff1f; 迭代器模式的角色1. Iterator&#xff08;迭代器&#xff09;2. ConcreteIterator&#xff08;具体迭代器&#xff09;3. Aggregate&#xff08;聚…

【Git】如何不管本地文件,强制git pull

要在 Git 中强制执行 git pull 操作&#xff0c;忽略本地文件的更改&#xff0c;可以按照以下步骤操作&#xff1a; 保存当前工作状态&#xff1a;如果你有未提交的更改&#xff0c;可以使用 git stash 将这些更改存储起来。 git stash强制拉取最新代码&#xff1a;使用 git re…

物联网学习小记

https://www.cnblogs.com/senior-engineer/p/10045658.html GOSP: 提供类似Qt的API接口&#xff0c;仅需要几百KB的硬件资源&#xff08;比Qt小的多&#xff09;&#xff0c;能运行在Qt不支持的低配置硬件上&#xff08;对Qt生态形成补充&#xff09;&#xff0c;适用于嵌入式…

基于SpringBoot+Vue单位考勤系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

YOLOv5车流量监测系统研究

一. YOLOv5算法详解 YOLOv5网络架构 上图展示了YOLOv5目标检测算法的整体框图。对于一个目标检测算法而言&#xff0c;我们通常可以将其划分为4个通用的模块&#xff0c;具体包括&#xff1a;输入端、基准网络、Neck网络与Head输出端&#xff0c;对应于上图中的4个红色模块。Y…

【Python错误】:AttributeError: ‘generator‘ object has no attribute ‘next‘解决办法

【Python错误】&#xff1a;AttributeError: ‘generator’ object has no attribute next’解决办法 在Python中&#xff0c;生成器是一种使用yield语句的特殊迭代器&#xff0c;它允许你在函数中产生一个值序列&#xff0c;而无需一次性创建并返回整个列表。然而&#xff0c;…

网线制作(双绞线+水晶头)——T568B标准

参考视频&#xff1a;https://www.bilibili.com/video/BV1KQ4y1i7zP/ 1、使用剥线器 2、将线捋顺、排序、剪掉牵引线 记忆技巧 1.线序颜色整体是一浅一深 2.颜色顺序是黄、蓝、绿、棕 一个黄种人、从上向下看&#xff0c;分别看到的是蓝天、青草(绿)、泥土(棕色) 3.中间两根浅…

常见机器学习概念

信息熵 信息熵&#xff08;information entropy&#xff09;是信息论的基本概念。描述信息源各可能事件发生的不确定性。20世纪40年代&#xff0c;香农&#xff08;C.E.Shannon&#xff09;借鉴了热力学的概念&#xff0c;把信息中排除了冗余后的平均信息量称为“信息熵”&…

大数据环境搭建@Hive编译

Hive3.1.3编译 1.编译原因1.1Guava依赖冲突1.2开启MetaStore后运行有StatsTask报错1.3Spark版本过低 2.环境部署2.1jdk安装2.2maven部署2.3安装图形化桌面2.4安装Git2.5安装IDEA 3.拉取Hive源码4.Hive源码编译4.1环境测试1.测试方法——编译2.问题及解决方案&#x1f4a5;问题1…

了解JVM中的Server和Client参数

了解JVM中的Server和Client参数 Java虚拟机&#xff08;Java Virtual Machine&#xff0c;JVM&#xff09;作为Java程序运行的核心&#xff0c;提供了多种参数来优化和调整程序的性能和行为。其中&#xff0c;-server和-client是两个重要的参数&#xff0c;分别用于配置JVM在服…

[ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;网络通信基础TCP/IP专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月8日14点23分 &#x1f004;️文章质量&#xff1a;94分 前言—— 在现代通信网络中&#xff0c;传输介质是数据传…

莱芜代理记账公司-全方位为您服务的专业会计服务机构

莱芜代理记账&#xff0c;一个专注于为各类企业提供专业、高效和全面的财务咨询服务的机构&#xff0c;我们的团队由一群经验丰富、富有责任心的会计专业人士组成&#xff0c;他们具备深厚的理论知识和丰富的实践经验&#xff0c;能够根据企业的实际需求&#xff0c;提供最适合…

无人机电机选型

2306的意思是电机定子直径23MM&#xff0c;定子高度6MM.在相同KV值的情况下电机的定子体积越大&#xff0c;扭矩越大&#xff1a;KV的意思是每增加1V的电压电机转速增加多少。同参数的电机KV越低&#xff0c;在低速的情况下能带动更大的质量。这也就解释了竞速机选用更高KV值的…