希尔排序(Shell Sort)是插入排序的一种,其实质就是分组插入排序,该方法又称缩小增量排序,因D.L.Shell于1959年提出而得名。它是对直接插入排序的一种改进,通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使得数据项大跨度的移动。
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
假设当前增量h为4(间隔),对[9,-16,21,23,-30,-49,21,30,30]进行shell排序:
①[9,-16,21,23,-30,-49,21,30,30]—>[-30,-16,21,23,9,-49,21,30,30]
②[-30,-16,21,23,9,-49,21,30,30]—>[-30,-49,21,23,9,-16,21,30,30]
③[-30,-49,21,23,9,-16,21,30,30]—>[-30,-49,21,23,9,-16,21,30,30]
④[-30,-49,21,23,9,-16,21,30,30]—>[-30,-49,21,23,9,-16,21,30,30]
第一趟只需保证0,4,8上的数据有序,完成后整体右移一步,对1,5上的数据进行排序。。。这样的过程持续进行,直到所有的数据项都完成了以4为增量的排序。
接下来减少增量,直到完成以1为增量的排序。
从上面的分析可以看出,shell排序的关键在于增量序列的值,常用的增量序列由Knuth提出,该序列从1开始,由如下公式产生:h = h*3 + 1
程序中要反向计算增量序列:h = (h-1)/3
package com.liuhao.sort;
import java.util.Arrays;
public class ShellSort {
public static void shellSort(DataWrap[] data){
int arrayLength = data.length;
int h = 1;//保存可变增量
//按h * 3 + 1得到增量序列的最大值
while(h <= arrayLength/3){
h = h * 3 + 1;
}
while(h > 0){
System.out.println("====h的值:" + h + "====");
for(int i = h; i < arrayLength; i++){
DataWrap tmp = data[i];
if(data[i].compareTo(data[i - h]) < 0){
int j = i - h;
//整体后移h位
for(; j >= 0 && data[j].compareTo(tmp) > 0; j-=h){
data[j+h] = data[j];
}
data[j+h] = tmp;
}
System.out.println(Arrays.toString(data));
}
h = (h-1) / 3;
}
}
public static void main(String[] args) {
DataWrap[] data = {
new DataWrap(9, "")
,new DataWrap(-16, "")
,new DataWrap(21, "")
,new DataWrap(23, "*")
,new DataWrap(-30, "")
,new DataWrap(-49, "")
,new DataWrap(21, "")
,new DataWrap(30, "")
,new DataWrap(3, "")
,new DataWrap(67, "")
,new DataWrap(35, "")
,new DataWrap(5, "")
};
System.out.println("排序之前:" + Arrays.toString(data));
shellSort(data);
System.out.println("排序之后:" + Arrays.toString(data));
}
}
排序效果:
1.增量序列的选择
Shell排序的执行时间依赖于增量序列。
好的增量序列的共同特征:
① 最后一个增量必须为1;
② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在nl.25到1.6n1.25之间。
2.Shell排序的时间性能优于直接插入排序
希尔排序的时间性能优于直接插入排序的原因:
①当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
②当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。
③在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插人排序有较大的改进。
3.稳定性
希尔排序是不稳定的。参见上述实例,该例中两个相同关键字49在排序前后的相对次序发生了变化。