任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”学习小组“,沙雕博主在线答疑~此外,公众号内还有更多AI、算法、编程和大数据知识分享,以及免费的SSR节点和学习资料。其他平台(知乎/B站)也是同名「图灵的猫」,不要迷路哦~

在上一次的算法讨论中,我们一起学习了直接插入排序。它的原理就是把前i个长度的序列变成有序序列,然后循环迭代,直至整个序列都变为有序的。但是说来说去它还是一个时间复杂度为(n^2)的算法,难道就不能再进一步把时间复杂度降低一阶么?

确实,以上几种算法相对于之前的O(n^2)级别的算法真的是弱,效率可能还会差上千万倍,但是我们不妨翻看一下历史,你就会感觉每一种算法的出现都是很可贵的。

一、算法思想

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。因为在1959年之前的相当长的一段时间里,各种各样的排序算法无论是怎么花样繁多,都始终无法突破O(n^2),在当时直接插入排序已经是相当优秀的了,而排序算法不可能突破O(n^2)的声音成为了当时的主流。希尔排序的出现带来了新的希望。

该方法的基本思想是:先将整个待排元素序列切割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。由于直接插入排序在元素基本有序的情况下(接近最好情况),效率是非常高的,因此希尔排序在时间效率上比前两种方法有较大提高。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

增量的选择:在每趟的排序过程都有一个增量,至少满足一个规则 增量关系 d[1] > d[2] > d[3] >..> d[t] = 1 (t趟排序);根据增量序列的选取其时间复杂度也会有变化,这个不少论文进行了研究,在此处就不再深究。本文采用增量为n/2,以此递推,每次增量为原先的1/2,直到增量为1。

希尔排序的排序效率和选择步长序列有直接关系,从length逐步减半,这还不算最快的希尔,有几个增量在实践中表现更出色,具体可以看weiss的数据结构书,同时里面有希尔排序复杂度的证明,但是涉及组合数学和数论,希尔排序是实现简单但是分析极其困难的一个算法的例子。目前最好的序列是 塞奇威克(Sedgewick)的步长序列(摘自维基百科)

  • 希尔(Shell)原始步长序列:N / 2,N / 4,...,1(重复除以2)
  • 希伯德(Hibbard)的步长序列:1,3,7,...,2 k - 1
  • 克努特(Knuth)的步长序列:1,4,13,...,(3 k - 1)/ 2
  • 塞奇威克(Sedgewick) 的步长序列:1,5,19,41,109

二、算法步骤

算法步骤可以简单分为:

  • 用增量进行分组
  • 对每组进行插入排序

举个例子,按步长序列 [1,3,5,...] 对数组[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ] 进行希尔排序,首先按步长为5 进行分组,每行为一个分组得到:

13   25   45   10
14   59   27
94   94   73
33   65   25
82   23   39

然后对每行分组进行排序得到:

10   13   25   45 
14   27   59
73   94   94
25   33   65
23   39   82 

然后再按步长为3进行分组,每行为一个分组得到:

10   25   27   39   94   45 
14   23   94   25   65
73   13   33   59   82

对每行分组进行排序得到:

10   25   27   39   45   94 
14   23   25   65   94
13   33   59   73   82

此时数组如下所示,可以看到,元素本身已经基本有序了,此时插入排序的效率可以达到最高
[ 10 14 13 25 23 33 27 25 59 39 65 73 45 94 82 94 ]
看起来 比直接分组排序多了些步骤,而实际上是让一些小数跳过了一些比较和交换操作,直接从后面跳到了前面,从而提高了效率。下面这个动态图形象的解释了希尔排序的过程:

三、算法分析

希尔排序中对于增量序列的选择十分重要,直接影响到希尔排序的性能。我们上面选择的增量序列{n/2,(n/2)/2...1}(希尔增量),其最坏时间复杂度依然为O(n2),一些经过优化的增量序列如Hibbard经过复杂证明可使得最坏时间复杂度为O(n3/2)

