归并排序与计数排序(含代码)

news2024/9/29 17:23:39

目录

目录:

        1:归并排序递归

        2:归并排序的非递归

        3:计数排序的思想


1:归并排序递归

              思路:归并排序是采用分治算法的一种排序,将两个有序的子数组合并到一个数组中去使得数组完全有序,所以我们先使子数组有序,在使整体的数组有序,这种先让子数组有序,最后在使整体完全有序,也称为二路归并。

        归并排序的核心步骤如下图:

         

                

        思路:先将数组划分为两个区间,使得左右两个区间有序 ,在借用tmp数组将两个有序的区间合并成一个有序的区间最后在将tmp数组中的值拷贝到原数组中去,而要使左右区间有序,那么我们又得将左右区间划分为两个部分,使得左右区间的子区间变成有序,在利用tmp数组来进行归并.......,这个过程肯定涉及递归与合并的过程,最后归并的过程是左右子区间都有序,即区间数为1的时候,就得开始归并了。这个思路需要自行去画图来感悟,才能有更好的理解。

        如下图:

代码如下:

void _MergeSort(int* a, int* tmp, int begin, int end)
{
    //递归返回条件
	if (begin >= end)
		return;
    //将区间分为左右两个子区间
	int mid = (begin + end) / 2;
	//递归左边
	_MergeSort(a, tmp, begin, mid);
	_MergeSort(a, tmp, mid + 1, end);

    //走到这里,说明我们得开始合并两个子数组了
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;

	int index = begin;//要保证index在tmp数组中同一个位置
	while (begin1 <= end1 && begin2 <= end2)
	{
            //取小的尾插
		if (a[begin1] > a[begin2])
		{
			tmp[index++] = a[begin2++];
		}
		else
		{
			tmp[index++] = a[begin1++];
		}
	}
    //肯定有一个区间没有完全插入到tmp数组
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}
    //拷贝回原数组                               区间为左闭右闭所以还得加1,拷贝空间的个数
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));

}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, tmp, 0, n - 1);
}

        归并排序的时间复杂度为标准的O(N*logN),因为每一层都有n个数需要合并,一共有高度层。

        空间复杂度:O(N),开了一块临时的空间,在该空间上进行插入。

2:归并排序的非递归

        我们先上代码吧!!

        

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail:");
		return;
	}
	//归并的数据个数
	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;
			//[begin1,end1]与[begin2,end2]一起进行归并
			int index = i;
			if (end1 >= n || begin2 >= n)
			{ 
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] >= a[begin2])
				{
					tmp[index++] = a[begin2++];
				}
				else
				{
					tmp[index++] = a[begin1++];

				}
			}
			//有一个未尾插完
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));
		}
		gap *= 2;
	}
	
}

        归并排序递归的思路:要想整体有序就得左右子区间先有序,一次递归下去

        非递归的思路:直接先从11开始归并,然后22归并,44归并,一直以两倍进行归并下去

       在上图采用非递归进行归并的时候,我们需要注意的就是4这个数字,当我们将数组划分为

        [begin1,end1],[begin2,end2]的时候,假如我们的end1越界了,即end1>=n或begin2越界,我们就不需要对他进行归并,直接break跳出循环就行了。

        当只有end2越界的时候,这时需要先将end2的值改为n-1,然后在进行归并即可。

        非递归形式下的时间与空间复杂度与递归一样。

3.计数排序

        关于计数排序应该是在这些排序中思路最简单的排序了。

        计数排序的思想是,统计原数组中值的个数,在另外一块空间中对应下标++,然后从下标为0开始遍历数组,当遍历到数组中的值不为0时,将该数尾插到原数组。

        我们通过图来讲解

        这种方式是采用了绝对映射的方式,即原数组中值出现,对应在count数组下标处进行++

        而当数据非常大,但是也属于相对集中的范围,则我们需要采取相对映射的方式来进行。

        相对映射是可以用来减少开辟空间的个数的。

        图:

        从这里我们也能看出来,计数排序的缺点:1.只能对相对集中的值进行排序,否则会浪费空间

        代码如下:

