希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。html
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。git
希尔排序实质上是一种分组插入方法。它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分红若干个组子序列,全部距离为gap的倍数的记录放在同一个组中;而后,对各组内的元素进行直接插入排序。 这一趟排序完成以后,每个组的元素都是有序的。而后减少gap的值,并重复执行上述的分组和排序。重复这样的操做,当gap=1时,整个数列就是有序的。github
如今有一个序列:{h1,h2,… hn}:算法
1. 设先取定一个小于n的整数 di 做为一个增量,全部间隔为 di 的记录放在同一个子序列,而后在每一个子序列内进行直接插入排序。shell
2. 而后取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量dt = 1 (d1>d2> … >dt-1>dt),即全部记录放在同一组中进行直接插入排序为止。数据结构
增量 di 的值是随便取的,可是这并不表明增量 di 的值能够随便取。也就是说,在定义增量 di 时,定义增量的序列为:dn>dn−1>...>d1=1,通常使用Shell建议的序列:di=n/2。post
下面以数列{80,30,60,40,20,10,50,70}为例,演示它的希尔排序过程。性能
第1趟:(gap=4)学习
当gap=4时,意味着将数列分为4个组: {80,20},{30,10},{60,50},{40,70}。 对应数列: {80,30,60,40,20,10,50,70}
对这4个组分别进行排序,排序结果: {20,80},{10,30},{50,60},{40,70}。 对应数列: {20,10,50,40,80,30,60,70}url
第2趟:(gap=2)
当gap=2时,意味着将数列分为2个组:{20,50,80,60}, {10,40,30,70}。 对应数列: {20,10,50,40,80,30,60,70}
注意:{20,50,80,60}实际上有两个有序的数列{20,80}和{50,60}组成。
{10,40,30,70}实际上有两个有序的数列{10,30}和{40,70}组成。
对这2个组分别进行排序,排序结果:{20,50,60,80}, {10,30,40,70}。 对应数列: {20,10,50,30,60,40,80,70}
第3趟:(gap=1)
当gap=1时,意味着将数列分为1个组:{20,10,50,30,60,40,80,70}
注意:{20,10,50,30,60,40,80,70}实际上有两个有序的数列{20,50,60,80}和{10,30,40,70}组成。
对这1个组分别进行排序,排序结果:{10,20,30,40,50,60,70,80}
希尔排序是按照不一样步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,因此插入排序的元素个数不多,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。
希尔排序的时间复杂度与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为O(N²),而Hibbard增量的希尔排序的时间复杂度为O(N3/2)。
希尔排序时间复杂度的下界是O(n*log2n)。
希尔排序过程当中用到了直接插入排序,须要临时变量存储待排序元素,所以空间复杂度为O(1)。
希尔排序是不稳定的算法,对于相同的两个数,可能因为分在不一样的组中而致使它们的顺序发生变化。
不能归位,好比最后一个数为最小值,那么全部的值都未在最终的位置。
那么希尔排序算法为何比直接插入排序好呢?
假设如今要对10个元素进行排序。
若是使用直接插入排序,大约花费的时间为 =10^2=100。
若是使用希尔排序,当增量di = 5时,分为5组,时间为5×2^2=20。当增量di = 2时,分为2组,时间为2×5^2=50。当di = 1时,分为1组,此时几乎是有序的,时间约为10,而后把每一个分组的时间都加起来,总的时间约为20+50+10=80。
public int[] shellSort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int gap = arr.length/2; while (gap > 0) { for (int i = gap; i < arr.length; i++) { int tmp = arr[i]; int j = i - gap; while (j >= 0 && arr[j] > tmp) { arr[j + gap] = arr[j]; j -= gap; } arr[j + gap] = tmp; } gap = (int) Math.floor(gap / 2); } return arr; }