【排序】插入排序,希尔排序

news2025/1/17 0:02:24

前面我们讲述了冒泡排序和选择排序,我们本章讲的排序方法是插入排序,插入排序是希尔排序实现的基础函数,大家一定要好好理解插入排序的逻辑,这样才能在后面学习希尔排序的时候,更容易的去理解,我们直接开始。

目录

插入排序(以升序为例)

基本思想:

思路

代码实现:

总结

希尔排序

基本思想:

一组一组依次排序代码实现

多组同时排序

希尔排序的时间复杂度分析

总结

总结


插入排序(以升序为例)

基本思想:

直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

就和我们打牌时,从小到大排牌序,当拿到一张新牌时,我们从后往前找,当最后的牌大于此时的牌,那么就让最后一张牌向后挪动一位,继续和倒数第二张比较,若倒数第二张小于此时的牌,就说明找到了位置,将此时的牌插入即可。

如上图中,此时的牌为7,那么我们从后往前找,发现10>7,那么10就往后挪动,继续向前找,发现是5,而5<7,说明找到了合适的位置将7放入5的后面即完成了插入排序。

思路

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

由于插入排序插入第i个元素时,需要前i-1个元素都是有序的,因此我们仍然需要从第1个元素开始排序

假设我们目前前i个是有序的,我们排序的就是第i+1个元素,我们用tmp记录a[i+1]的值,然后从后往前开始依次比较,由下标i到下标0,我们再创建循环,用变量(j=i , j >= 0 , j - -)控制循环

当 a[j] > tmp时,a[j]就向后挪动,即a[j+1] = a[ j ]  

当a[j] < tmp 时,说明已经找到了合适的位置,直接跳出循环,并将j+1的位置赋值为tmp即可

以下便是逻辑图: 

以上是找到比tmp小正常插入的情况 ,还有一种情况,当数组在tmp前内没有比tmp小的元素时,那么就会将小数放到数组首位,逻辑图如下,还是上面数组的例子。

动态逻辑图

代码实现:

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int tmp = a[i+1];
		int j = 0;
		for (j = i; j >= 0; j--)
		{
			if (a[j] > tmp)
			{
				a[j + 1] = a[j];
			}
			else
			{
				break;
			}
		}
		a[j+1] = tmp;
	}
}

小结

直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定

希尔排序

希尔排序法又称缩小增量法。希尔排序已经算是是排序中的大哥了,所以也比较难以理解,他是与快速排序,堆排序,归并排序在同一条赛道上的排序算法,十分的厉害。

基本思想:

希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

可能比较难理解,希尔排序分为两个部分,首先进行预排序,再进行插入排序

预排序:跨元素进行分组排序,让数组更接近有序,这样再进行插入排序的时候,基本都是在有序的情况下,那么再进行插入排序就减少了数组挪动的次数,效率更高了。

我们假设跳的距离为gap,gap初始值为3

看一下逻辑图,我们就明白进行预排序后的效果

下面我们来单独实现代码,我们可以将代码拆分成几个小部分,依次实现,这样会让代码实现变得更加容易实现,这便是由小及大的思想

我们先来实现对单个分组排序的实现,即对红色排序的实现

代码实现的底层逻辑,依然是插入排序,只不过在进行遍历和比较的时候是一次跳跃gap个元素

大家看一下代码实现应该就能理解

部分代码实现

int gap = 3;
for (size_t i = 0; i < n - gap; i+=gap)
{
	int tmp = a[i + gap];
	int j = 0;
	for (j = i; j >= 0; j-= gap)
	{
		if (a[j] > tmp)
		{
			a[j + 1] = a[j];
		}
		else
		{
			break;
		}
	}
	a[j + gap] = tmp;
}

在完成单趟逻辑后,然后我们再建一个for循环,让gap组依次进行排序,即完成了一组一组依次排序的逻辑实现

一组一组依次排序代码实现

	int gap = 3;
	for (int k = 0; k < gap; k++)
	{
		for (size_t i = k; i < n - gap; i += gap)
		{
			int tmp = a[i + gap];
			int j = 0;
			for (j = i; j >= 0; j -= gap)
			{
				if (a[j] > tmp)
				{
					a[j + 1] = a[j];
				}
				else
				{
					break;
				}
			}
			a[j + gap] = tmp;
		}
	}

还有一种实现方法,是令多组同时进行排序,只需要修改一下逻辑代码即可

多组同时排序

int gap = 3;
for (size_t i = 0 ; i < n - gap; i++)
{

	int tmp = a[i + gap];
	int j = 0;
	for (j = i; j >= 0; j -= gap)
	{
		if (a[j] > tmp)
		{
			a[j + 1] = a[j];
		}
		else
		{
			break;
		}
	}
	a[j + gap] = tmp;
}

