排序算法(3)——归并排序、计数排序

news2024/12/20 18:06:47

目录

1. 归并排序 

1.1 递归实现 

1.2 非递归实现

1.3 归并排序特性总结

 2. 计数排序

代码实现

 3. 总结


1. 归并排序 

基本思想: 归并排序(merge sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

划分阶段:通过递归不断地将数组从中间点分开。

合并阶段:当数组中只有一个元素时停止划分,开始合并,将左右两个较短的子序列按照一定的规则合并。 

拆分序列 

(left+right)/2为中间点,将数组拆分成两个无序序列,循环拆分,拆分到最后,每个序列都是单独的一个元素,再开始归并。

合并有序序列 

从两个数列的第一个数开始,谁小就先取谁,放到临时数组中,指针再向后走,接着进行比较,如果一个数列走完了,另一个数列还有剩余,直接把剩余的元素依次取下来放到临时数组中。

 递归展开图

通过观察,归并排序与二叉树后序遍历递归顺序相同。 

1.1 递归实现 

//归并
void merge(int* a, int* tmp, int left, int mid, int right)//左半区的起始位置  中间点  右半区结束位置
{
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;    //[left,mid]  [mid+1,right] //划分成[left,mid-1][mid,right]会造成死循环!
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)//左半区有元素并且右半区也有元素
	{
		if (a[begin1] <= a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}

	//归并左半区剩余元素
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	//归并右半区剩余元素
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	//将临时数组中归并后的元素拷贝到原数组当中
	memcpy(a + left, tmp + left, (right - left + 1) * sizeof(int));
}
//划分和归并
void Msort(int* a, int* tmp, int left, int right)
{
	if (left >= right)
		return;
	//找中间点
	int mid = (left + right) / 2;	//[begin,mid-1]   [mid,end]	

	//递归划分左半区域
	Msort(a, tmp, left, mid);
	//递归划分右半区域
	Msort(a, tmp, mid + 1, right);

	//归并
	merge(a, tmp, left, mid, right);

}
//归并排序入口
void MergeSort(int* a, int n)
{
	//开辟一个辅助数组
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		return;
	}

	Msort(a, tmp, 0, n - 1);//划分和归并

	free(tmp);//释放空间
	tmp = NULL;
}

复杂度分析 

时间复杂度:O(n*logn)  数组划分的深度是 logn,而在每一层递归中,合并操作的时间复杂度是 O(n),所以,总的时间复杂度为O(n*logn),这个时间复杂度是在最好、最坏和平均情况下都成立的,因为归并排序不依赖于原始数组的顺序。

空间复杂度:O(n)   归并排序需要在归并过程中需要与原数组同样的存储空间存放归并结果,还需要额外的空间来存储递归调用的栈,占用空间 n+logn 

1.2 非递归实现

定义一个变量gap,规定gap为每组归并数据的数据个数,gap=1,2,4,8,16……1个和一个归并,2个和2个归并,4个和4个归并,依次循环下去直到gap>n。

//归并排序——非递归
void MergeSortNonRecur(int* a, int n)
{
	//开辟一个辅助数组
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail!");
		return;
	}
	//规定gap为每组归并数据的数据个数
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;			

			if (begin2 >= n)//第二组越界不存在,这一组就不需要归并了
			{
				break;
			}

			if (end2 >= n)//第二组begin2没越界,end2越界了,修正一下,继续归并
			{
				end2 = n - 1;
			}

			printf("[%d][%d][%d][%d]  ", begin1, end1, begin2, end2);

			int k = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					tmp[k++] = a[begin1++];
				}
				else
				{
					tmp[k++] = a[begin2++];
				}				
			}

			while (begin1 <= end1)
			{
				tmp[k++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[k++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		printf("\n");
		gap *= 2;
	}
	free(tmp);
	tmp = NULL;
}

上面两条 if 语句是为了处理一下越界情况: 

非递归的迭代方法,避免了递归时深度为log₂n的栈空间,空间只是用到归并临时用的tmp数组,空间复杂度为O(n),避免递归在时间性能上有一定的提升,使用归并排序时,尽量考虑用非递归方法。

1.3 归并排序特性总结

时间复杂度:O(n*logn)

空间复杂度:O(n)

稳定性:稳定

缺点:缺点在于需要O(N)的空间复杂度,归并排序应用更多的是解决在磁盘中的外排序问题。

适用性:归并排序适用于各种数据规模的排序,而且对于大规模数据的排序效果较好。它的时间复杂度稳定在O(nlogn),不会因为数据规模的增大而导致时间复杂度的增加。由于其空间复杂度较高,通常在内排序中不会使用归并排序,而是选择快速排序。在外排序中,对于无法一次性加载到内存的大规模数据进行排序,归并排序则是一个很好的选择。

 2. 计数排序

计数排序是一种非比较型排序算法,适用于一定范围内的整数排序。在计数排序中,我们不直接比较元素的大小,而是利用数组索引来统计每个元素的出现次数。

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。

操作步骤:

1. 统计相同元素出现次数

2. 根据统计的结果将序列回收到原来的序列中

要根据range的范围来创建统计数组count,比如上面数组值的范围是99~109,如果按照最大值创建,那就需要长度109的数组,会造成极大的空间浪费。 所以变形一下,每个元素在统计数组的位置为减去最小值之后的下标位置。

calloc函数开辟空间的时候会把空间的每个字节都初始化为0,这就不需要我们手动去初始化了,没有出现的数字在统计数组中就是0。

代码实现

void CountSort(int* a, int n)
{
	int min = a[0], max = a[0]; //找最大和最小值
	for (int i = 1; i < n; i++)
	{
		if (a[i] < min)
		{
			min = a[i];
		}
		if (a[i] > max)
		{
			max = a[i];
		}
	}
	
	int range = max - min + 1;//数值范围
	int* count = (int*)calloc(range, sizeof(int));
	if (count == NULL)
	{
		perror("calloc fail!");
		return;
	}

	//统计次数
	for (int i = 0; i < n; i++)
	{
		count[a[i] - min]++;
	}

	//排序
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (count[i]--)
		{
			a[j++] = i + min;
		}
	}

	free(count);
	count = NULL;   
}

