当前位置: 首页 > news >正文

【数据结构与算法】希尔排序:基于插入排序的高效排序算法

            💓 博客主页:倔强的石头的CSDN主页 

           📝Gitee主页:倔强的石头的gitee主页

            ⏩ 文章专栏:《数据结构与算法》

                                  期待您的关注

1b7335aca73b41609b7f05d1d366f476.gif

目录

一、引言 

二、基本原理

三、实现步骤

四、C语言实现

五、性能分析

1. 时间复杂度:近似为O(Nlog2N)

2. 空间复杂度:O(1)

3. 稳定性:不稳定的

六、优化

七、应用场景


一、引言 

希尔排序(Shell Sort)是插入排序的一种更高效的改进版本,也称为缩小增量排序。

希尔排序由Donald Shell于1959年提出,并在其发表的论文“A high-speed sorting procedure”中详细描述了该算法。希尔排序的直接灵感来源于插入排序,但它在插入排序的基础上进行了显著的改进,旨在提高排序效率,特别是针对大规模数据集

想要读懂希尔排序,最好先理解插入排序,参考下面这篇文章

【数据结构与算法】深入解析插入排序算法:原理、实现与优化-CSDN博客

二、基本原理

希尔排序的基本思想是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次标准的直接插入排序。

这里的“基本有序”是指待排序的数组元素值满足某个增量序列的“局部有序”,即对于某个变量gap,序列中所有距离为gap的元素之间是有序的。随着变量gap的逐渐减小,当gap减小到1时,整个序列恰好被“基本有序”,此时再对全体元素进行一次直接插入排序即可

三、实现步骤

 
1.外循环进行多轮预排序
选择一个变量序列:

这个序列是逐渐减小的,gap的值较大时,数据可以更快的前后变动,但不容易"基本有序";gap较小时数据前后变动较慢,但更接近"基本有序"。  通常可以选取gap = n/3, gap = gap/3, ...,直到gap= 1。

但是要注意,如果直接每次都/3,可能面临的情况就是最后一组gap的值跳过了1,比如n=8时,gap第一次等于2,第二次等于0,解决方法也很简单,gap每次不是/3,而是gap=gap/3+1,就可以让gap最后一次一定会减小到1

2.第二层循环,每一轮预排序中进行分组
按gap进行分组:根据当前的变量gap,将待排序的数组元素下标按gap分组,总共可以分成gap组。比如gap为3时,每一组元素的首元素分别是0,1,2

3.第三层循环,分组之后,控制组里数据执行插入排序
每一组的数据有n/gap个,下标为0,gap, 2gap, 3gap,...的元素分为一组;下标为1,gap+1,2gap+1,3gap+1……的元素分为一组……


这一层循环一个需要注意的细节就是预防数组的越界:每一组分组数据的最后一个数据一般不会是数组的最后一个数据。每次选取的要插入的数据下标是end+gap,那么这个下标不能超过n-gap。比如数组有10个元素,gap为3,第一组数据最后一个数据的下标是9,要保证这一组数据访问到下标9之后,不再向后访问,因为下一次访问end为9,要插入的数据,9+gap的位置已经没有数据了。


4.第四层循环,实现插入排序的过程
每个数据向前扫描和移动,找到合适的位置后插入,直接在插入排序代码的基础上稍加修改即可

5.递减变量gap并重复上述分组排序过程:

每完成一轮按变量gap的分组排序后,将变量gap减小,然后重复分组排序过程,直到变量gap为1,此时整个数组恰好被分成一组,进行最后一次直接插入排序。
 

四、C语言实现

