数据结构:排序(1)【冒泡排序】【插入排序】【堆排序】【希尔排序】

news2024/11/25 22:55:53

一.冒泡排序

冒泡排序实际上就是这样:

1.冒泡排序的实现 

两个数进行比较,大的往后移动。对于排序这个专题来说,这是比较简单的一种排序了:

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void BubbleSort1(int* a, int n)
{
	for (int j = 0; j < n; j++)//加上这个循环就是所有走的
	{
        //单趟走的
		for (int i = 1; i < n-j; i++)//刚开始j是0,要一直比较到n的位置,此时n的位置就是最大的,随着j的递增,后面最大的数就不用再去比较了
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
			}
		}
	}
}

 2.冒泡排序的时间复杂度

在最坏的情况下,整个数组都是逆序排列的,这个时候排列的次数最多,时间最长。基于比较次数的计算:我们一次需要比较n-1次,第二次需要比较n-2次,以此类推一直到只剩一次需要比较:c(n) = (n-1) + (n-2) + ... + 1 = n(n-1)/2,也就是O(N^2)

二.插入排序

插入排序实际上是这样:

1.插入排序的实现

根据动图我们可以看出冒泡排序和插入排序的差别,冒泡排序是旁边的两个元素两两比较,而这个插入排序是不断的往左比较,遇到比自己大的就交换。这个最主要的一点就是,前end个元素是有序的,后面是无序的。

这也是很有趣的一个排序:

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)//范围是[0~n-2]
	{
		int end = i;//end刚开始是0,最后是n-2
		int tmp = a[end + 1];//因为在后面要改变,这里记录一下下标为end+1的值
		while (end >= 0)//end就是下标,下标是大于等于0的
		{
			if (a[end] > tmp)//如果后面的值小于前面的那个值,就把后面的值用前面的值覆盖
			{
				a[end + 1] = a[end];
				--end;//end递减,但是tmp依然是刚才位置的值,tmp不变,然后再往前面比较
			}
			else
			{
				break;//因为前面的end个元素全部都有序了,这里如果没有比tmp大的了,就跳出循环
			}
		}
		a[end + 1] = tmp;//此时的end后面的那个位置就给tmp。
		                 //当然如果这个数组前面的元素刚开始就是有序的,我们只是把tmp的值重复赋值给了end+1的位置
	}
}

2.插入排序的时间复杂度

我们依然是假如我们的数据刚好是逆序的,每个元素都需要和前面的所有元素进行比较交换,此时我们需要排列的的次数最多,时间最长。所以总的比较和交换次数就是1 + 2 + 3 + ... + (n-1) = n(n-1)/2。时间复杂度就是O(N^2)

3.冒泡排序和插入排序时间复杂度比较

从数值上可以看出,它们两个的时间复杂度都是O(N^2)。那么它们两个来排序所用的时间相不相同呢?

答案是大多数情况下插入排序更优:

  1. 比较次数更少:在冒泡排序中,每一轮都需要通过相邻元素之间的比较来确定是否需要交换位置,而在插入排序中,只需要在已有序列中找到合适的位置插入新元素。因此,插入排序需要的比较次数更少。

  2. 数据交换次数更少:冒泡排序在每一次比较后,如果需要交换位置,就会立即进行交换。而插入排序只在找到合适的位置后才进行插入操作,因此数据交换次数更少。

三.堆排序

堆排序是一种很强的一种排序,相对于堆排序,前面的两种排序都是不够看的。

1.堆排序的实现

这个就比较考验我们对堆概念的理解了,如果我们需要升序的话就建大堆。