计数排序是稳定的排序算法,时间复杂度为 O(n + range),空间复杂度为 O(range)。但是,计数排序对数据的要求较为严格,只适合整数和范围集中的数据。

 3. 总结

排序方法时间复杂度(平均)时间复杂度(最好)时间复杂度(最坏)空间复杂度稳定性
冒泡排序O(n²)O(n)O(n²)O(1)稳定
简单选择排序O(n²)O(n²)O(n²)O(1)不稳定
直接插入排序O(n²)O(n)O(n²)O(1)稳定
希尔排序O(nlogn)~O(n²)O(n^1.3)O(n²)O(1)不稳定
堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
归并排序O(nlogn)O(nlogn)O(nlogn)O(n)稳定
快速排序O(nlogn)O(nlogn)O(n²)O(logn)~O(n)不稳定

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

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

相关文章

Electron-Vue 开发下 dev/prod/webpack server各种路径设置汇总

背景 在实际开发中&#xff0c;我发现团队对于这几个路径的设置上是纯靠猜的&#xff0c;通过一点点地尝试来找到可行的路径&#xff0c;这是不应该的&#xff0c;我们应该很清晰地了解这几个概念&#xff0c;以下通过截图和代码进行细节讲解。 npm run dev 下的路径如何处理&…

HTML零基础入门教学

目录 一. HTML语言 二. HTML结构 三. HTML文件基本结构 四. 准备开发环境 五. 快速生成代码框架 六. HTML常见标签 6.1 注释标签 6.2 标题标签&#xff1a;h1-h6 6.3 段落标签&#xff1a;p 6.4 换行标签&#xff1a;br 6.5 格式化标签 6.6 图片标签&a…

晶闸管-直流电动机调速系统设计【MATLAB源码+Word文档】

1.1.设计指标及要求 某双闭环直流调速系统采用晶闸管三相桥式整流电路供电&#xff0c;基本数据为:直流他励电动机&#xff0c; 设计要求主要技术指标&#xff1a; 1.2 目录 2. 硬件电路设计 3. 控制电路 4. MATLAB系统仿真 转速输出波形 硬件电路图 Word文档MATLAB仿真源…

Andriod Studio | 项目构建成功,依赖无报错的情况下,却无法启动App?

启动App时出现问题&#xff08;Error running app&#xff09;&#xff1a; &#xff08;1&#xff09; Emulator failed to connect within 5 minutes 原因&#xff1a;App(模拟器)超过5分钟未响应&#xff0c;连接失败 解决办法&#xff1a;可能是因为电脑磁盘不足&#…

UE5中实现Billboard公告板渲染

公告板&#xff08;Billboard&#xff09;通常指永远面向摄像机的面片&#xff0c;游戏中许多技术都基于公告板&#xff0c;例如提示拾取图标、敌人血槽信息等&#xff0c;本文将使用UE5和材质节点制作一个公告板。 Gif效果&#xff1a; 网格效果&#xff1a; 1.思路 通过…

中宇联与亚马逊云科技共同推出Well-Architected联合解决方案

数字化转型正如火如荼地进行&#xff0c;云计算已逐渐成为企业发展的核心动力。亚马逊云科技积极承担起数字经济时代基础设施提供者及企业成长的高质量伙伴角色&#xff0c;全心全意深化客户服务&#xff0c;赋能企业迈向成功之路。基于多年服务各行各业客户的经验总结&#xf…

React+Vite从零搭建项目及配置详解

相信很多React初学者第一次搭建自己的项目&#xff0c;搭建时会无从下手&#xff0c;本篇适合快速实现功能&#xff0c;熟悉React项目搭建流程。 目录 一、创建项目react-item 二、调整项目目录结构 三、使用scss预处理器 四、组件库Ant Design 五、配置基础路由 六、配置…

JDK21 虚拟线程:能完全代替传统线程吗?聊聊 Web 应用中的场景适配

