八大算法排序@希尔排序(C语言版本)

目录

  • 希尔排序
    • 概念
    • 算法思想
      • 示例
      • 分析
      • 结论
      • 算法步骤
        • 选择增量序列
        • 按增量分组
        • 逐步缩小增量
      • 算法优势
    • 代码实现
      • 核心算法
      • 希尔排序代码实现:
    • 时间复杂度
    • 空间复杂度
    • 特性总结



该排序会关联到直接插入排序的知识点,如果对于直接插入排序还有所疑惑,可以跳转文章过去观摩一二,希望能够帮助到你。

希尔排序

概念

  希尔排序(Shell Sort)是一种基于直接插入排序的排序算法,又称缩小增量法。其主要思想是通过对数据集合进行多次的直接插入排序,每次使用不同的增量进行直接插入排序,最终使数据基本有序,最后进行一次直接插入排序,达成排序的效果。




算法思想

希尔排序是对直接插入排序的优化算法

示例

让我们回忆一下直接插入排序的特点,下面有两个数组,
数组arr1:
数组1

数组arr2:
数组2

现在使用直接插入排序,分别对数组 arr1 和 arr2 进行升序的排序,达到以下的效果:
排序结果


分析

  对于数组arr1而言,插入的第一个元素,是数组中最大的元素9。那么对于后续要插入的元素,都要进行的一个步骤是与元素9进行交换。根据最终完成的排序结果,元素9是要排到最后的,即元素9要移动八次才能到达最终的位置。
  对于数组arr2而言,插入的第一个元素是3,根据排序完成的最终结果,我们发现只需要对元素3移动两次,就能到达最终的位置。到了最终的位置后,该元素便扎根不移动了。


结论

根据两个数组中的第一个元素插入,在整个排序中的情况。我们能够发现:
  要实现升序的排序时,对于直接插入排序而言,“ 数组中较大的元素如果较靠前 ” 的情况,比起 “ 数组中较小的元素较靠前 ” 的情况移动的次数更多,即复杂度更高。这也吻合直接插入排序的特点,如果原数组大体的趋势上越接近我们要实现的排序的效果,那么直接插入排序的效率/时间复杂度相对较低( 极端情况下,数组已经有序,那么时间复杂度为O(N) );如果原数组大体的趋势上与我们要实现的排序的效果相背而驰,那么直接插入排序的效率/时间复杂度相对更高( 极端情况下,数组是逆序,那么时间复杂度为O(N^2) )。因此直接插入排序的时间复杂度为O(N^2)。


了解至此,让我们再次解读 “ 希尔排序是对直接插入排序的优化算法 ” 这句话的意思。希尔排序,就是进行多次的直接插入排序,每次使用不同的增量,达到让数组大体上逐渐的趋近有序且是要实现的排序的效果。最后一次使用直接插入排序的算法时,此时因为数组大体上已经趋近于最终的排序效果,所以对于整个数组的元素的移动并不多,因此效率上更高。整体上相对于直接插入排序的效率而言,得到了提高。


算法步骤

  在阐述算法步骤前,让我们想这么一个问题:希尔排序是一种多次排序的算法,且每一次的排序都是直接插入排序。那么每一次排序的区别是什么?
带着这个问题,我们对上述的数组arr1进行图文模拟希尔排序的过程。


第一次直接插入排序,选择间距 (下标 ) 等于5的两两元素进行排序。如下图:
在这里插入图片描述
元素9和元素1,下标间距为5,而距离元素1间距为5的已经超出数据的范围。其他的元素同理,即数组中每个元素有且只有一个与自己距离为5的元素(除了元素5之外)。因此间距为5时,具体的直接插入排序,实现的是:
元素9和元素1的升序排序,元素9和元素1位置互换、
元素6和元素8的升序排序,元素6和元素8位置不变、
元素7和元素2的升序排序,元素7和元素2位置互选、
元素3和元素4的升序排序,元素3和元素4位置不变、
元素5单独一个,则不移动,在原位置上。

也可以这么理解:元素之间间距相等(等于5)且是“ 邻居 ”关系的,划分为一个数组。在原先各自的位置上,对数组的元素进行排序。实现在这些位置上的数组元素是升序/降序的效果。


第二次直接插入排序,选择间距 (下标 ) 等于4的两两元素进行排序。如下图:

在这里插入图片描述

元素1、5 和元素5、4,两两元素之间间距为4,元素1、5、4组成一个小数组,元素5是元素1的“邻居”,元素4是元素5的 “邻居”,对这个小数组进行升序/降序的排序。如升序排序时,原先的顺序1、5、4,将变为1、4、5。但是位置依旧是原先的那三个位置,只是在这三个位置上的元素达成了升序的排布效果。
而元素6间距为4的只有元素9的位置符合,而距离元素9间距为4的已经超出数组的范围。因此对于元素6切分的小数组中,只有元素6和元素9这两个数据,其他同理。分别对各个切分出来的小数组进行排序,最终达到整体上趋近于想要的排序效果,如升序时,大的元素靠后。
注意:实际排序时,要想想直接插入排序是如何实现的。