void ShellSort1(int* a, int n)//希尔排序升序
{int gap = n;while (gap > 1)//多组预排序,最后一组gap==1为直接插入排序{gap = gap / 3 + 1;for (int i = 0; i <gap; i++)//控制分组的组数:gap组{for (int j = i; j < n - gap; j += gap)//控制每组的插入元素个数:n/gap个{int key = a[j+gap];int end = j;while (end >= 0 && a[end] > key)//比较和移动元素{a[end + gap] = a[end];end -= gap;}a[end + gap] = key;//满足大小关系后插入到指定位置}}}
}void ShellSort2(int* a, int n)//希尔排序降序
{int gap = n;while (gap > 1)//多组预排序,最后一组gap==1为直接插入排序{gap = gap / 3 + 1;for (int i = 0; i < gap; i++)//控制分组的组数:gap组{for (int j = i; j < n - gap; j += gap)//控制每组的插入元素个数:n/gap个{int key = a[j + gap];int end = j;while (end >= 0 && a[end] < key)//比较和移动元素{a[end + gap] = a[end];end -= gap;}a[end + gap] = key;//满足大小关系后插入到指定位置}}}
}

五、性能分析

1. 时间复杂度:近似为O(Nlog2N)

希尔排序的时间复杂度并不是一个定值,它依赖于增量序列(gap序列)的选取

  • 平均时间复杂度:在多数情况下,希尔排序的平均时间复杂度可以近似为O(Nlog2N),但这并不是一个严格的结论,因为实际性能会受到增量序列的具体选择和数据初始状态的影响。
  • 最佳时间复杂度:在最佳情况下,即数据已经接近有序时,希尔排序可以接近线性排序的效率,但具体数值难以准确给出。
  • 最差时间复杂度:在最坏情况下,希尔排序的时间复杂度仍然是O(N^2)。这通常发生在增量序列选择不当,导致算法无法有效减少数据比较和移动次数时。

2. 空间复杂度:O(1)

希尔排序是原地排序算法,它只需要一个额外的空间来存储临时变量(用于数据交换),因此其空间复杂度为O(1)。这意味着希尔排序在排序过程中不会占用额外的存储空间,这对于内存资源有限的环境非常有利。

3. 稳定性:不稳定的

希尔排序是不稳定的排序算法。在排序过程中,由于存在跳跃式的比较和移动,相同元素的相对位置可能会发生变化。因此,在需要保持元素原始顺序的场景中,希尔排序可能不是最佳选择。

六、优化

将第二层第三层循环合为一层循环,
以前是四层循环时,我们是将分组作为一层循环,每组里的数据插入作为一层循环
将两层循环合为一层之后,不是一组一组进行预排序,而是将数据逐个的,与它对应的组里的数据进行预排序,互不影响
优化之后代码更加简洁,但效率没有提升

void ShellSort(int* a, int n)//希尔排序升序代码优化版(效率没有提升)
{int gap = n;while (gap > 1)//多组预排序,最后一组gap==1为直接插入排序{gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++)//控制多组以及插入元素个数{int key = a[i + gap];int end = i;while (end >= 0 && a[end] > key)//比较和移动元素{a[end + gap] = a[end];end -= gap;}a[end + gap] = key;//满足大小关系后插入到指定位置}}
}

七、应用场景

希尔排序(Shell Sort)由于其实现简单且在某些情况下表现良好,因此在一些特定的应用场景中得到了应用。以下是一些希尔排序可能适用的场景:

  1. 中等规模数据集:对于小到中等规模的数据集(比如几千个元素),希尔排序通常能够提供良好的性能。在这些情况下,希尔排序可能比更复杂的排序算法(如快速排序、归并排序等)更容易实现且效率相当。

  2. 部分有序数据集:如果数据集已经部分有序(即元素接近它们的最终位置),希尔排序可以利用这一点来更快地完成排序。因为希尔排序通过引入间隔(gap)来允许元素在更远的距离上进行交换,这有助于减少数据移动的次数,从而加速排序过程。

  3. 内存受限的环境:希尔排序是原地排序算法,只需要O(1)的额外空间。在内存受限的环境中(如嵌入式系统或大型数据集排序时内存使用受到严格控制的场景),这一点尤为重要。

  4. 实时系统:在一些实时系统中,排序操作的响应时间非常关键。希尔排序由于其实现简单且通常能够快速完成排序,因此可能是一个不错的选择。尽管在最坏情况下其时间复杂度为O(N^2),但在实际应用中,它往往能够更快地完成排序,特别是在数据集部分有序或规模适中的情况下。

  5. 教育目的:在教学和学习排序算法的过程中,希尔排序是一个很好的例子,因为它展示了如何通过引入简单的改进(即间隔序列)来显著提高基本排序算法(如插入排序)的性能。

