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

一.冒泡排序

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Amazon云计算AWS(二)

目录 三、简单存储服务S3&#xff08;一&#xff09;S3的基本概念和操作&#xff08;二&#xff09;S3的数据一致性模型&#xff08;三&#xff09;S3的安全措施 四、非关系型数据库服务SimpleDB和DynamoDB&#xff08;一&#xff09;非关系型数据库与传统关系数据库的比较&…

Elasticsearch 认证模拟题 -2

一、题目 有一个索引 task3&#xff0c;其中有 fielda&#xff0c;fieldb&#xff0c;fieldc&#xff0c;fielde 现要求对 task3 重建索引&#xff0c;重建后的索引新增一个字段 fieldg 其值是fielda&#xff0c;fieldb&#xff0c;fieldc&#xff0c;fielde 的值拼接而成。 …

基于JSP的高校二手交易平台

开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;浏览器&#xff08;如360浏览器、谷歌浏览器、QQ浏览器等&#xff09;、MySQL数据库 系统展示 系统功能界面 用户注册与登录界面 个人中心界面 商品信息界面 摘要 本文研究了高…

Go 优雅的爬虫框架 - Colly

Colly 是一款用 Go 语言编写的优雅网络爬虫框架,速度快、灵活且易于使用 关键特性包括: 线程安全。用户友好的 API。支持 XHR(Ajax)和 WebSocket。缓存和持久化。支持速度限制和分布式爬取。强大的可扩展性。colly采集器配置 AllowedDomains: 设置收集器使用的域白名单,设…

TrueNAS开启SSH登录ROOT

简介: 从 SCALE Bluefin 22.12.0 开始,为了加强安全性并遵守联邦信息处理标准 (FIPS),root帐户登录已被弃用。所有 TrueNAS 用户都应创建具有所有必需权限的本地管理员帐户,并开始使用它来访问 TrueNAS。当根用户密码被禁用时,只有管理用户帐户才能登录 TrueNAS Web 界面。…

深入剖析 Kubernetes 原生 Sidecar 容器

1 Sidecar 容器的概念 sidecar 容器的概念在 Kubernetes 早期就已经存在。一个明显的例子就是 2015 年的这篇 Kubernetes 博客文章&#xff0c;其中提到了 sidecar 模式。多年来&#xff0c;sidecar 模式在应用程序中变得越来越普遍&#xff0c;使用场景也变得更加多样化。 其…

大语言模型拆解——Tokenizer

1. 认识Tokenizer 1.1 为什么要有tokenizer&#xff1f; 计算机是无法理解人类语言的&#xff0c;它只会进行0和1的二进制计算。但是呢&#xff0c;大语言模型就是通过二进制计算&#xff0c;让你感觉计算机理解了人类语言。 举个例子&#xff1a;单1&#xff0c;双2&#x…

【银河麒麟V10服务器OS-系统根分区扩容】指导教程手册

【银河麒麟V10服务器OS-系统根分区扩容】指导教程手册 环境信息&#xff1a;VMware虚拟软件16.0 首先查看KylinOS服务器版本&#xff1a;nkvers 备注&#xff1a; (Tercel) 版本是 V10 SP1 版本&#xff0c; (Sword) 版本是 V10 SP2 版本&#xff0c; (Lance) 版本是 V10 …

Apache SeaTunnel On SparkEngine 集成CDP

随着数据处理需求的日益增长&#xff0c;选择一个高效、灵活的数据处理工具变得尤为关键。SeaTunnel&#xff0c;作为一个开源的数据集成工具&#xff0c;不仅支持多种数据处理引擎&#xff0c;还提供了丰富的连接器和灵活的数据同步方案。 本文将详细介绍 SeaTunnel 的优势和…

笔记:Windows故障转移集群下的oracle打补丁

以下方法比较暴力&#xff0c;请谨慎使用 1&#xff0c;关闭并禁用故障转移集群的服务&#xff0c;如下 2&#xff0c;关闭故障转移集群中资源的自启动 3&#xff0c;重启服务器 4&#xff0c;手动关闭服务 net stop msdtc net stop winmgmt 5&#xff0c;分别对所有节点打…