同样的思路,我们对间距分别为3、2、1的进行演变模拟:


第三次直接插入排序,选择间距 (下标 ) 等于3的两两元素进行排序。如下图:

在这里插入图片描述



第四次直接插入排序,选择间距 (下标 ) 等于2的两两元素进行排序。如下图:

在这里插入图片描述



最后一次直接插入排序,选择间距 (下标 ) 等于1的两两元素进行排序。如下图:
在这里插入图片描述


  以上,通过不同的间距,进行总共5次的直接插入排序,最终达成了数组arr1升序的排序效果,整个过程的实现便是希尔排序的算法逻辑了。其中所谓的间距就是前文提到的增量
  我们发现随着不同的增量,执行直接插入排序后,数组在整体上逐渐的形成了升序的效果(较大的数靠后,较小的数靠前)。当增量等于1的时候,排序的想法和直接插入排序的想法一致。


结合以上的学习,下面给出希尔排序的步骤总结:

选择增量序列

选择一个增量序列,这个序列的选择对排序的效率有影响。常用的增量序列有希尔建议的序列(例如,n/2、n/4、n/8…直到增量为1)。


注释:
  n为数组的个数,如上述对n=9的数组arr1模拟中。选择的增量序列为5、4、3、2、1。细心的同学在观察上述模拟过程图中可以发现,有些间距的直接插入排序,对数组的变动并不大。比如间距为4的时候,只是对元素5和元素4进行了交换,其他的并没有变动。对于这种间距的直接插入排序,效果不大,但是却是实打实造成一定效率的消耗的,我们可以排除掉。
  结论:不同的增量序列,对排序的效率是有影响的。如何选取一个高效的增量序列,这牵扯到数学问题。根据前人的计算,选择“ n/2、n/4、n/8…1 ”序列或者“ n/3、n/9、…1 ”序列时,效率最理想。
注:要确保增量为1的直接插入排序存在。



按增量分组

将待排序的元素按照增量分成若干个子序列,对每个子序列进行插入排序。这样,每个子序列都是部分有序的。


注释:
  如上述间距为2时,将数组 arr1 切分为两个子序列,分别是 { 1 , 2 , 5 , 8 ,4 } 、{ 6 , 3 , 9 , 7 }。然后分别对这两个子序列进行插入排序,最终在数组 arr1 中,达成了各个子序列所在的位置是有序的。arr1 数组整体上也趋近于有序。



逐步缩小增量

重复上述步骤,逐步减小增量,直至增量为1。当增量为1时,整个序列基本有序,再进行一次直接插入排序,排序完成。


注释:
  如上述间距从5、4、3、2逐步减小时,数组 arr1 整体上已经愈发的有序。当间距/增量减小为1时,整个数组基本是有序的状态,这是进行直接插入排序,完成最终的排序效果的同时也大大的增大了排序的效率。



算法优势

希尔排序的优势在于,它可以在开始时快速地将较小的元素移动到合适的位置,从而减小后续插入排序的工作量。这样,希尔排序相较于直接插入排序在效率上有所提高,特别是对于较大数据集合。虽然希尔排序不如一些更现代的排序算法,如快速排序或归并排序,但在某些特定场景下仍然有其优势。



  简洁而言,希尔排序就是对数组进行切分,隔开间距的排序、让数组整体上接近有序。再每次缩短间距,一次次的下来,数组整体上已经临近与有序的状态。当间距等于1时,排序的想法和直接插入排序的想法一致。
下面是代码的实现。


代码实现

核心算法

// 交换数值函数
void swap(int* x1,int* x2)
{
	int tmp=*x1;
	*x1=*x2;
	*x2=tmp;
}

// 直接插入排序
void InsertSort(int* a, int n)
{
	assert(a);

	// 最后一个 n-1(下标) 插入到 前n-2个排好序列的数 
	// i是有序序列的下标,当i=n-1,即数组最后一个数的下标时,则整个数组就是有序的序列了
	for (int i = 0; i < n - 1; i++)
	{
		// 单趟排序,设定end为已排序部分的最后一个元素下标
		int end = i;	// 有序序列的最后一个下标是i
		int tmp = a[end + 1];  // a[end+1] 是即将要插入的数据

		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;	
	}
	// 出了for循环,利用直接插入算法实现对数组的升序/降序的效果
}



希尔排序代码实现:

// 交换数值函数
void swap(int* x1,int* x2)
{
	int tmp=*x1;
	*x1=*x2;
	*x2=tmp;
}

