数据结构_第十三关(3):归并排序

news2024/11/15 8:26:45

目录

归并排序

1.基本思想:

2.原理图:

1)分解合并

2)数组比较和归并方法:

3.代码实现(递归方式):

归并排序的非递归方式

原理:

情况1:

情况2:

情况3:

非递归代码实现

归并排序的特性总结:

排序算法复杂度及稳定性分析

什么时稳定性?

各种常见排序算法的总结


归并排序

1.基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,

该算法是采用分治法(Divide andConquer)的一个非常典型的应用。

将已有序的子序列合并,得到完全有序的序列;

即先使每个子序列有序,再使子序列段间有序。

若将两个有序表合并成一个有序表,称为二路归并。

2.原理图:

1)分解合并

第一层将一个数组分两个大组,

第二层再继续分,直到分成每组都只有一个为止

 

分解完了之后就是进行合并,每两个小数组,按顺序合并成一个大的数组

最终,合并称为一个有序的集合

 

动图效果:

归并排序是在原数组上进行的,用一个临时数组来做归并,把归并好的元素复制回原数组

2)数组比较和归并方法:

用上述长度为4的集合举例:

 

第一步:比较p1和p2位置元素的大小,谁的小,将谁的值放到p位置,并将指向小的元素的那个指针++,并且将p++

1比2小,放1到p位置,p1++,p++

 ......

 

第二步:按第一步的步骤,逐一比较,直到有一个指针走到了集合之外如下:

此时p2已经走到了集合外,就可以退出循环了

 第三步:放一个循环,将没走完的那个集合的剩余元素按顺序放到大集合种即可

当p走到大集合外面时结束循环

3.代码实现(递归方式):

//归并排序(递归实现)

//归并子函数
//(在遇到需要malloc扩容的函数时,将malloc代码放入主函数,另写一个子函数用来完成递归)
void _MergeSort(int* a, int begin ,int end, int* temp)
{
	//最后只剩下一个数的时候就说明begin=end,返回
	if (begin >= end)
	{
		return;
	}
	
	int mid = (begin + end) / 2;

	//[begin, mid] [mid+1, end] 递归让子区间都有序
    
	_MergeSort(a, begin, mid, temp);    //递归左半区
	_MergeSort(a, mid+1, end, temp);    //递归右半区

	//归并
	int begin1 = begin, end1 = mid;     //左区间[begin1, end1]
	int begin2 = mid + 1, end2 = end;   //右区间[begin2, end2]

	int i = begin;
	//如果左右两个区间都没有结束就继续,只要有一个区间结束就终止
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] <= a[begin2])
		{
			temp[i++] = a[begin1++]; 
		}
		else
		{
			temp[i++] = a[begin2++];
		}
	}

	//将没走到头的区间按顺序放到后面
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[i++] = a[begin2++];
	}

	//将临时区间的数据拷贝回原数组
	memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1));
}

//归并主函数
void MergeSort(int* a, int n)
{
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	_MergeSort(a, 0, n-1, temp);

	free(temp);
	temp = NULL;
}

归并排序的非递归方式

原理:

控制每次参与归并的元素即可,可以先定义一个变量rangeN,让其来划分区域

 开始时rangeN=1,区域为1则是有序,

i = i + 2*rangeN     定义 i 来区分每块区域

左区域:[begin1,end1]                右区域:[begin2,end2]

上图情况是一个特殊情况,如果遇到不能被完全划分左右区域对称的情况分为以下三种:

情况1:

当最后一个区域进行归并时,最后一组的左区间越界,所以需要对左区间的end1进行控制

情况2:

当最后一个区域进行归并时,最后一组的右区间全部越界,所以需要对右区间的begin2进行控制

情况3:

 当最后一个小组进行归并时,由于右区间越界,所以我们需要对右区间end2进行控制

 

非递归代码实现

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	// 归并每组数据个数,从1开始,因为1个认为是有序的,可以直接归并
	int rangeN = 1;

	while (rangeN < n) 
	{
		for (int i = 0; i < n; i += 2 * rangeN)
		{
			// [begin1,end1][begin2,end2] 归并

			int begin1 = i, end1 = i + rangeN - 1;
			int begin2 = i + rangeN, end2 = i + 2 * rangeN - 1;

			int j = i;

			// end1 begin2 end2 越界的三种情况
            //一定需要按顺序进行判断,不然会出错
			
            //end1越界,情况1,
            //解决:直接退出本次循环,可以不让后面的进行归并,再下一次循环时再排序
            if (end1 >= n)
			{
				break;
			}

            //begin2出界,情况2,
            //解决:直接退出本次循环,可以不让后面的进行归并,再下一次循环时再排序
			else if (begin2 >= n)
			{
				break;
			}

            //end2越界,情况3
            //解决:让end2等于数组最后的下标
			else if (end2 >= n)
			{
				end2 = n - 1;
			}

            //开始按顺序归并
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}

			// 归并一部分,拷贝一部分
			memcpy(a + i, tmp + i, sizeof(int)*(end2 - i + 1));
		}

		rangeN *= 2;
	}

	free(tmp);
	tmp = NULL;
}

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,
    归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