排序方法 时间复杂度 空间复杂度 稳定性 复杂性
平均情况 最坏情况 最好情况
Shell 排序 O(n3/2) O(n^2) O(n) O(1) 不稳定 较复杂

(上面这个我引用的图空间复杂度有问题,原来是O(N),我修改了,其实应该是O(1))

对于希尔排序的一个理解,我觉得知乎上有个答主说的很好,从本质上剖析了高效算法之所以高效的原因:

希尔能突破O(N^2)的界,可以用逆序数来理解,假设我们要从小到大排序,一个数组中取两个元素如果前面比后面大,则为一个逆序,容易看出排序的本质就是消除逆序数,可以证明对于随机数组,逆序数是O(N^2)的,而如果采用“交换相邻元素”的办法来消除逆序,每次正好只消除一个,因此必须执行O(N^2)的交换次数,这就是为啥冒泡、插入等算法只能到平方级别的原因,反过来,基于交换元素的排序要想突破这个下界,必须执行一些比较,交换相隔比较远的元素,使得一次交换能消除一个以上的逆序,希尔、快排、堆排等等算法都是交换比较远的元素,只不过规则各不同罢了。

四、算法实现

代码在VC++环境下编译通过

/*Shell排序数组version: Shell插入排序
*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#ifndef N
#define N 100
#endif // Nint arr[N];inline int Shell_Sort(int *arr)
{register int i, j, k, tmp;int incre;  //选择一个增量,这里我们用简单的二分法for(incre = N/20; incre > 0;incre /= 2){ for(i = incre; i < N/10; i++){tmp = arr[i];// 很明显和插排的不同就是插排这里是j = i - 1j = i - incre;while( j >= 0 && tmp < arr[j]){arr[j + incre] = arr[j];j -= incre;}arr[j + incre] = tmp;}}
}int main( int argc, int *argv[])
{int i;printf("please enter 10 numbers: \n");for(i = 0;i < N/10;i++){scanf("%d", &arr[i]);}Shell_Sort(arr);printf("\n");printf("the ordered array is: \n");for(i = 0;i < N/10;i++){printf("%4d", arr[i]);}
}

输入:

5,13,7,26,54,8,42,33,72,41

输出:

参考文章:

排序算法系列—希尔排序

算法篇:希尔排序

知乎:希尔排序为什么那么牛

有趣的算法(七):3分钟看懂希尔排序(C语言实现)相关推荐

  1. 十分钟看懂数据库——数据库入门级语言总结

    从sql入门经典,到sql视频,再到自考的数据库系统原理,不知不觉中我们已经走进了数据库的世界,那么什么叫数据库,他又是干什么用的呢? 咱们先看一下百度给的解释啊. 数据库(Database)是按照数 ...

  2. 量子计算机 漫画,漫画 | 10分钟看懂量子比特、量子计算和量子算法

    原标题:漫画 | 10分钟看懂量子比特.量子计算和量子算法 请做好准备,即将进入烧脑模式! 宏观世界的生活经验很多都是表象.比如,你可能认为世界的运行是确定的.可预测的:一个物体不可能同时处于两个相互 ...

  3. 十分钟看懂图像语义分割技术

    转载于:十分钟看懂图像语义分割技术 大多数人接触"语义"都是在和文字相关的领域,或语音识别,期望机器能够识别你发出去的消息或简短的语音,然后给予你适当的反馈和回复.嗯,看到这里你应 ...

  4. java和python的web自动化有什么区别-三分钟看懂Python和Java的区别

    随着人工智能的火爆,Python和Java一直在各种流行编程语言中名列前茅.其实Java和Python有些相似,因为很多编程语言之间是互通的.Java现在还是第一,不知道Python未来会不会超越Ja ...

  5. python和java一样吗-三分钟看懂Python和Java的区别

    随着人工智能的火爆,Python和Java一直在各种流行编程语言中名列前茅.其实Java和Python有些相似,因为很多编程语言之间是互通的.Java现在还是第一,不知道Python未来会不会超越Ja ...

  6. python和java的区别-三分钟看懂Python和Java的区别

    随着人工智能的火爆,Python和Java一直在各种流行编程语言中名列前茅.其实Java和Python有些相似,因为很多编程语言之间是互通的.Java现在还是第一,不知道Python未来会不会超越Ja ...

  7. 【游戏客户端】5分钟看懂商店拍卖系统

    [游戏客户端]5分钟看懂商店&拍卖系统     大家好,我是Lampard~~     最近刚研究完图的最短路径算法[20分钟回顾四大寻路算法],现在终于有空腾出时间写一篇游戏系统的分享了. ...

  8. 图像拾取点_10分钟看懂Photoshop 照片修饰(用“消失点”滤镜编辑照片)

    "消失点"滤镜具有特殊的功能,它可以在包含透视平面(如建筑物侧面或热和矩形对象)的图像中进行透视校正.在应用诸如绘画.仿制.拷贝或粘贴,以及变换等编辑操作时,Photoshop可以 ...

  9. 怎么看到方法内引用方法的注释_网页内文字无法复制怎么办?一分钟看懂这些方法,让你随意复制...

    网页内文字无法复制怎么办?一分钟看懂这些方法,让你随意复制 现在的生活中,无论你是一名上班族还是学生或者什么职业,遇到不会的问题,总是需要上网查资料,找到某些好用的资料,却因为某些原因需要付费才能复制 ...

  10. 三相逆变器双pi控制器参数如何调节_一分钟看懂维也纳三相整流器

    欢迎加入技术交流QQ群(2000人):电力电子技术与新能源 1105621549 高可靠新能源行业顶尖自媒体 在这里有电力电子.新能源干货.行业发展趋势分析.最新产品介绍.众多技术达人与您分享经验,欢 ...

最新文章

  1. Flink 在爱奇艺广告业务的实践
  2. python 调用文件上传图片简单例子
  3. [html] 要减少DOM的数量有什么办法吗?
  4. java中byte,String,InputStream之间的转换
  5. 哈苏相机加持!一加9 Pro海外抢先发布:采用120Hz LTPO屏幕
  6. Atitit.swt 线程调用ui控件的方法
  7. 2022 年最佳 15 款网络监控工具
  8. Java、JSP大学生助学贷款管理系统的设计与实现
  9. Python之基础语法
  10. pdf格式如何压缩文件大小?
  11. Alex Fung魔方解法学习记
  12. easypoi导入图片_EasyPOI—导出Excel图片问题
  13. PAKDD 2019 都有哪些重要看点?看这篇文章就够了!
  14. JavaScript : 对LHS和RHS两个名词的理解
  15. 毕业去哪儿?看这里!神策数据 2023 校园招聘启动啦
  16. [前端笔记——HTML介绍] 4.HTML文本基础+超链接+高级文本格式
  17. Couldn't read row 0, col 0 from CursorWindow. Make sure the Cursor is initia
  18. 企业邮箱怎么设置自动回复邮件
  19. 备课大师控件开发流程
  20. 【面向对象】重载和重写

热门文章

  1. ZOJ 4067 Books (2018icpc青岛J) (贪心)
  2. SpringBoot学习之logback.xml 配置指定包或类输出至单独的日志文件中
  3. 把项目通过maven生产源码包和文档包并发布到自己的私服上
  4. 乘风破浪:LeetCode真题_027_Remove Element
  5. php 连接redis服务器
  6. Hello Word!
  7. Ubantu16.04LTS麒麟版:取消登录界面的客人回话
  8. IOI flower
  9. 基于Flex的MapGIS web开发——Flex中显示矢量地图(控件)
  10. Windows2000计划任务对机器进行重新启动