void CountSort(int* a, int n)
{
	int max = a[0];
	int min = a[0];
    //为相对映射做准备,统计数组中最大与最小值
	for (int i = 0; i < n; i++)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
		{
			min = a[i];
		}
	}
    //开辟range个空间,用来统计每个数出现的次数
	int range = max - min + 1;
    //在统计前一定要将count数组中值全部变为0,calloc默认初始化可以实现这一功能
	int* count = (int*)calloc(range, sizeof(int));
	//统计数组中元素的个数
	for (int i = 0; i < n; i++)
	{    //相对映射在a[i]-min处++
		count[a[i] - min]++;
	}
	int index = 0;
	for (int i = 0; i <= range; i++)
	{
            //进行最后的排序,并且将值回显
		while (count[i] > 0)//统计k次
		{
			a[index++] = i + min;
			count[i]--;
		}
	}
}

        时间复杂度:为O(N+range),N为数组的个数,range为映射开辟的空间

        空间复杂度:O(range)

        

        本章到这里在知识点就已经分享完毕了,感谢大家的耐心观看,如果你觉得有用的话可以给博主一个赞哦!!!

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

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

相关文章

CAN测量模块总线负载率,你关注了吗?

一 背景 随着新能源汽车的飞速发展&#xff0c;整车系统日趋复杂&#xff0c;整车性能的可靠性也变得愈发重要。在车辆测试过程中&#xff0c;为应对更加多样的试验需求&#xff0c;传感器的种类和数量会随着测量种类而增加&#xff0c;数据量也会因此变得越发庞大&#xff0c…

docker部署prometheus+grafana服务器监控(一)

docker-compose 部署prometheusgrafana Prometheus Prometheus 是有 SoundCloud 开发的开源监控系统和时序数据库&#xff0c;基于 Go 语言开发。通过基于 HTTP 的 pull 方式采集时序数据&#xff0c;通过服务发现或静态配置去获取要采集的目标服务器&#xff0c;支持多节点工…

Kubernetes 部署 kubeflow1.7.0

KubeFlow 是一个开源的项目&#xff0c;旨在为 Kubernetes 提供可组合、便携式、可扩展的机器学习技术栈。它最初是为了解决在 Kubernetes 上运行分布式机器学习任务所带来的挑战而创建的。Kubernetes 本身是一个容器平台&#xff0c;但在近年来&#xff0c;越来越多的公司开始…

【Html】交通灯问题

效果 实现方式 计时器&#xff1a;setTimeout或setInterval来计时。setInterval和 setTimeout 在某些情况下可能会出现计时不准确的情况。这通常是由于JavaScript的事件循环机制和其他代码执行所需的时间造成的。 问询&#xff1a;通过getCurrentLight将每个状态的持续时间设置…

解密分布式事务:CAP理论、BASE理论、两阶段提交(2PC)、三阶段提交(3PC)、补偿事务(TCC)、MQ事务消息、最大努力通知

文章目录 &#x1f34a; CAP理论&#x1f34a; BASE理论&#x1f34a; 两阶段提交&#xff08;2PC&#xff09;&#x1f389; XA事务 &#x1f34a; 三阶段提交&#xff08;3PC&#xff09;&#x1f34a; 补偿事务&#xff08;TCC&#xff09;&#x1f34a; MQ事务消息&#x1…

web开发初级工程师学习笔记ing(持续更新)!!!

web开发初级工程师学习笔记 前端开发工具实验1 VS Code 初体验介绍 前端开发工具 实验1 VS Code 初体验 介绍 VS Code 环境提供的是一个可以在浏览器中使用原生 VS Code 编辑代码的程序。在该环境中&#xff0c;你可以使用到与本地安装近乎一致的 VS Code 程序来编辑代码文件…

计算机网络(谢希仁)第八版课后题答案(第二章)

1.物理层要解决哪些问题&#xff1f;物理层的主要特点是什么&#xff1f; (1)物理层要尽可能地屏蔽掉物理设备和传输媒体&#xff0c;通信手段的不同&#xff0c;使数据链路层感觉不到这些差异&#xff0c;只考虑完成本层的协议和服务。 (2)给其服务用户&#xff08;数据链路…

IO多路复用技术

IO多路复用 一、概念 IO多路复用技术 是一种 网络通信 的方式&#xff0c;通过这种方式可以同时检测多个 文件描述符&#xff08;这个过程是阻塞的&#xff09;&#xff0c;一旦检测到某一个文件描述符&#xff08;状态是 可读 或者 可写 的&#xff09;是就绪的&#xff0c;…

苏州健雄职业技术学院人工智能学院学生在“火焰杯”软件测试开发选拔赛总决赛获奖