虚拟线程到底是个什么东西&#xff1f; 虚拟线程的出现&#xff0c;可以说是 Java 并发编程的一次“大手术”。本质上&#xff0c;它是对 线程模型的抽象和轻量化&#xff1a; 传统线程&#xff1a;由操作系统管理&#xff0c;每个线程需要分配较大的栈空间&#xff08;通常 …

《Vue3实战教程》13:Vue3侦听器

如果您有疑问&#xff0c;请观看视频教程《Vue3实战教程》 侦听器​ 基本示例​ 计算属性允许我们声明性地计算衍生值。然而在有些情况下&#xff0c;我们需要在状态变化时执行一些“副作用”&#xff1a;例如更改 DOM&#xff0c;或是根据异步操作的结果去修改另一处的状态。…

Intel(R) Iris(R) Xe Graphics安装Anaconda、Pytorch(CPU版本)

一、Intel(R) Iris(R) Xe Graphics安装Anaconda 下载网址&#xff1a;https://repo.anaconda.com/archive/ 双击Anaconda3-2024.10-1-Windows-x86_64&#xff0c;一直下一步&#xff0c;选择安装的路径位置&#xff0c;一直下一步就安装完成了。打开Anaconda PowerShell Promp…

如何在NGINX中实现基于IP的访问控制(IP黑白名单)?

大家好&#xff0c;我是锋哥。今天分享关于【如何在NGINX中实现基于IP的访问控制&#xff08;IP黑白名单&#xff09;&#xff1f;】面试题。希望对大家有帮助&#xff1b; 如何在NGINX中实现基于IP的访问控制&#xff08;IP黑白名单&#xff09;&#xff1f; 1000道 互联网大…

16.springcloud_openfeign之父子容器

文章目录 前言springcloud中的父子容器基础知识了解springcloud提供父子容器的支持springcloud_openfeign对父子容器的应用 总结 前言 在前面springcloud_openfeign的文章中了解到FeignClientsRegistrar在扫描到满足条件的feign接口后会生成BeanDefinition并注入到spring上下文…

写入hive metastore报问题Permission denied: user=hadoop,inode=“/user/hive”

背景 使用Doris创建hive catalog后&#xff0c;想在hive上的库中创建一个表&#xff0c;报如下图片错误 解决办法 hdfs dfs -ls /看到如下图片所示&#xff0c;只有root用户有写的权限 所以通过export HADOOP_USER_NAMEroot将hadoop的用户名改成root&#xff0c;然后再hdfs…

【AI图像生成网站Golang】项目测试与优化

AI图像生成网站 目录 一、项目介绍 二、雪花算法 三、JWT认证与令牌桶算法 四、项目架构 五、图床上传与图像生成API搭建 六、项目测试与优化 六、项目测试与优化 在开发过程中&#xff0c;性能优化是保证项目可扩展性和用户体验的关键步骤。本文将详细介绍我如何使用一…

单节点calico性能优化

在单节点上部署calicov3273后&#xff0c;发现资源占用 修改calico以下配置是资源消耗降低 1、因为是单节点&#xff0c;没有跨节点pod网段组网需要&#xff0c;禁用overlay方式网络(ipip&#xff0c;vxlan),使用route方式网络 配置calico-node的环境变量 CALICO_IPV4POOL_I…

Redis 中 IntSet 底层数据结构

IntSet 底层数据结构 序言: 像字符串 SDS 只是保存了一个变量的值&#xff0c;但是像 Redis 中也是需要保存一些集合元素的&#xff0c;这里就介绍一下其中一种集合 IntSet&#xff0c;由于是 Set 所以也有 Set 的一些特性&#xff0c;不过也多加了一些特性&#xff1a; ● 唯…

Java——网络编程(下)

(UDP通讯的实现) 1 UDP通信介绍 (面向无链接的一个传输协议——>不会创建连接——>效率高) (发送数据要经行封包操作——>使用DatagramPacket类——>底层是UDP) (DatagramPacket——>数据封包——>发送数据和接收数据都要去包装对象&#xff01;&#xf…

《Django 5 By Example》读后感

一、 为什么选择这本书&#xff1f; 本人的工作方向为Python Web方向&#xff0c;想了解下今年该方向有哪些新书出版&#xff0c;遂上packt出版社网站上看了看&#xff0c;发现这本书出版时间比较新(2024年9月)&#xff0c;那就它了。 从2024年11月11日至2024年12月18日期间&…

基于Spring Boot的校园商城系统

一、系统背景与意义 随着互联网技术的快速发展&#xff0c;电子商务已经渗透到生活的方方面面。校园作为一个相对封闭但活跃的社群&#xff0c;同样需要一个专门的线上平台来满足其特殊的需求。基于Spring Boot的校园商城系统正是为此目的而设计&#xff0c;它结合了微服务架构…

感知机收敛性定理证明

1. 问题描述 感知机收敛性定理假设&#xff1a; 存在一个参数向量 θ&#xff08;被归一化为单位向量&#xff0c;&#xff0c;以及一个正数 &#xff0c;使得对所有训练样本 满足&#xff1a; 这是线性可分的假设&#xff0c;意味着每个样本点与正确超平面之间有一个至少为的…