void AdjustDown(int* a, int n,int parent)
{
	int child = parent * 2 + 1;//左孩子
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])//右孩子存在并且右孩子大,孩子换成右孩子
		{
			child++;//右孩子
		}
		if (a[child] > a[parent])//如果孩子比父亲大,就交换
		{
			Swap(&a[child], &a[parent]);
			parent = child;//父亲变孩子,继续往下移动
			child = parent * 2 + 1;//再重新先找左孩子
		}
		else//因为孩子节点一定比父亲节点小,所以如果孩子不比父亲节点大的话,下面的节点也不可能比父亲节点大,直接跳出循环就行
		{
			break;
		}
	}
}
void HeapSort(int* a, int n)
{
	//升序建大堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);//从第一个非叶子节点开始
	}
	//此时根节点就是整个树最大的点
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);//把最后一个节点与根节点交换
		AdjustDown(a, end, 0);//从根结点往下
		end--;//此时数组最后的元素就是最大的,不用再管它了,继续找次大的。
	}
}

2.堆排序的时间复杂度

具体对于堆排序的时间复杂度的计算,可以看我这一篇博客:堆排序

  1. 建堆的时间复杂度: 建堆的过程是从最后一个非叶子节点开始,依次将每个非叶子节点和它的子节点进行比较和交换,保证每个节点都大于其子节点(对于大堆)。建堆的时间复杂度是O(n)。

  2. 排序的时间复杂度: 在建堆完成后,堆顶元素一定是最大的元素,将堆顶元素与最后一个元素交换,然后对剩下n-1个元素进行堆调整,再将堆顶元素与倒数第二个元素交换,以此类推。每次交换后,需要对剩下的元素进行堆调整,堆调整的时间复杂度是O(logn)。综上所述,堆排序的时间复杂度为O(n + nlogn) = O(nlogn)。

四.希尔排序

希尔排序可以分为两个部分:预排序(让数组更加接近有序)和插入排序(最终排成有序)。

我们把数组的数据分成间隔为gap的几组数据。比如下图中的9,5,8,5为红色组,1,7,6为绿色组,2,4,3为紫色组。然后分别对这几组数据进行插入排序,这样做的目的就是把这组数据接近有序。