然而,需要注意的是,对于非常大的数据集或对数据排序的稳定性有严格要求的应用场景,希尔排序可能不是最佳选择。在这些情况下,可能需要考虑使用更高效的排序算法(如快速排序、归并排序或堆排序)或稳定的排序算法(如归并排序、冒泡排序等)。

总之,希尔排序适用于中等规模、部分有序、内存受限或需要快速响应的实时系统等场景。但在选择排序算法时,还需要根据具体的应用需求和数据集特点进行综合考虑。

相关文章:

【数据结构与算法】希尔排序:基于插入排序的高效排序算法

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​ 目录 一、引言 二、基本原理 三、实现步骤 四、C语言实现 五、性能分析 1. 时间复杂度…...

关于正点原子的alpha开发板的启动函数(汇编,自己的认识)

我傻逼了&#xff0c;这里的注释还是不要用&#xff1b; 全部换成 /* */ 这里就分为两块&#xff0c;一部分是复位中断部分&#xff0c;第二部分就是IRQ部分&#xff08;中断部分最重要&#xff09; 我就围绕着两部分来展开我的认识 首先声明全局 .global_start 在 ARM 架…...

Deep Layer Aggregation【方法部分解读】

摘要: 视觉识别需要跨越从低到高的层次、从小到大的尺度以及从精细到粗略的分辨率的丰富表示。即使卷积网络的特征层次很深,单独的一层信息也不足够:复合和聚合这些表示可以改进对“是什么”和“在哪里”的推断。架构上的努力正在探索网络骨干的许多维度,设计更深或更宽的架…...

大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】

大数据面试SQL题复习思路一网打尽&#xff01;(文档见评论区)_哔哩哔哩_bilibiliHive SQL 大厂必考常用窗口函数及相关面试题 大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】大数据面试SQL题-笔记02【...】 目录 01、力扣网-sql题 1、高频SQL50题&#xff08…...

零基础自学爬虫技术该从哪里开始入手?

零基础自学爬虫技术可以从以下几个方面入手&#xff1a; 一、学习基础编程语言 Python 是爬虫开发的首选语言&#xff0c;因此首先需要学习 Python 编程语言的基础知识。这包括&#xff1a; 语法基础&#xff1a;学习 Python 的基本语法&#xff0c;如变量定义、数据类型、控…...

CV11_模型部署pytorch转ONNX

如果自己的模型中的一些算子&#xff0c;ONNX内部没有&#xff0c;那么需要自己去实现。 1.1 配置环境 安装ONNX pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/simple 安装推理引擎ONNX Runtime pip install onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/si…...

Redis的使用(四)常见使用场景-缓存使用技巧

1.绪论 redis本质上就是一个缓存框架&#xff0c;所以我们需要研究如何使用redis来缓存数据&#xff0c;并且如何解决缓存中的常见问题&#xff0c;缓存穿透&#xff0c;缓存击穿&#xff0c;缓存雪崩&#xff0c;以及如何来解决缓存一致性问题。 2.缓存的优缺点 2.1 缓存的…...

BERT架构的深入解析

BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是由Google在2018年提出的一种基于Transformer架构的预训练模型&#xff0c;迅速成为自然语言处理&#xff08;NLP&#xff09;领域的一个里程碑。BERT通过双向编码器表示和预训练策略&am…...

数字孪生技术如何助力低空经济飞跃式发展?

一、什么是低空经济&#xff1f; 低空经济&#xff0c;是一个以通用航空产业为主导的经济形态&#xff0c;它涵盖了低空飞行、航空旅游、航空物流、应急救援等多个领域。它以垂直起降型飞机和无人驾驶航空器为载体&#xff0c;通过载人、载货及其他作业等多场景低空飞行活动&a…...

HTTP背后的故事:理解现代网络如何工作的关键(二)

一.认识请求方法(method) 1.GET方法 请求体中的首行包括&#xff1a;方法&#xff0c;URL&#xff0c;版本号 方法描述的是这次请求&#xff0c;是具体去做什么 GET方法&#xff1a; 1.GET 是最常用的 HTTP 方法. 常用于获取服务器上的某个资源。 2.在浏览器中直接输入 UR…...

