希尔排序【C语言】

news2024/9/21 11:15:19

希尔排序

前言

在上一篇文章中我们了解了直接插入排序算法(建议先阅读),但其实这个算法还是有一定优化空间的。而它优化之后,就变成了另一个大名鼎鼎的排序算法:希尔排序。

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序的算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。


正文

记得么,我们说直接插入排序最差的情况也就是降序(我们要排升序)时的情况。那么有什么办法可以让这种情况的时间复杂度得到提升吗?

是有的,我们可以将我们要排序的数组划分为几段,在一段中进行直接插入排序,排完后再划分为大一点的段,再各自直接插入排序…这样我们的时间复杂度就会小于O(n²)。

希尔排序其实就是两步:

  1. 预排序
  2. 直接插入排序

希尔排序法的基本思想是:先选定⼀个整数(通常是gap=n/3+1),把待排序⽂件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进⾏排序;

然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进⾏插⼊排序;

当gap=1时,就相当于 直接插⼊排序。 它是在直接插⼊排序算法的基础上进⾏改进⽽来的,综合来说它的效率肯定是要⾼于直接插⼊排序算法的。

我们来画图理解一下这个排序过程:

现在我们先有这10个数据(n=10),让gap为5(n/2),也就是步长为5。也就是每隔5个数我们将其划分到一个组中。

9和4为一组,1和8为一组,2和6为一组,5和3为一组,7和5为一组。

我们划分了5组,每组2个数据,在每组里进行直接插入排序

(为了方便观察与直接插入排序的区别,将直接插入排序代码放在这:)

在每组里进行直接插入排序,也就是说在第一组里,让end为9,tmp为4。

按照我们之前的直接插入算法的做法,我们比较end与tmp的大小,end更大应该往后放,但是不再是放到end+1的位置了,而是放到end+gap的位置;

end再往前走也不再是减1而是减gap了;

end越界了,此时我们将tmp不再是放到end+1而是放到end+gap的位置。

剩下的4组也是同样的操作。

可以看到这样进行完,我们基本把小的数据放到前面而大的数据放到后面了。这第一步就叫做预排序。

然后我们gap/2,让gap变为2。每隔2划分为一组。

我们先尝试写这个代码。

我们仍用刚才的数据,但先让gap为3来看看。

这一步我们将end与tmp比较,满足条件后将tmp位置替换为end的值。

这一步我们让end往前走,发现end>=0不满足,跳出了循环,执行arr[end+gap]=tmp这一句。

然后我们再次进入外层循环。这时我们将i++改为i+=gap,来观察我们一组排完再排下一组的情况。

所以这时进入外层循环end来到的是第一组的第二个数据,也就是现在的9的位置,tmp就走到了第一组的第三个数据也就是8的位置。然后再次进行组内直接插入排序:

可见,这时第一组(黄色)的前三个数据5 8 9就排为有序了。又回到外层循环,i+=gap,所以end来到现在的9,tmp来到现在5的位置。

最后,我们的第一组就有序了。

可以看到我们此时的外循环已经结束了,但是我们只排了一组,也就是说我们外面要再来一层循环。

但是这些排完之后我们也只是把三组各自直接插入排序了,也就得到5 1 2 5 6 3 8 7 4 9,接近有序但仍然不是有序的。所以我们的gap还要除3,变为1,也就是变成了直接插入排序了

通过预排序,小的靠前,大的靠后,基本有序,然后再直接插入排序(gap=1),时间复杂度就比直接插入排序低很多。

然后我们会发现,由于gap要改变,代码会变成4层循环:

这样循环太多层了,我们需要优化

怎么优化呢?

我们刚才是一组一组分开去排的,一组排完了才能排下一组,现在我们不这么做,将代码改为:

将刚才第二层循环去掉,并且将剩下的for循环里的i+=gap改为i++

现在我们就变成了end从前往后逐次加一,也就是排完第一组的前两个数据后我们就先排第二组的前两个数据,然后再排第三组的前两个数据……这样走下去我们会走到第一组的第二个数据和第三个数据,这样我们也能把一组排好。总之,不再是一组一组去排。

还有一个问题,我们的gap到底取取多少呢?

我们一般用gap/3,也就是先分为3组。然后我们刚才所说,gap>1时我们是预排序,而gap=1时则为直接插入排序,也就是最后的一次排序。所以我们要让gap最后一次除3为1。假如我们现在n为6,第一次除3为2,第二次除3变为0,达不到我们要的效果。所以我们改为gap=gap/3+1,保证最后一次gap一定为1。

然后容易写错的,是我们的外层while循环条件,注意不能写为>=,否则在gap变为1后也就是理应最后一次排序后,gap/3+1还是1,又进入循环,变成了死循环