1.希尔排序的实现 

 先来一种一组一组比较的方式,就是把红色组排完序,再去排绿色组,最后是紫色组,这个比较好理解:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)//每一次的循环都缩小间隔gap,直到gap为1
	{
		gap = gap / 3 + 1;//这里的加一保证最后一个gap一定是1,实行插入排序
		                  //当gap>1时,我们进行的都是预排序,gap为1时是插入排序
		for (int j = 0; j < gap; j++)//从这个循环开始,进行每组的插入排序,因为就gap组,所以循环gap次
		{
			for (int i = j; i < n - gap; i += gap)//每一次都是跳跃gap个数据,同时判断条件也要变为n-gap,防止后面越界
			{
				int end = i;
				int tmp = a[end + gap];//还是跟插入排序同样的道理,把end后面gap位数存起来
				while (end >= 0)
				{
					if (a[end] > tmp)
					{
						a[end + gap] = a[end];
						end = end - gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = tmp;
			}
		}
	}
}

当然如果嫌我们嵌套的循环太多,我们可以换一种方式,不再使用一组一组的方式,不过思想还是一样的:

void ShellSort1(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//到后面就能看出,这个比上一个少了一个循环
		for (int i = 0; i < n - gap; i++)//这个循环就一直遍历到n-gap的位置
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

这个比上一个少了一个嵌套循环,不过速度和思想都是一样的,就只是处理的一下循环。

2.希尔排序的时间复杂度

这个排序是相当牛逼的一种排序了,也就比快排弱一点,跟冒泡排序和插入排序就不是一个桌子上的。不过可惜的是,这个排序的时间复杂度非常的难算。具体怎么算的,其实对于我们程序员来说,我们不是非数学专业的,我们学了也不一定会。

我们只要记住一个结论就行了:O(N^1.3)。

 至于为什么它的时间复杂度为什么那么难算,我还是可以给出答案的:

但是我们想一想,这个第二趟排序的情况成不成立?当我们在第一次最坏的情况下,我们已经对数组进行了改变,当我们第二次进行预排序时,数据不一定是最坏的情况了。这里只能说,当我们的gap为1的时候,数据已经逼近有序了,此时就是直接插入排序:n

到这里这篇文章就结束了,感谢大家的观看,如有错误与不足的地方,还请多多指出。 

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

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

相关文章

安装Kubernetes v3 ----以docker的方式部署

以docker的方式部署 docker run -d \ --restartunless-stopped \ --namekuboard \ -p 80:80/tcp \ -p 10081:10081/tcp \ -e KUBOARD_ENDPOINT"http://192.168.136.55:80" \ -e KUBOARD_AGENT_SERVER_TCP_PORT"10081" \ -v /root/kuboard-data:/data \ e…

IT圈里最苦逼的职位,你中招了吗?

要我说最苦逼的是技术领导&#xff0c;并不是项目经理&#xff0c;也不是开发。 开发&#xff0c;分为前端和后端&#xff0c;产品提出了需求&#xff0c;剩下的就是你想怎么写怎么写&#xff0c;只要功能可以实现&#xff0c;剩下的都是你的事&#xff0c;但技术的领导遇到写…

C# WinForm —— 23 Timers.Timer 组件介绍与使用

1. 简介 System.Timers.Timer 计时器 轻量 每隔一段时间触发Elapsed事件&#xff0c;执行操作(不是由UI线程执行的)&#xff0c;即使事件中执行了比较耗时的操作&#xff0c;也不会造成 UI 失去响应 如果要获取服务器的计时功能的话&#xff0c;可以使用System.Timers.Timer …

第十三届蓝桥杯物联网试题(国赛)

还是那句话不能掉以轻心&#xff0c;全力以赴吧&#xff0c;遇事不要慌&#xff0c;该做的都做了&#xff0c;冷静沉稳的处理&#xff0c;看看配置&#xff0c;看看代码&#xff0c;还是不行就重启&#xff0c;都没问题换个板子 下面对比较复杂的部分的处理过程进行展现&#x…

【排序】冒泡排序

在我们的生活中&#xff0c;到处都离不开排序的作用&#xff0c;考试分数要排序&#xff0c;商场购物要排序&#xff0c;可以说排序对我们来说处处存在&#xff0c;那么从本章开始&#xff0c;我将要依次分享一些排序方法&#xff0c;从易到难&#xff0c;包括冒泡&#xff0c;…

Linux 编译安装python

以deepin操作系统安装Python3.8.10为例。 下载 python3.8.10 官网下载 Linux要下载源码&#xff0c;进行编译。 下图tarball即tar包&#xff0c;是压缩包的意思。python官网给出两种压缩格式的tarball&#xff0c;下载哪个都可以。 方式一&#xff1a;直接点击链接下载 方式…

中间件复习之-消息队列

消息队列在分布式架构的作用 消息队列&#xff1a;在消息的传输过程中保存消息的容器&#xff0c;生产者和消费者不直接通讯&#xff0c;依靠队列保证消息的可靠性&#xff0c;避免了系统间的相互影响。 主要作用&#xff1a; 业务解耦异步调用流量削峰 业务解耦 将模块间的…

线上 | OpenSergo - [规范]

INDEX 1 参考资料2 OpenSergo 与 Sentinel 关系3 规范体系3.1 服务元数据ReportMetadataRequest 信息![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ffba569841ae4668b4cff74e4d41d21f.png)##### ReportMetadataReply 信息![在这里插入图片描述](https://img-blog…

链表相交00

题目链接 链表相交 题目描述 注意点 题目数据保证整个链式结构中不存在环函数返回结果后&#xff0c;链表必须保持其原始结构设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案 解答思路 双指针分别指向headA和headB&#xff0c;当遍历完某个链表后&#xff0c;将指针指…

【面结构光三维重建】0.基于openCV实现相机的标定

1.标定结果 2.相机标定原理 相机标定是计算机视觉和机器视觉领域中的重要技术,用于确定相机成像的几何关系和畸变特性,以提高成像的精度和稳定性。该技术广泛应用于三维重建、机器人视觉、自动驾驶等领域。 世界坐标系:由用户定义的三维世界坐标系,描述物体和相机在真实世…

生产现场的作业标准化,这么做就对了!

制造型企业的生产过程是以计划的成本、工时生产出达到客户要求的产品。如果在制造过程中&#xff0c;产品工艺、作业方法或作业条件有所变化的话&#xff0c;一定无法生产出符合上述要求的产品。 因此&#xff0c;公司领导必须对作业工艺流程、作业方法、作业条件进行标准化管…

无缝接入GPT-4o:智创聚合API平台的创新与实践

在2024年5月13日&#xff0c;美国开放人工智能研究中心&#xff08;OpenAI&#xff09;发布了最新版本的ChatGPT——GPT-4o。这一更新标志着人工智能领域的又一重大进步&#xff0c;引起了全球科技界的广泛关注。GPT-4o的“o”代表“omni”&#xff08;全能&#xff09;&#x…

ARM-V9 RME(Realm Management Extension)系统架构之系统能力的设备隔离和保护

安全之安全(security)博客目录导读 目录 三、设备隔离和保护 1、外设隔离 2、非pe请求者(设备) 3、可编程完成端过滤器Programmable completer-side filters 4、RME设备分配 4.1 设备权限表 本博客探讨 RME 所需系统能力的设备隔离和保护&#xff0c;以保证 Arm CCA 对于…

辅导男朋友转算法岗的第2天|self Attention与kv cache

文章目录 公式KV CacheMHA、MQA、GQA 面试题为什么除以 d k \sqrt{d_k} dk​ ​Multihead的好处decoder-only模型在训练阶段和推理阶段的input有什么不同&#xff1f;手撕必背-多头注意力 公式 $ \text{Output} \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) \times …

SG7050EEN差分晶体振荡器:为5G路由器提供卓越的时钟源

随着5G技术的快速发展&#xff0c;5G路由器作为连接高速网络的重要设备&#xff0c;正迅速普及。为了确保5G路由器在高宽带和低延迟的网络环境中表现出色&#xff0c;选择一款高性能的晶体振荡器至关重要。爱普生推出的SG7050EEN差分晶体振荡器&#xff0c;以其高精度、低相位噪…

K210视觉识别模块学习笔记2:固件的下载升级_官方数字识别例程导入方法

今日开始学习K210视觉识别模块:固件的下载升级_官方数字识别例程导入方法 主要学习如何升级固件库&#xff0c;在哪下载固件库&#xff0c;以及如何在TF卡正确导入官方例程&#xff1a; 亚博智能的K210视觉识别模块...... 本次最终目的是正确导入官方的数字识别例程&#xff0…

Python 之SQLAlchemy使用详细说明

目录 1、SQLAlchemy 1.1、ORM概述 1.2、SQLAlchemy概述 1.3、SQLAlchemy的组成部分 1.4、SQLAlchemy的使用 1.4.1、安装 1.4.2、创建数据库连接 1.4.3、执行原生SQL语句 1.4.4、映射已存在的表 1.4.5、创建表 1.4.5.1、创建表的两种方式 1、使用 Table 类直接创建表…

小程序使用Canvas设置文字竖向排列

在需要使用的js页面引入js文件,传入对应参数即可 /** * 文本竖向排列 */ function drawTextVertical(context, text, x, y) {var arrText text.split();var arrWidth arrText.map(function (letter) {return 26; // 字体间距,需要自定义可以自己加参数,根据传入参数进行…

飞凌嵌入式FET3568/3568J-C核心板现已适配OpenHarmony4.1

近日&#xff0c;飞凌嵌入式为FET3568/3568J-C核心板适配了OpenHarmony4.1系统&#xff0c;新系统的加持使核心板在兼容性、稳定性与安全性等方面都得到进一步提升&#xff0c;不仅为FET3568/3568J-C核心板赋予了更强大的功能&#xff0c;也为开发者们提供了更加广阔的创新空间…

WordPress中借助Table of Contents Plus+Widget Options插件,实现仅在文章侧边栏显示文章目录的功能

本文转自博主的个人博客&#xff1a;https://blog.zhumengmeng.work,欢迎大家前往查看。 原文链接&#xff1a;点我访问 序言&#xff1a;今天心血来潮&#xff0c;写了一篇文章&#xff0c;忽然发现自己的文章极少有目录&#xff0c;这对于长文章的阅读来说是十分不利的&#…