// 希尔排序 : 时间复杂度O(N^1.3 - N^2)
void ShellSort(int* a, int n)
{

	// 1、gap>1相当于预算排序,让数组接近有序
	// 2、gap == 1就相当于直接插入排序,保证有序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;//增量gap,+1保证了最后一次gap一定是1
		//gap == 1 最后一次就相当于直接插入排序
		// 直接插入排序
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;	// 有序数组的最后一个元素的下标
			int tmp = a[end + gap];	// 要插入的数据,与要排序的间距为gap

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

			a[end + gap] = tmp;
		}
	}

}

  观察以上代码,与直接插入排序的代码实现比对发现,希尔排序在直接插入排序外面多了一层循环,用来确保每次的直接插入排序的增量不同。而希尔排序循环内的直接插入排序与直接插入排序的区别就在于元素之间、边界的差异。具体的可以根据代码自己在图纸上结合上述演示的演示图带入数据过一遍。





时间复杂度

希尔排序的时间复杂度介于N^1.3 - N^2 之间,即O(N^1.3 - N^2)。具体的计算过程就不加以解释了,有兴趣的可以自行在网上了解、


空间复杂度

O(1)。


特性总结

1、 希尔排序是对直接插入排序的优化;
2.、当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的
了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的
对比;
3.、希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N^1.3—
N^2);
4.、稳定性:不稳定。

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

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

相关文章

Stable Diffusion模型概述

Stable Diffusion 1. Stable Diffusion能做什么&#xff1f;2. 扩散模型2.1 正向扩散2.2 反向扩散 3. 训练如何进行3.1 反向扩散3.2 Stable Diffusion模型3.3 潜在扩散模型3.4 变分自动编码器3.5 图像分辨率3.6 图像放大 4. 为什么潜在空间是可能的&#xff1f;4.1 在潜在空间中…

uniapp:签字版、绘画板 插件l-signature

官方网站&#xff1a;LimeUi - 多端uniapp组件库 使用步骤&#xff1a; 1、首先从插件市场将代码下载到项目 海报画板 - DCloud 插件市场 2、下载后&#xff0c;在项目中的uni_modules目录 3、最后 没有其它步骤&#xff0c;直接官网代码复制到vue文件中就可以了&#xff0c…

挑战 ChatGPT 和 Google Bard 的防御

到目前为止&#xff0c;科学家已经创建了基于人工智能的聊天机器人&#xff0c;可以帮助内容生成。我们还看到人工智能被用来创建像 WormGPT 这样的恶意软件&#xff0c;尽管地下社区对此并不满意。但现在正在创建聊天机器人&#xff0c;可以使用生成人工智能通过即时注入活动来…

stable diffusion 基础教程-文生图

置顶大模型插件资源链接 你如果没有魔法上网,请自取 百度云盘链接:链接:https://pan.baidu.com/s/1_xAu47XMdDNlA86ufXqAuQ?pwd=23wi 提取码:23wi 界面介绍 参数解释 参数解释Sampling method扩散去噪算法的采样模式,不同采样模式会带来不一样的效果steps模型生成图片的迭…

OpenCV-Python(24):模板匹配

原理及介绍 模板匹配是一种常用的图像处理技术&#xff0c;它用于在一幅图像中寻找与给定模板最匹配的区域(在一副大图中搜寻查找模版图像位置的方法)。模板匹配的基本思想是将模板图像在目标图像上滑动&#xff0c;并计算它们的相似度&#xff0c;找到相似度最高的位置即为匹配…

Docsify:一款便捷的文档生成工具

一、产品介绍 Docsify是一个简单、易用的文档生成工具。它允许用户使用Markdown编写文档&#xff0c;然后一键生成静态网站。这样&#xff0c;我可以方便地将我的知识库和项目文档分享给他人&#xff0c;同时还能保持文档的更新和完整性。 二、应用场景 非常适合个人知识库、…

Redis缓存与数据库如何保证一致性

数据库和缓存如何保证一致性&#xff1f; 目录 数据库和缓存如何保证一致性&#xff1f;背景方案先更新数据库&#xff0c;还是先更新缓存&#xff1f;先更新数据库&#xff0c;再更新缓存先更新缓存&#xff0c;再更新数据库 先更新数据库&#xff0c;还是先删除缓存&#xff…

代码随想录刷题笔记(DAY4)

今日总结&#xff1a;今天把中心放在前端学习上&#xff0c;最后一个题没有完全理解&#xff0c;明天早起补上吧。勉强算完成任务。&#xff08;已补上&#xff09; Day 4 01. 两两交换链表中的节点&#xff08;No. 24&#xff09; 题目链接 代码随想录题解 1.1 题目 给你…

Python高级用法:装饰器(decorator)

