希尔排序
public class Shell {
private Shell(){}
public static void sort(Comparable[] a){
int N = a.length;
int h = 1;
while (h<N/3) h =3*h + 1;
while (h>=1){
for(int i=h;i<N;i++){
for(int j=i;j>=h&&less(a[j],a[j-h]);j-=h)
exch(a,j,j-h);
}
h/=3;
}
}
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int vIndex,int wIndex){
Comparable temp = a[vIndex];
a[vIndex] = a[wIndex];
a[wIndex] = temp;
}
private static void show(Comparable[] a){
for(int i=0,len=a.length;i<len;i++)
StdOut.print(a[i]+" ");
StdOut.println();
}
private static boolean isSorted(Comparable[] a){
for(int i=1,len=a.length;i<len;i++)
if(less(a[i],a[i-1])) return false;
return true;
}
public static void main(String[] args){
String[] a = new In(args[0]).readAllStrings();
sort(a);
assert isSorted(a);
show(a);
}
}
实现策略
对大数组进行拆分变成较小的增量为h的数组,每个小的数组都是各自有序的,按照间隔递减的方式将小的元素尽量靠近左边,大的元素尽量靠近右边,交换方式采用插入排序方式,减少插入排序需要交换的次数。
分析
插入排序的问题:插入排序的性能问题主要体现在数组的规模和有序性问题。
希尔排序:希尔排序将插入排序的1增量改进为h增量,对间隔h的数组进行有序排列,优势在于减少了插入排序的交换次数(小的值都被移动到了靠近小值的一边,大的值都被移动到 了靠近大值的一遍)。原本需要一步一步移动的插入排序变为了大范围移动的插入排序。希尔排序的难点主要在于h的选择上,但是重要的是其运行时间肯定是小于平方级别的,对于一些简单 的中等排序,应该优先想到希尔排序。递增序列选用1 4 13…能满足大多数场景了,目前并没有找到一种很好的递增序列
如上图中的第一个S元素,本来是很大的元素,位置比较靠后,但是按照1 4 13…的递增序列使用希尔排序在第一次交换后就可将其放在它排序后所在位置,减少了最终增量为1的插入排序的交换次数。