![30d372f3b41b82e09f0384c6666323d8.png](https://i-blog.csdnimg.cn/blog_migrate/872cf76453854fb6553d9e9dbe28121a.jpeg)
提升效率的本质在于少做事。
一. 序
我们知道,插入排序的优点是针对比较有序的数据进行排序是相当高效的,缺点则是每次只移动一个数据。 接下来,我们就针对插入排序的缺点,对其进行优化。
二. 插入排序回顾
首先看一下插入排序的实现,以数据7、8、3、5为例:
- 取第一个数7,肯定是有序的,不需要处理,得到7、8、3、5
- 取第二个数8,比7大放在7后面,得到7、8、3、5
- 取第三个数字3,分别与8、7比较,得到3、7、8、5
- 最后取5,分别与8、7、3相比,得到3、5、7、8
不难发现,几乎每个数字之间都做了比较。 效率本身除了做事的速度,还取决于做事的量。 想要更快的完成排序,计算机的速度可以看成是常量,可以优化的在于我们如何指导计算机少做事。
插入排序的优点是对基本有序的数据排序效率高,这给我们的启发便是想办法让一个比较无序的数组先变得比较有序。 插入排序的缺点是一次只移动一个数据,优化的方向显然就是一次移动多个数据。
三. 希尔排序
由此,插入排序的进阶版本, 希尔排序 (Shell sort)被发明了出来。 还是外国人的老套路,该算法以设计者希尔(Donald Shell)的名字命名,于1959年公布。
希尔排序首先把一串数据按照一定的间隔(Gap)分开,比如7、8、3、5按照间隔2分开,就变成了两个数组7、3和8、5,这样可以分别对这两个小的数组进行排序,变成3、7和5、8。 放在原来的数组中看就变成了3、5、7、8。 最后以1的间隔扫描一遍数据,不需要再移动任何数据。
![16f67a14d19a08a37a1228c79c3a94b8.png](https://i-blog.csdnimg.cn/blog_migrate/98eb5ef4874dd6edf8ff810c54d0b35e.jpeg)
一次搞定,牛叉不牛叉,哇塞不哇塞。
到此可以看出,希尔排序通过把数据按照一定的间隔分组,然后以小组为单位,在小组内使用插入排序,然后逐步缩小分组间隔,对于100个数据A1~A100,可以先按照50为间隔分成:
- A1、A51
- A2、A52
- ……
- A50、A100
等50组,每组先进行插入排序。 然后再以25为间隔,把数据分成25组,每组4个数据进行插入排序。 以此类推,直到最后以1为间隔分成1个组,再做一次插入排序。
要注意的是,分组间隔里面间隔1是必选项,由它保证整个数据最后肯定是有序的。 其余间隔如何选择是个很复杂的数学问题,简单的实现可以用待排序数据的个数不停除以2,直到最后间隔为1停止。
老规矩,算法一般来说与编程语言无关,因此这里依然用通俗易懂的伪码(Pseudocode):
gaps = [701, 301, 132, 57, 23, 10, 4, 1]foreach (gap in gaps){ for (i = gap; i < n; i += 1) { temp = a[i] for (j = i; j >= gap and a[j - gap] > temp; j -= gap) { a[j] = a[j - gap] } a[j] = temp }}
四. 复杂度
希尔排序的时间复杂度是多少呢? 不知道,我是认真的,确实不知道。 希尔排序使用的分组间隔如果很小,分出来的组就少,每个组内需要比较的元素就很多; 反之,如果间隔很大,每个组内需要比较的元素大幅减少,但是需要比较的组却大幅增加。 很容易想到,分组间隔需要找到一个均衡。 目前Sedgewick提出的(1, 5, 19, 41, 109,...)被认为是最好的分组间隔。 该序列的项来自:
![5ead2a274f2942f5955c147089db7ca6.png](https://i-blog.csdnimg.cn/blog_migrate/0a3dba5f3054e630d4225b9f75ab7331.jpeg)
![d4cf2214b78955734ceca8b65b7d859b.png](https://i-blog.csdnimg.cn/blog_migrate/8e667a38004823b50db5c85d42183979.jpeg)
使用此序列,在小数组中比快速排序和堆排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。 目前一般认为希尔排序的时间复杂度在 O( n^(1.3~2) ) 之间。 由于希尔排序的复杂度与排序分组间隔的选择直接相关,目前还是个未解决问题(Open problem)。
说到排序,一般还会提到排序的稳定性。 什么是稳定性呢? 简单的讲,就是相同数据之间的相对位置是否会多次变动,如果会多次变动,就是不稳定的,否则就是稳定的。 不难看出,希尔排序会使用不同的间隔多次排序,很可能多次移动同一数据,因此是不稳定的。
简单看一下插入排序和希尔排序的效率,以10000个随机数排序耗时为例:
![f3dbd30dc3f6a55effd17dfc50a17bff.png](https://i-blog.csdnimg.cn/blog_migrate/603256b5530c16b5348747eaa53213d4.jpeg)
耗时差了近200倍,这就是算法的魅力。 有兴趣后台回复 『 希尔排序 』获取源码。
五. 小结分析
希尔排序虽然不是速度最快的排序算法,但是希尔排序的应用极其广泛。原因主要有如下几点:
- 排序速度比较快,比插入排序等O( n² )的算法要好。
- 代码实现极其简单,且空间复杂度为O(1),排序过程仅需一个辅助存储变量。
- 虽然大部分情况下没有快速排序速度快,但是希尔排序在程序实现上不需要开栈操作,使用的空间极少,而快速排序则需要开辟大量的栈空间。