3月22日&#xff0c;第三届“火焰杯”软件测试开发选拔赛颁奖仪式在人工智能学院D2-102机房举行&#xff0c;软件工程20级学生和软件测试社团全体社团成员参加本次活动。本次活动由测吧&#xff08;北京&#xff09;科技有限公司项目总监王雪冬担任颁奖嘉宾&#xff0c;并为同学…

使用Windows平台的Hyper-V虚拟机安装CentOS7的详细过程

Hyper-V虚拟机安装CentOS7 前言常见Linux系统CentOSUbuntuDebianKaliFedoraArch LinuxMintManjaroopenSUSE Hyper-V开启Hyper-V打开Hyper-V Hyper-V的使用新建虚拟机开始安装分区配置开始安装 修改yum源为阿里源 前言 作为一名开发者&#xff0c;就服务器而言&#xff0c;接触最…

SpringMVC 报文信息转换器(HttpMessageConverter)

文章目录 描述1、RequestBody2、RequestEntity3、ResponseBody4、SpringMVC处理json5、SpringMVC处理ajax6、RestController注解7、ResponseEntity 描述 HttpMessageConverter&#xff0c;报文信息转换器&#xff0c;将请求报文转换为Java对象&#xff0c;或将Java对象转换为响…

Linux:firewalld防火墙-(实验2)-IP伪装与端口转发(4)

实验环境 本章实验环境要建立在上一章之上&#xff0c;ip等都是继承上一章&#xff0c;完全在上一章之下的操作 Linux&#xff1a;firewalld防火墙-小环境实验&#xff08;3&#xff09;-CSDN博客https://blog.csdn.net/w14768855/article/details/133996151?spm1001.2014.3…

动态链接函数(dlopen/dlsym/dlclose)使用总结

一、简介 动态链接函数操作&#xff08;显式运行时链接&#xff09;主要包含头文件dlfcn.h&#xff08;/usr/include/dlfcn.h&#xff09;&#xff0c;涉及的常用的函数主要有dlopen&#xff0c;dlysm&#xff0c;dlclose。主要作用是从动态库中加载函数到程序中使用&#xff…

shell脚本条件语句(极其粗糙版)

条件测试操作和条件测试语句&#xff1a; $?:条件判断&#xff0c;失败或者成功&#xff0c;真或者假&#xff0c;true false shell脚本中&#xff1a;0为真&#xff0c;true 执行成功&#xff1b;其他所有的非0 都是假&#xff0c; false&#xff0c;执行失败 条件测试的命…

如何禁止员工上班玩游戏

如何禁止员工上班玩游戏 在这个游戏盛行的年代里&#xff0c;不少游戏玩家会玩到忘我的状态&#xff0c;也有不少员工在上班的时候也要玩上两把&#xff0c;但是公司是雇佣员工的时间是来工作的&#xff0c;出现这种情况很显然是对公司不利的&#xff0c;会严重影响工作效率和…

Python print 函数用法总结

Python3 print 函数用法总结 一、print()函数概述 print() 方法用于打印输出&#xff0c;是python中最常见的一个函数。 print([*objects][,seq ][,end\n][,filesys.stdout]) 参数的具体含义如下&#xff1a; objects --表示输出的对象。输出多个对象时&#xff0c;需要用…

MySQL---表的增查改删(CRUD基础)

文章目录 什么是CRUD&#xff1f;新增&#xff08;Create&#xff09;单行数据 全列插入多行数据 指定列插入 查询&#xff08;Retrieve&#xff09;全列查询指定列查询查询字段为表达式起别名查询去重查询排序查询条件查询分页查询 修改&#xff08;Update&#xff09;删除&…

新手如何备考学习PMP?

一、PMP学习7步走攻略 1、熟悉考试大纲&#xff1a; PMP考试大纲是备考的基础&#xff0c;考生需要详细熟悉考试大纲&#xff0c;了解各个知识领域的重点和难点。 2、制定学习计划&#xff1a; 根据考试大纲和个人情况&#xff0c;制定学习计划&#xff0c;合理分配学习时间…

stm32移植u8g2库内存不足解决办法

1.现象 跟着视频教程移植完u8g2库到stm32f103c8t6后&#xff0c;进行编译&#xff0c;报了100多个空间不足的问题&#xff0c;如下图。 ..\Output\Output.axf: Error: L6406E: No space in execution regions with .ANY selector matching u8g2_fonts.o(.constdata). ..\Outp…

蓝天远控2023(VIP会员版)

蓝天远控2023&#xff08;VIP会员版&#xff09;下载地址&#xff1a;https://user.qzone.qq.com/512526231/main