二者的逻辑图如图所示

希尔排序的时间复杂度分析

希尔排序的时间复杂度比较难计算,因为gap的取值方法很多,导致很难去计算

我们通过上面的逻辑分析,发现 在预排序阶段

gap越大,大的数可以更快的跳到后面,小的数可以更快的跳到前面,越不接近有序

gap越小,大的数跳到后面越慢,小的数跳到前面越慢,但越接近有序

因此控制gap的大小是增加效率的直接办法

但是如何取最适合的gap呢,根据大量数据统计,我们发现当gap = n/3时效率最高,但是我们需要+1,保证gap最后等于1

代码如下

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		// +1保证最后一个gap一定是1
		// gap > 1时是预排序
		// gap == 1时是插入排序
		gap = gap / 3 + 1;

		for (size_t i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

我们以n/3为例,以gap对时间复杂度的影响进行分析

如下

 代码实现

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		// +1保证最后一个gap一定是1
		// gap > 1时是预排序
		// gap == 1时是插入排序
		gap = gap / 3 + 1;

		for (size_t i = 0; i < n - gap; ++i)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

小结

1. 希尔排序是对直接插入排序的优化。


2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。


3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定,通过数据统计,希尔排序的时间复杂度大概为O(n^1.3)

4. 稳定性:不稳定

总结

以上是对插入排序和希尔排序的分析,是一种新的排序思想,其中的种种知识点也很碎很多,希望大家能够有所收获。

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

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

相关文章

Vue根据后端返回的tabList动态渲染组件信息

最近做了一个功能&#xff0c;后端根据配置信息&#xff0c;动态返回一个tabList&#xff0c;其中结构是List<String,Object> tabList; map里面的数据是 label、value 页面需要根据tablist动态渲染组件&#xff08;不同的tab都使用了组件进行了封装&#xff09; 实现效果…

[原创][Delphi多线程]TThreadedQueue的经典使用案例.

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delph…

Git介绍及应用

1.简介 Git是一个分布式版本控制器&#xff0c;通常用来对软件开发过程中的源代码文件进行管理。通过Git仓库来存储和管理这些文件&#xff0c;Git仓库分为两种&#xff1a; 本地仓库:开发人员自己电脑上的Git仓库远程仓库:远程服务器上的Git仓库 2.执行流程 3.Git代码托管服务…

NFT Insider #133:苏富比将拍卖BAYC系列NFT,Taiko创世NFT系列已上线

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members &#xff08;https://twitter.com/WHALEMembers&#xff09;、BeepCrypto &#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜…

360误删掉的文件怎么恢复?3种文件找回办法!

360是一款电脑安全辅助软件&#xff0c;可以用它来清理垃圾、查杀病毒、修复系统等。然而&#xff0c;有时文件可能会被360误认为是垃圾而自动清理&#xff0c;造成重要文件丢失&#xff0c;那么360删掉的文件怎么恢复&#xff1f;下面&#xff0c;让我们一起来看看如何恢复文件…

Java面试题:Redis持久化问题

Redis持久化问题 RDB (Redis Database Backup File) Redis数据快照 将内存中的所有数据都记录到磁盘中做快照 当Redis实例故障重启时,从磁盘读取快照文件恢复数据 使用 save/bgsave命令进行手动快照 save使用主进程执行RDB,对所有命令都进行阻塞 bgsave使用子进程执行R…

Java——面向对象进阶(一)

前言 面向对象进阶(一)&#xff1a;static&#xff0c;继承&#xff0c;this和super关键字 文章目录 一、static1.1 静态变量1.2 静态方法1.3 静态变量和静态方法在内存中 二、继承2.1 概念2.2 继承的特点和能继承什么2.3 继承中的重写2.4 this和super关键字 一、static 在 Jav…

基于拓扑漏洞分析的网络安全态势感知模型

漏洞态势分析是指通过获取网络系统中的漏洞信息、拓扑信息、攻击信息等&#xff0c;分析网络资产可能遭受的安全威胁以及预测攻击者利用漏洞可能发动的攻击&#xff0c;构建拓扑漏洞图&#xff0c;展示网络中可能存在的薄弱环节&#xff0c;以此来评估网络安全状态。 在网络安…

如何使用Vuforia AR进行增强现实技术的开发?

前言 今天是坚持写博客的第17天&#xff0c;很高兴自己可以一直坚持下来。我们今天来讲讲怎么使用Vuforia AR进行增强现实的开发。 我们需要在今天的开发中用到Vuforia AR和2018版的Unity3d 什么是Vuforia AR Vuforia AR是基于实时计算摄影机影像的位置及角度&#xff0c;并…

【Kubernetes】三证集齐 Kubernetes实现资源超卖(附镜像包)

目录 插叙前言一、思考和原理二、实现步骤0. 资料包1. TLS证书签发2. 使用 certmanager 生成签发证书3. 获取secret的内容 并替换CA_BUNDLE4.部署svc deploy 三、测试验证1. 观察pod情况2. 给node 打上不需要超售的标签【可以让master节点资源不超卖】3. 资源实现超卖4. 删除还…

吴谨言墨雨背后用真诚柱铸就爆款之路

吴谨言&#xff1a;墨雨背后&#xff0c;用真诚铸就爆款之路在繁华的娱乐圈&#xff0c;每一个成功的背后隐藏着不为人知的努力和汗水。而今天&#xff0c;我们要讲述的&#xff0c;正是这样一位用真诚和执着&#xff0c;一步步走向成功的演员——吴谨言。近日&#xff0c;一则…

古字画3d立体在线数字展览馆更高效便捷

在数字时代的浪潮中&#xff0c;大连图书馆以崭新的面貌跃然屏幕之上——3D全景图书馆。这座承载着城市文化精髓与丰富知识资源的数字图书馆&#xff0c;利用前沿的三维建模技术&#xff0c;为我们呈现了一个全新的知识世界。 随时随地&#xff0c;无论您身处何地&#xff0c;只…

python如何画函数图像

通过图像可以直观地学习函数变化、分布等规律&#xff0c;在学习函数、概率分布等方面效果显著。下面我们尝试用Python的2D绘图库matplotlib来绘制函数图像。 下面我们来实现一个简单的函数&#xff1a; 首先&#xff0c;调用matplotlib库和numpy库 import matplotlib.pyplot …

视频修复工具,模糊视频变清晰!

老旧视频画面效果差&#xff0c;视频效果模糊。我们经常找不到一个好的工具来让视频更清晰&#xff0c;并把它变成高清画质。相信很多网友都会有这个需求&#xff0c;尤其是视频剪辑行业的网友&#xff0c;经常会遇到这个问题。今天给大家分享一个可以把模糊视频修复清晰的工具…

创新指南|2个维度、3个步骤助力AI办公在企业内部顺利实施运行

AI可以提升工作质量&#xff0c;但如何在企业内部运行AI办公却可能是管理者们面临的最具挑战性的一步。众所周知&#xff0c;AI正在高速发展&#xff0c;它带给企业的好处显而易见&#xff0c;既提高了工作效率也精进了产品服务&#xff0c;然而员工对AI办公的普及却并不都是持…

Spring Boot + URule 实现可视化规则引擎,太优雅了!

Spring Boot URule 实现可视化规则引擎&#xff0c;太优雅了&#xff01; 一、背景二、介绍三、安装使用四、基础概念整体介绍库文件变量库文件常量库文件参数库文件动作库文件规则集向导式规则集脚本式规则集 决策表其他 五、运用场景六、总结 一、背景 前段时间&#xff0c…

释放视频潜力:Topaz Video AI for mac/win 一款全新的视频增强与修复利器

在数字时代&#xff0c;视频已经成为我们记录生活、分享经历的重要方式。然而&#xff0c;有时候我们所拍摄的视频可能并不完美&#xff0c;可能存在模糊、噪点、抖动等问题。这时候&#xff0c;就需要一款强大的视频增强和修复工具来帮助我们提升视频质量&#xff0c;让它们更…

​在哪些场景下,使用SOCKS5代理会特别有用?(socks5代理ip)​

SOCKS5代理作为网络协议转换的利器&#xff0c;其独特功能在众多实际场景中展现出了极大的价值。以下是几个特定场景&#xff0c;其中SOCKS5代理的使用将变得尤为重要&#xff1a; 一、网络安全与隐私访问 1.高级渗透测试&#xff1a;在网络安全领域&#xff0c;渗透测试人员…

基于ChatGLM3的本地问答机器人部署流程

基于ChatGLM3的本地问答机器人部署流程 前言一、确定文件结构1.新建文件夹储存本地模型2.下载源码和模型 二、Anaconda环境搭建1.创建anaconda环境2.安装相关库3.设置本地模型路径4.启动 三、构建本地知识库1.下载并安装postgresql2.安装c库3.配置向量插件 四、线上运行五、 全…

Llama模型家族之拒绝抽样(Rejection Sampling)(二)均匀分布简介

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…