数据流通环节如何规避安全风险

由于参与数据流通与交易的数据要素资源通常是经过组织加工的高质量数据集&#xff0c;甚至可能涉及国家核心战略利益&#xff0c;一旦发生针对数据流通环节的恶意事件&#xff0c;将造成较大负面影响&#xff0c;对数据要素市场的价值激活造成潜在威胁。具体来说&#xff0c;数…...

部署k8s 1.28.9版本

继上篇通过vagrant与virtualBox实现虚拟机的安装。笔者已经将原有的vmware版本的虚拟机卸载掉了。这个场景下&#xff0c;需要重新安装k8s 相关组件。由于之前写的一篇文章本身也没有截图。只有命令。所以趁着现在。写一篇&#xff0c;完整版带截图的步骤。现在行业这么卷。离…...

实验二:图像灰度修正

目录 一、实验目的 二、实验原理 三、实验内容 四、源程序和结果 源程序(python): 结果: 五、结果分析 一、实验目的 掌握常用的图像灰度级修正方法,包括图象的线性和非线性灰度点运算和直方图均衡化法,加深对灰度直方图的理解。掌握对比度增强、直方图增强的原理,…...

bash: ip: command not found

输入&#xff1a; ip addr 报错&#xff1a; bash: ip: command not found 报错解释&#xff1a; 这个错误表明在Docker容器中尝试执行ip addr命令时&#xff0c;找不到ip命令。这通常意味着iproute2包没有在容器的Linux发行版中安装或者没有正确地设置在容器的环境变量PA…...

全开源TikTok跨境商城源码/TikTok内嵌商城/前端uniapp+后端+搭建教程

多语言跨境电商外贸商城 TikTok内嵌商城&#xff0c;商家入驻一键铺货一键提货 全开源完美运营 海外版抖音TikTok商城系统源码&#xff0c;TikToK内嵌商城&#xff0c;跨境商城系统源码 接在tiktok里面的商城。tiktok内嵌&#xff0c;也可单独分开出来当独立站运营 二十一种…...

云原生、Serverless、微服务概念

云原生&#xff08;Cloud Native&#xff09; 云原生是一种设计和构建应用程序的方法&#xff0c;旨在充分利用云计算的优势。云原生应用程序通常具有以下特征&#xff1a; 容器化&#xff1a;应用程序和其依赖项被打包在容器中&#xff0c;确保一致的运行环境。常用的容器技…...

Windows上LabVIEW编译生成可执行程序

LabVIEW项目浏览器(Project Explorer)中的"Build Specifications"就是用来配置项目发布方法的。在"Build Specifications"右键菜单中选取"New"&#xff0c;可以看到程序有几种不同的发布方法&#xff1a;Application(EXE)、Installer、.Net Inte…...

ref 和 reactive 区别

在Vue 3中&#xff0c;ref和reactive都是用于创建响应式数据的API&#xff0c;但它们之间存在一些关键的区别。以下是ref和reactive的主要区别&#xff1a; 1. 数据类型处理 ref&#xff1a;主要用于定义基本类型的数据&#xff08;如字符串、数字、布尔值等&#xff09;以及…...

深度学习计算机视觉中, 多尺度特征和上下文特征的区别是?

在深度学习和计算机视觉中&#xff0c;多尺度特征和上下文特征都是用来捕捉和理解图像中复杂模式和关系的重要概念&#xff0c;但它们的侧重点有所不同。 多尺度特征 (Multi-scale Features) 多尺度特征是指在不同尺度上对图像进行特征提取&#xff0c;以捕捉不同尺度的物体特…...

Facebook未来展望:数字社交平台的进化之路

在信息技术日新月异的时代&#xff0c;社交媒体平台不仅是人们交流沟通的重要工具&#xff0c;更是推动社会进步和变革的重要力量。作为全球最大的社交媒体平台之一&#xff0c;Facebook在过去十多年里&#xff0c;不断创新和发展&#xff0c;改变了数十亿用户的社交方式。展望…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…...