装饰器&#xff08;decorator&#xff09; Python装饰器的作用是使函数包装与方法包装&#xff08;一个函数&#xff0c;接受函数并返回其增强函数&#xff09;变得更容易阅读和理解。最初的使用场景是在方法定义的开头能够将其定义为类方法或静态方法。 不使用装饰器的代码如…

Cesium特效-2023年汇总

1-3dTiles建筑实现随机贴图 使用3dTiles的customShader接口&#xff0c;在前端实现不同白模建筑贴不同的图片 2-淡入淡出的扩散雷达效果 在扩散雷的基础上&#xff0c;实现渐隐渐现的效果 3-不规则多边形的扩散效果 指定一个中心点&#xff0c;改变每个多边形的顶点位置来实现动…

如何做一个炫酷的Github个人简介(3DContribution)

文章目录 前言3D-Contrib第一步第二步第三步第四步第五步第六步 前言 最近放假了&#xff0c;毕设目前也不太想做&#xff0c;先搞一点小玩意玩玩&#xff0c;让自己的github看起来好看点。也顺便学学这个action是怎么个事。 3D-Contrib 先给大家看一下效果 我的个人主页&am…

坐标转换 | EXCEL中批量将经纬度坐标(EPSG:4326)转换为墨卡托坐标(EPSG:3857)

1 需求 坐标系概念&#xff1a; 经纬度坐标&#xff08;EPSG:4326&#xff09;&#xff1a;WGS84坐标系&#xff08;World Geodetic System 1984&#xff09;是一种用于地球表面点的经纬度坐标系。它是美国国防部于1984年建立的&#xff0c;用于将全球地图上的点定位&#xff0…

普中STM32-PZ6806L开发板(HAL库函数实现-读取内部温度)

简介 主芯片STM32F103ZET6&#xff0c;读取内部温度其他知识 内部温度所在ADC通道 温度计算公式 V25跟Avg_Slope值 参考文档 stm32f103ze.pdf 电压计算公式 Vout Vref * (D / 2^n) 其中Vref代表参考电压&#xff0c; n为ADC的位数&#xff0c; D为ADC输入的数字信号。 实现…

Linux驱动学习—设备树及设备树下的platform总线

1、什么是设备树&#xff1f; 设备树是一种描述硬件资源的数据结构。他通过bootloader将硬件资源传给内核&#xff0c;使得内核和硬件资源 描述相对独立。 2、设备树的由来 2.1 平台总线的由来 要想了解为什么会有设备树&#xff0c;设备树是怎么来的&#xff0c;我们就要先…

网络安全—模拟IP代理隐藏身份

文章目录 网络拓扑安装使用代理服务器设置隐藏者设置 使用古老的ccproxy实现代理服务器&#xff0c;仅做实验用途&#xff0c;禁止做违法犯罪的事情&#xff0c;后果自负。 网络拓扑 均使用Windows Server 2003系统 Router 外网IP&#xff1a;使用NAT模式 IP DHCP自动分配或者…

提升软件质量与效率:UI自动化测试的重要性

在软件开发领域&#xff0c;UI自动化测试工具被广泛应用&#xff0c;其意义不仅仅体现在节省时间和资源上&#xff0c;更关系到软件质量的提升、团队效率的增加&#xff0c;以及用户体验的改善。本文将探讨使用UI自动化测试工具的重要性&#xff0c;以及它在软件开发生命周期中…

IDEA生成jar包

一、打开项目结构管理界面 英文版可以使用Ctrl Alt Shift S 打开 Project Structure 窗口 如下图 汉化idea 在设置中 tips&#xff1a;idea汉化包如果不能下载的话&#xff0c;可以手动下载安装 1、先确认自己安装的idea版本 2、来这里Chinese (Simplified) Language Pack…

一篇了解springboot3请求参数种类及接口测试

SpringBoot3数据请求&#xff1a; 原始数据请求&#xff1a; //原始方式RequestMapping("/simpleParam")public String simpleParam(HttpServletRequest request){//获取请求参数String name request.getParameter("name");String age request.getParame…

【备忘】今天写一下如何买免费证书

使用场景 使用微信支付宝支付转账时小游戏小程序接口开发时其它情况 开发中不可避免的会接触https&#xff0c;有的公司有运维去做这个事&#xff0c;有的是老板自己会搞https证书&#xff0c;咱多了解一项技术也是好事。 如何买证书 登录阿里云控制台&#xff0c;搜索ssl证…

transformers Trainer自定义optimizer和scheduler

1.需求 我自定义了一个evaluate方法&#xff0c;想在每一轮训练过后都执行一次。如果只是在TrainingArguments里设置warmup_steps100&#xff0c;那么每轮都会重置学习率&#xff0c;也就是每一轮开始的时候都会按照warmup刚开始的学习率进行训练&#xff0c;这就很头疼。 2.…