希尔排序定义
希尔排序其实就是在插入排序的基础上改进而来,不过由于改进的效率特别明显,而且希尔排序本身也算是一个研究的课题了,拥有各种各样的优化方式,各位大牛也写了很多论文来验证希尔排序的不同优化方式, 其中讨论的最多的就是步长。
在讲到插入排序时, 我们是比较相邻的两个元素,而希尔排序,是比较相隔n个间距的两个数, n的大小就是步长。
首先看一下代码实现,然后再具体讲解。
代码实现
public static void shellSort(int[] arr) {
int h = 1;//保证步长大于1
int len = arr.length;
while(h < (len/3)) {//保证步长不超过总长度三分之一
h = h*3 +1;
}
while(h >= 1) {
for(int i=h; i<len; i++) {
int j = i;
int temp = arr[i];
for(; j >= h && arr[j - h] > temp; j=j-h) {
arr[j] = arr[j-h];//将较大数往后移h位
}
arr[j] = temp;//将数插入大小合适的位置
}
//每进行一次排序后,减少h的大小
h = (h - 1) / 3;//对应上面的h = h*3 +1;
}
System.out.println("shellSort: "+Arrays.toString(arr));
}
可以看到,代码思想基本和插入排序保持一致, 又多了一些东西。首先就是步长,在将插入排序时,我们提到, 如果数据越是趋向于有序,则排序速度回越快。而加入步长以后,可以快速的把大的数往后移动,小的数向前移动,使整体数据越来越趋向于有序, 从而使排序效率得到极大的提升。
如果是完全无序的数列,使用希尔排序的时间复杂度和步长是相关的。在上述代码示例中,我们使用了3h+1这样的增量序列,即{1,3,7,…,3n+1},这个比较常用。除此以外, 还有几种增量序列:
-
{1,2,4,8,…}这种序列并不是很好的增量序列,使用这个增量序列的时间复杂度(最坏情形)是O(n^2)
-
Hibbard提出了另一个增量序列{1,3,7,…,2k-1},这种序列的时间复杂度(最坏情形)为O(n1.5)
-
Sedgewick提出了几种增量序列,其最坏情形运行时间为O(n^1.3),其中最好的一个序列是{1,5,19,41,109,…}
使用增量排序都是根据数据的长度,选取增量序列中可使用的最大的数, 先进行一次大范围的数据移动,使大的数尽可能的往后移动,然后不断的减少步长,直到最后步长为1,进行一次插入排序。
每进行一次希尔排序,整个数据就越有序。
总结
关于希尔排序,先了解这么多,希尔排序的具体使用和具体优化,有很多论文进行阐述和证明,本文只是一个简单的介绍和了解。先了解基本使用再说吧。
附上自动生成测试数组的代码
public static int[] randomArray(int length) {
int[] arr = new int[length];
Random random = new Random();
for(int i=0; i<length; i++) {
arr[i] = random.nextInt(100);
}
//自动生成随机数组,先进行一次原始数据打印
System.out.println(Arrays.toString(arr));
return arr;
}