声明:
主要代码和部分算法说明参考自算法(第四版),这里将代码列出,是想和大家交流一些学习心得。
1.简介
希尔排序建立在插入排序的基础上,不同的是,希尔排序交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
2.思路
如果现在有个大小为16的整形数组:
Integer[] a = {16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
首先我们会根据数组长度算出一个合适的步长(用h表示)。
int h = 1; //给步长h一个初始值
while(h<N/3) h=3*h+1; //1,4,13...赋予h一个合适的步长
经过计算,可以得到长度为16的数组的步长是13。
然后进行排序,分别会使a[13]和a[13-13]有序(默认升序):
Integer[] a = {3,15,14,13,12,11,10,9,8,7,6,5,4,16,2,1};
a[14]和a[14-13]有序:
Integer[] a = {3,2,14,13,12,11,10,9,8,7,6,5,4,16,15,1};
a[15]和a[15-13]有序:
Integer[] a = {3,2,1,13,12,11,10,9,8,7,6,5,4,16,15,14};
上面得到的数组就是一个步长为13的有序数组。
接着我们继续减小步长:
h = h/3;
现在的h是4,按照之前的方法再继续进行步长为4的排序。
即对(a[4]和a[0])、(a[5]和a[1])、(a[6]和a[2])、(a[7]和a[3])、(a[8]和a[4],a[4]和a[0])、(a[9]和a[5],a[5]和a[1])……进行排序。
最后,当h减小到1时,则变成了一个插入排序,但是不同的是,此时的数组已经是基本有序的了,之前的数组会变成:
3 2 1 5 4 7 6 9 8 11 10 13 12 16 15 14
虽然不是绝对的有序,但是已经基本有序,而这时再进行插入排序,效率就会高很多了。
3.代码
排序代码(核心):
public class Shell {
//升序排序
public static void sort(Comparable[] a){
int N = a.length;
int h = 1; //给步长h一个初始值
while(h<N/3) h=3*h+1; //1,4,13...赋予h一个合适的步长
while(h>=1){ //h最小值为1,h=1时,变成插入排序
for(int i=h; i<N; i++){ //将数组变为h有序数组
//将a[i]插入到a[i-h],a[i-2h]a[i-3h]...之中
for(int j=i; j>=h && Example.less(a[j], a[j-h]); j-=h)
Example.exch(a, j, j-h);
}
h = h/3;
}
}
}
其余代码(测试):
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
//p153 排序算法类的模板
public class Example {
//对数组中的元素(实现了Comparable接口)进行排序
public static void sort(Comparable[] a){
// //选择排序
// Selection.sort(a);
// //插入排序
// Insertion.sort(a);
//希尔排序
Shell.sort(a);
}
//判断v是否小于w
public static boolean less(Comparable v,Comparable w){
//如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数
return v.compareTo(w) < 0;
}
//交换两个元素的位置
public static void exch(Comparable[] a, int i, int j){
Comparable t = a[i]; a[i] = a[j]; a[j] = t;
}
//打印数组
public static void show(Comparable[] a){
for(int i = 0; i<a.length; i++)
StdOut.print(a[i]+" ");
StdOut.println();
}
//判断数组a是否有序
public static boolean isSorted(Comparable[] a){
for(int i=1; i<a.length; i++){
if( less(a[i],a[i-1]) ) {
return false; //因为默认升序排列
}
//System.out.println("判断是否有序中:"+a[i-1]+"小于"+a[i]);
}
return true;
}
//测试
public static void main(String[] args) {
//@SuppressWarnings("deprecation")
//String[] a = In.readStrings();
Integer[] a = {16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
sort(a);
if(isSorted(a)) System.out.println("有序");
show(a);
}
}
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
注:
上面一定要建立Integer数组,而不是String数组,因为数字和字符串判断大小的依据不同。