排序算法复杂度及稳定性分析

什么时稳定性?

稳定性的价值:

比如再考试排名的时候,第三名种有三个人的成绩相同,那么如果先交卷的人是第三名的话,就要去再成绩排序的时候保证其稳定性

 

各种常见排序算法的总结

 

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

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

相关文章

《剑指大前端全栈工程师》--大前端时代全站式开发,直指大厂P7技术专家

【内容提要】 实力打造大前端时代&#xff0c;走在时代的钱端&#xff01;   实战驱动教学&#xff0c;探索前端黑科技。紧跟企业实际技术选型&#xff0c;追求技术的实用性与前瞻性完美结合&#xff01;   本书对大前端技术栈进行了全面的讲解&#xff0c;内容涉及HTML5CS…

AI产品铺天盖地,企业却用不上?

近年来,随着人工智能技术的飞速发展,越来越多的企业开始关注并尝试使用人工智能技术来提高业务效率和降低成本。然而,国内企业使用人工智能技术仍然存在一些困难和问题,主要原因如下: 国外产品不稳定或不安全 目前国内市场上存在许多国外的AI产品,例如ChatGPT、GPT-4等,但这些…

QT CTK控件 CTK开发(二)

CTK 为支持生物医学图像计算的公共开发包,其全称为 Common Toolkit。为医学成像提供一组统一的基本功能;促进代码和数据的交互及结合;避免重复开发;在工具包(医学成像)范围内不断扩展到新任务,而不会增加现有任务的负担;整合并适应成功的解决方案。 本专栏文章较为全面…

教你如何搭建物业-后勤管理系统,demo可分享

1、简介 1.1、案例简介 本文将介绍&#xff0c;如何搭建物业-后勤管理。 1.2、应用场景 该应用包含疫情上报、绿化、安保等管理功能。 2、设置方法 2.1、表单搭建 1&#xff09;新建表单【返区登记】&#xff0c;字段设置如下&#xff1a; 名称类型名称类型姓名单行文本…

【历史上的今天】3 月 17 日:苹果起诉微软;CN 域名开放注册;赛博朋克之父出生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 3 月 17 日&#xff0c;在 1958 年的今天&#xff0c;我国第一台黑白电视机诞生。当时&#xff0c;我国电视机研制技术与日本基本处在同一起跑线&#xff0c;是…

四十六、docker-compose部署

一个项目肯定包含多个容器&#xff0c;每个容器都手动单独部署肯定费时费力。docker-compose可以通过脚本来批量构建镜像和启动容器&#xff0c;快速的部署项目。 使用docker-compose部署主要是编写docker-compose.yml脚本。 一、项目结构 不论是Dockerfile还是docker-compo…

如果你想从事人工智能职业,学习Python吧

人工智能并不会抢走你的工作&#xff0c;至少目前还不会。人工智能和机器学习&#xff08;AI/ML&#xff09;最好的应用是补充人类的创造力&#xff0c;而不是取代它。具有讽刺意味的是&#xff0c;最好的大型语言模型&#xff08;LLMs&#xff09;可能是通过使用受版权保护的人…

本地环境配置自签名HTTPS证书

在本地使用的线上的https证书的话&#xff0c;每三个月需要更新一次比较繁琐&#xff0c;用本地证书也可以满足调试需求也会方便许多 下载签名工具&#xff1a; https://github.com/FiloSottile/mkcert/releases/tag/v1.4.4 根据需求下载对应系统的版本&#xff0c;以64位的win…

有奖征文|小鱼再进化!OceanBase 4.1免费体验

OceanBase 4.0&#xff08;小鱼&#xff09;的首次亮相是在 2022 年 8 月&#xff0c;作为业内首个单机分布式一体化架构的数据库&#xff0c;4.0 版本兼顾了分布式架构的扩展性和集中式架构的性能优势&#xff0c;在同等硬件条件下实现单机性能赶超集中式数据库的同时&#xf…