公路资产三维实景快速建模技术方案

目录 1. 应用背景点云矢量建模特征提取1. 路面标识线自动提取2. 交通标志牌自动提取3.护栏、路缘石自动提取4.路面矢量高程自动纠正 属性及编码计算1.里程桩号自动计算2.单体化要素自动编码 公路三维实景模型自动化建模 1. 应用背景 随着“数字交通强国”建设的不断深入&#x…

「多客」圈子论坛社区交友系统开源版小程序源码|圈子社区系统

简述 社交圈子论坛系统是一种面向特定人群或特定话题的社交网络&#xff0c;它提供了用户之间交流、分享、讨论的平台。在这个系统中&#xff0c;用户可以创建、加入不同的圈子&#xff0c;圈子可以是基于兴趣、地域、职业等不同主题的。用户可以在圈子中发帖、评论、点赞等互…

表格中附件的上传及显示#Vue3#后端接口数据

表格中附件的上传及显示#Vue3#后端接口数据 实现效果&#xff1a; 表格中上传附件 代码&#xff1a; <!-- 文件的上传及显示 --> <template><!-- 演示地址 --><div class"dem-add"><!-- Search start --><div class"dem-ti…

利用audacity和ffmpeg制作测试音频文件

最近要用SIPP测试一个场景&#xff0c;需要发送双声道/16K采样率/16bit量化的PCM流&#xff0c;但是下载的素材往往不能满足参数要求。那么就自己制作。 首先下载mp3文件&#xff0c;并用audacity打开。 接下来&#xff0c;点击菜单栏中轨道-重采样&#xff0c;将采样频率设为1…

【机器学习】Samba-CoE实现高效推理部署

Samba-CoE&#xff1a;突破AI内存墙&#xff0c;实现高效推理部署 一、引言二、Samba-CoE系统概述三、突破AI内存墙的关键技术流数据流三层内存系统 四、Samba-CoE的推理部署与优化动态模型切换资源优化分配性能加速 五、代码实例与实现细节六、结语 一、引言 随着人工智能技术…

AI视频下载:ChatGPT数据科学与机器学习课程

ChatGPT是一个基于OpenAI开发的GPT-3.5架构的AI对话代理。作为一种语言模型,ChatGPT能够理解并对各种主题生成类似人类的响应,使其成为聊天机器人开发、客户服务和内容创作的多用途工具。 此外,ChatGPT被设计为高度可扩展和可定制的,允许开发人员对其响应进行微调并将其集成到…

在“AI PC”中使用NPU运行 Phi-3-mini

欢迎关注我的公众号“ONE生产力”&#xff0c;获取更多AI、云计算资讯分享&#xff01; 前段时间&#xff0c;我做了一系列微软Phi-3-mini小语言模型的教程&#xff0c;很多朋友参考教程进行了实践&#xff0c;其中有一个朋友反馈说模型token推理很慢&#xff0c;没有答道我说…

【论文精读】SAM

摘要 本文提出Segment Anything&#xff08;SA&#xff09;&#xff0c;一个可prompt的视觉分割模型&#xff0c;通过一个 能实现视觉特征强大泛化的任务在包含大量图像的数据集上对模型进行预 训练&#xff0c;旨在通过使用prompt工程解决新数据 分布上的一系列下游分割问题。…

FPGA新起点V1开发板(七-语法篇)——程序框架+高级语法(选择性做笔记)

文章目录 一、模块结构二、赋值三、条件语句 一、模块结构 默认是wire类型&#xff0c;assign是定义功能。 上面这两个always都是并行 例化 二、赋值 有两种赋值“”和“<” “”是阻塞赋值&#xff0c;也就是从上到下&#xff0c;依次完成 “”是非阻塞赋值&#xff0c;…

PMP认证与NPDP认证哪个含金量高?

PMP和NPDP&#xff0c;哪个含金量更高呢&#xff1f; PMP可以全面提升你的职业发展&#xff0c;无论你是技术人员还是项目管理人员&#xff0c;都能帮助你打破思维定式&#xff0c;拓宽视野&#xff0c;并提升管理水平和领导能力。 NPDP不仅帮助个人了解新产品开发流程和原理…