希尔排序与直接插入排序对比
//直接插入排序
void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n-1; i++)
	{
		int end = i;//end是有序区间的最后一个数据
		int tmp = arr[end + 1];

		while (end>=0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];//往后放一位
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}
//希尔排序
void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap>1)//不能是gap>=1,否则会死循环!!
	{
		gap = gap / 3;

		for (int i = 0; i < n - gap; i++)//这样tmp才不会越界
		{
			int end = i;//end最后一次为n-gap-1
			int tmp = arr[end + gap];//tmp最后一次为n-1
			while (end >= 0)
			{
				if (arr[end] > tmp)
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = tmp;
		}
	}
}

可以看到是非常相似的。有意思的是我们明明加了一个外层循环while,却让时间复杂度下降了。

测试代码:排序性能对比

在上一篇文章“直接插入排序”中我们写的10万个随机数的时间测试,我们在这里再次使用(具体代码详见上一篇文章):

从直接插入排序和希尔排序的对比我们可以直观地看到这个优化的显著效果。

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

我们知道对于直接插入排序,时间复杂度最差情况也就是随着有序区间的增大,交换的次数也越来越多。而希尔排序我们分组,每组的交换并不多,且最后能实现(升序)小的数据基本在左边,大的数据基本在右边(也就是预排序),这样我们随着有序区间的增大,交换的次数也很少。在这种情况下再去直接插入排序,时间复杂度就好了很多。

我们先来看外层循环多少次,gap>1,gap=gap/3(忽略常数1),也就是说gap一开始为n,每次除3直到为1,那么会进行多少次呢?假设进行x次,那么 3 x = n 3^x=n 3x=n成立,所以 x = l o g 3 n x=log_3n x=log3n。我们说过底数可以忽略不计,所以外层的复杂度就为logn。

内循环有两个,我们再来分析一下。

到gap为1时不可能是最坏的情况,而是小的在左边,大的在右边,已经接近有序了,所以时间复杂度为O(n)。

而这个顶点的位置就为最差情况,我们难以计算,总之最后我们希尔排序的时间复杂度就为 O ( n 1.3 ) O(n^{1.3}) O(n1.3)

本文到此结束,感谢阅读=_=

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

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

相关文章

PDF怎么转换成Word?这些工具一键搞定!

在日常生活中&#xff0c;我们经常遇到需要将PDF文件转换成Word文档的情况。PDF怎么转换成Word&#xff1f;一些工具的使用十分重要&#xff01;下文中就为大家推荐几个亲测好用的PDF转换工具。 一、Foxit PDF转换大师&#xff08;365客户端&#xff09; 链接&#xff1a;www…

【Story】编程迷航:从 “ 我怎么才学会 ? ” 到 “ 我怎么这么厉害 ! ”

目录 大学生编程入门指南&#xff1a;选择语言、制定计划与避坑技巧1. 选择适合的编程语言1.1 Python1.2 Java1.3 C/C1.4 JavaScript1.5 SQL 2. 制定有效的学习计划2.1 设定明确的目标2.2 制定学习时间表2.3 选择学习资源2.4 实践和项目 3. 避免常见学习陷阱3.1 避免过度焦虑3.…

7月31日学习笔记 基于域名,Ip,端口多虚拟主机配置以及上线商城系统

一&#xff0c;基于域名的虚拟主机 效果 劫持域名访问指定的文件夹的目录 步骤 1. vim /usr/local/nginx/conf/nginx.conf 2. 创建新的目录 mkdir /html/devopt/ 3. 在 devops 目录下添加新的 index.html 文件 4. 在 http 模块下添加新的 server 模块 1 . 设置监听的端…

数字的位操作——7、9、479、564、231、342

7. 整数反转(中等) 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例 1&…

钢铁无组织排放的超低改造(朗观视觉)

朗观视觉小编观察发现&#xff1a;随着环保政策的日益严格和公众对环境质量要求的不断提高&#xff0c;钢铁行业作为重工业的代表&#xff0c;面临着巨大的环保压力。无组织排放作为钢铁企业环保治理的难点之一&#xff0c;如何实现超低排放成为行业关注的焦点。本文将从技术路…

MAC安装mysql以及配置环境变量

安装mysql 下载mysql,网址&#xff1a;MySQL :: Download MySQL Community Server 我下载的版本是mysql-9.0.1-macos14-arm64.dmg 打开&#xff0c;双击 一路点击继续安装即可&#xff1b; 最后需要给root设置密码后就安装完成了 但是打开终端输入mysql&#xff0c;依然显…

河南萌新联赛2024第(三)场:河南大学

传送门&#xff1a;河南萌新联赛2024第&#xff08;三&#xff09;场&#xff1a;河南大学_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ B 正则表达式 思路&#xff1a;模拟 代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long lo…

狗狗换毛期怎么办?家里狗毛遍地,狗毛空气净化器帮你解决