【分享】群报数入驻集简云平台,实现无代码集成数百款应用

群报数介绍 群报数是一款人人可用的轻量化统计小程序&#xff0c;支持填表、报名、接龙、预约、打卡、问卷、通知等多种场景。 群报数集简云使用场景 企业的用户信息&#xff0c;人才信息往往在很多不同的系统里&#xff0c;比如CRM系统&#xff0c;客服系统&#xff0c;人力…

打造智慧医疗新生态:互联网医院系统源码分析

在数字化时代&#xff0c;医疗行业也在不断地探索新的模式和方法&#xff0c;以更好地服务于人民群众。互联网医院系统作为一种新型医疗服务模式&#xff0c;受到了广泛的关注和热议。下文&#xff0c;小编将为大家介绍互联网医院系统的概念、特点以及如何利用互联网医院系统源…

Rhodamine-PEG-NH2,罗丹明-聚乙二醇-氨基的结构式,一文了解RB-PEG-NH2的使用

RB-PEG-NH2,罗丹明-聚乙二醇-氨基 中文名称&#xff1a;罗丹明-聚乙二醇-氨基 英文名称&#xff1a;RB-PEG-NH2 性状&#xff1a;粉红色/红色固体或者粘稠液体&#xff0c;取决于分子量大小。 溶剂&#xff1a;溶于水和常规性有机溶剂 激发/发射波长&#xff1a;570nm/590…

开源周报第 89 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 Databend v1.1 …

【Mybatis源码分析】Mybatis中的反射(MetaObject)详细讲解

Mybatis中的反射 一、引入MetaObject二、MetaObject 源码分析1. 使用MetaObject 三、BeanWrapper源码分析1. MetaClass2. ReflectorFactory3. Reflector 四、总结 一、引入MetaObject 在使用Mybatis&#xff0c;编写DQL语句时&#xff0c;查询结果可能会是多个&#xff0c;多变…

Java实现图片验证码功能

文章目录一、背景二、实现步骤1、maven中加入依赖2、CaptchaController.java3、生成验证码配置4、CaptchaService.java接口5、CaptchaServiceImpl.java实现类6、增加验证码校验涉及文件一、背景 在实现登录功能时&#xff0c;为了防止特定的程序暴力破解&#xff0c;一般为了安…

使用DevExpress22.X(Patch)控件库在VisualStudio2022使用C#进行Winform、WPF应用的开发,看这一篇就够了!

写在开头&#xff0c;Dev Express是个十分强大的控件库&#xff08;下文简称Dev&#xff09;&#xff0c;但碍于其高昂的使用费用&#xff0c;“出于学习目的”&#xff0c;我们一般使用的都是Patch版本&#xff08;在版权意识日趋加强的当下&#xff0c;不要提那两个字&#x…

面试题React

1.React Fiber是什么&#xff1f; 在 React V16 将调度算法进行了重构&#xff0c; 将之前的 stack reconciler 重构成新版的 fiber reconciler&#xff0c;变成了具有链表和指针的 单链表树遍历算法。通过指针映射&#xff0c;每个单元都记录着遍历当下的上一步与下一步&…

接口测试用例编写和接口测试模板

一、简介 接口测试区别于传统意义上的系统测试&#xff0c;下面介绍接口测试用例和接口测试报告。 二、接口测试用例模板 功能测试用例最重要的两个因素是测试步骤和预期结果&#xff0c;接口测试属于功能测试&#xff0c;所以同理。接口测试的步骤中&#xff0c;最重要的是将…

149.网络安全渗透测试—[Cobalt Strike系列]—[HTTP Beacon重定器/代理服务器/流量走向分析]

我认为&#xff0c;无论是学习安全还是从事安全的人多多少少都会有些许的情怀和使命感&#xff01;&#xff01;&#xff01; 文章目录一、Cobalt Strike 重定器1、Cobalt Strike 重定器简介2、重定器用到的端口转发工具二、cobalt strike重定器实验1、实验背景2、实验过程3、流…

Springboot项目Aop、拦截器、过滤器横向对比

前言 伟人曾经说过&#xff0c;没有调查就没有发言权(好像是伟人说的&#xff0c;不管谁说的&#xff0c;这句话是正确的)&#xff0c;有些东西看着简单&#xff0c;张口就来&#xff0c;但很有可能是错的。我个人的经验是&#xff0c;aop、过滤器、拦截器的实现方式很简单&…