希尔排序(Shell's Sort)是由 Donald Shell于1959年提出,它的思想的基础是 插入排序。
在讲解希尔排序之前我们先理解一下什么是逆序对:对于下标i<j ,如果A[i]>A[j] ,则称(i,j)是一对 逆序对(inversion)。
问题:序列{34, 8, 64, 51, 32, 21}中有多少个逆序对?
共有 :(34, 8) (34, 32) (34, 21) (64, 51) (64, 32) (64, 21) (51, 32) (51, 21) (32, 21)
交换2 个相邻元素正好消去1 个逆序对!这是不是很像插入排序?
插入排序:T(N, I) = O( N+I ) -------> 如果序列 基本有序 ,则插入排序简单且高效。
定理:任意N 个不同元素组成的序列平均具有N ( N - 1 ) / 4 个逆序对。
定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为 Ω( N 2 ) 。
我们能不能每次交换相隔较远的2 个元素?
要提高算法效率,我们必须每次消去不止1 个逆序对!
要达到预想的结果要先定义一个增量序列,根据增量序列将数据分组,在各组内进行插入排序,然后再取第二个增量,再进行分组排序,直到最后增量等于1,进行直接插入排序。
定义增量序列 :
我们来看看原始的希尔排序的代码实现:
原始希尔排序的增量序列定义:
public static int[] shellSort(int[]arr){
int len = arr.length; //获取数组的长度
for(int d=len/2;d>0;d/=2){ //定义增量序列 **shell排序中最核心
for(int i =d;i<len;i++){ //以下为简单选择排序
for(int j = i;j>0;j--){
if(arr[j]<arr[j-1]){
int temp= arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
}
return arr;
}
在这种增量序列下,在最坏情况 希尔排序的时间复杂度度是O(n2)的,但是上文不是说希尔排序能提高算法的效率吗?
因为问题就出在增量序列的定义上,在下面这种情况下,希尔排序并没有提高效率,最终本质还是一个插入排序:
增量元素不互质,则小增量可能根本不起作用。
我们思考有没有更好的增量序列定义?
Hibbard 增量序列
Sedgewick 增量序列
这两个增量序列可以将希尔排序的算法时间复杂度优化到O(n 4/3),关于这两个增量序列在此不再介绍了。
希尔排序并不是一个稳定的排序算法!相等的元素有可能会被交换!
参考资料:陈越《数据结构与算法》