对于养狗家庭来说&#xff0c;换毛季节的到来无疑是一场家中的“毛发风暴”。特别是养如萨摩耶、金毛等大型长毛犬的朋友们&#xff0c;更是深有体会。每日即便精心梳理&#xff0c;家中仍难以避免地被层层狗毛所覆。狗狗时不时晃一下身体&#xff0c;抖动身上的毛发&#xff0…

【list的模拟实现】

list的模拟实现 小杨 list相关类要实现的接口 namespace yang {// List的节点类template<class T>struct ListNode{ListNode(const T& val T());ListNode<T>* _prev;ListNode<T>* _next;T _val;};//List的迭代器类template<class T, class Ref, cla…

土壤分析仪:解锁土壤奥秘,赋能现代农业的绿色引擎

在广袤无垠的大地上&#xff0c;土壤是生命之源&#xff0c;滋养着万物生长。然而&#xff0c;随着现代农业的快速发展和环境的不断变化&#xff0c;土壤的健康状况日益受到关注。如何科学、精准地了解土壤的性质与养分状况&#xff0c;成为现代农业可持续发展的关键。这时&…

ST-LINK未能串口keil识别的一个可能解决方案(前提驱动安装无问题)

打开这个软件&#xff0c;在点击清除之前&#xff0c;按住单片机复位按钮不放&#xff0c;点击清除按钮&#xff0c;等待3-5秒放开复位按钮&#xff0c;即可清除重置&#xff0c;若提示没识别到&#xff0c;多重复几次&#xff0c;即可重置&#xff0c;重置完成之后再回到烧写软…

兴业严选|朝阳优质好房合集 低至6.3折起~

7月25日&#xff0c;存款挂牌利率迎来今年首次下调。中国工商银行、中国农业银行、中国银行、中国建设银行四家大型商业银行从7月25日起&#xff0c;均下调了人民币存款挂牌利率。这是今年以来大型商业银行首次下调人民币存款利率&#xff0c;也是自2022年9月以来的第五次下调。…

不是ChatGPT模型,第一个GAI是ELIZA,你听说过吗?

人工智能&#xff08;Artificial Intelligence, AI&#xff09;的概念可以追溯到20世纪50年代&#xff0c;当时数学家和计算机科学家开始探讨如何让机器模拟人类智能。1956年&#xff0c;达特茅斯会议被认为是人工智能研究的正式起点。然而&#xff0c;生成式人工智能&#xff…

day7 Excel教程——如何用单元格格式给表格化个妆?(超多干货)

day7 如何用单元格格式给表格化个妆&#xff1f; 目录 1. 单元格内容 Excel中单元格内容分为文本、数值、逻辑值。在没有任何格式下&#xff1a; 文本&#xff1a;左对齐&#xff0c;不能计算 数值&#xff1a;右对齐&#xff0c;可以计算 逻辑值&#xff1a;对/错&#xff0…

XR-Frame 计算相机与场景物体的距离

如下哦 const cameraTransform this.scene.getElementById(camera).getComponent(transform)const modelTransform this.scene.getElementById(yourNodeId).getComponent("transform");if (cameraTransform.worldPosition.distanceTo(modelTransform.worldPosition…

Simulink代码生成:基本算数运算

文章目录 1 引言2 模块使用实例2.1 Add模块2.2 Product模块2.3 Gain模块 3 代码生成4 总结 1 引言 算数运算是Simulink中的一种基本运算&#xff0c;对应C语言中的算数运算符&#xff0c;包括加、减、乘、除和取模运算。本文研究这几种运算在Simulink的使用&#xff0c;以及生…

微服务架构革新:百度Jarvis2.0与云原生技术的力量

作者 | 商业广告平台团队 导读 从十几个模块到上千个微服务&#xff0c;百度如何构建业界最复杂的微服务系统&#xff1f;Jarvis平台&#xff0c;十年磨一剑&#xff0c;集服务治理、配置管理、链路追踪于一体&#xff0c;打造云原生控制中心。Jarvis2.0&#xff0c;多运行时架…

大型分布式B2B2C多用户商城7.0企业版源码分享【java语言、方便二次开发】

项目介绍 项目基于SpringBoot开发&#xff0c;运营端和商户端采用ElementVue&#xff0c;买家使用采用VueIviewnuxt服务端渲染。使用到的中间件有Redis、RabbitMQ、ElasticSearch、FastDFS、Mongodb等。主要功能包括有运营管理、商品管理、订单管理、售后管理、会员管理、财务…

【PyQt5】一文向您详细介绍 QRadioButton() 的作用

【PyQt5】一文向您详细介绍 QRadioButton() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&am…

windows下安装gcc和make

目录 引言 第一种&#xff1a;自定义安装 下载和安装MinGw 将bin目录添加进环境变量 拷贝mingw-get.exe改名为make.exe 查看gcc和make命令是否安装成功 测试make和makefile 第二种&#xff1a;免安装&#xff0c;解压可用 下载mingw64 配置环境变量 拷贝mingw-get.e…