1,原理及背景:
希尔排序算法是按其设计者希尔(Donald Shell)的名字命名,该算法由1959年公布,是插入排序的一种更高效的改进版本。它的做法不是每次一个元素挨一个元素的比较。而是初期选用大跨步(增量较大)间隔比较,使记录跳跃式接近它的排序位置;然后增量缩小;最后增量为 1 ,这样记录移动次数大大减少,提高了排序效率。希尔排序对增量序列的选择没有严格规定。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
2,算法思想:
- 基本思路:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2小于d1重复上述的分组和排序,直至所取的增量dt=1(dt 小于dt-l 小于… 小于 d2 小于 d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。
可视轨迹:
性能分析:
希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n^2),而Hibbard增量的希尔排序的时间复杂度为O(n^1.5),希尔排序时间复杂度的下界是n*log2n。希尔排序没有快速排序算法O(nlogn)快,因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择,但是比O(n^2)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法.
- 实例程序:
public class Shell {
//希尔排序:
public static void sort(Comparable[] a){
int n = a.length;
int h = 1;
while(h < n/3){
h = 3*h+1;//1, 4, 13, 40, 121, ...
}
while(h >= 1){
//将数组变为h有序(插入算法的变形):
for(int i=h; h<n; i++){
//将a[i]插入到a[i-h],a[i-2h],a[i-3h]...中
for(int j=i; j>=h; j-=h){
if(less(a[j], a[j-h])){
exch(a,j,j-h);
}
}
}
}
}
// v是否小于w ?
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
// 交换a[i]和a[j]
private static void exch(Object[] a, int i, int j) {
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}
// 打印数组到输出
private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
//测试函数
public static void main(String[] args) {
Comparable[] a = new Comparable[]
{'B','F','S','R','T','J','D','A','Z','V','Y','H','K','I','P','Q','C','G','N','U','M','E','O'};
System.out.print("排序前:");
show(a);
System.out.print("排序后:");
Selection.sort(a);
show(a);
}
}
[程序输出】:
排序前:B F S R T J D A Z V Y H K I P Q C G N U M E O
排序后:A B C D E F G H I J K M N O P Q R S T U V Y Z