一.概述
希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
在学习插入排序的时候,倒序将待排序的第一个数和前面已排序的数进行比较再往前插时,必须依次往前,效率较低。故可通过增加步长h来提高效率。
二.原理
1.选定一个增长量(步长)h,按照增长量h对数据进行分组;
2.对分好组的每一组数据为一轮,每一轮内进行插入排序;
3.按照规则减少增长量h,最后减为1,重复第二步操作
如图,假设输入数组为{9,1,2,5,7,4,8,6,3,5} , 按照插入排序的思想,将数组分为已排序和未排序两部分。在插入排序中,当数组为初始状态时,将索引0处的数视为已排序,索引1到n-1为未排序部分。而在希尔排序中,索引为0的数依旧视为已排序,而由于未排序数字倒序向已排序数字进行比较时存在一个增长量(步长)h,所以初始状态时,索引h的数字为待排序数字。
第一轮:为了方便演示,令h的第一个值为5,即索引=5为最初的待排序数字也就是“4”。将4和与其距离h的已排序数比较,此时已排序数字为索引0处的“9” ,4<9,所以不进行插入。由于待排序数字每次倒序遍历时,要和已排序数字的距离为h的倍数,所以此时“4”只能和“9”比较。
第一个待排序数“4”比较结束,使用第二个待排序数字“8”向前遍历,数字“8”的索引为6,与其比较的数的索引为6-5=1,即数字“1”。1<8,不将8进行插入…同理进行后序排列。
第二轮,h为2,第一个待排序数字为索引=h=2的数,即“2”,2与索引为0的数“4”比较,2<4,所以2和4交换。…后面的同理
三.java代码实现
public class shell {
public static void sort(int[] a) {
int h = a.length / 2; //令初始h为数组长度的一半
while (h >= 1) { //每一个不同的h 各视为一次完整的插入排序!
for (int i = h; i < a.length; i++) { //第一轮为待排序第一个数字的范围,索引0视为已排序,所以从h开始
for (int j = i; j >=h; j = j - h) { // j移动的步长为h,由普通插入排序的j--,变成j=j-h
if(a[j-h]>a[j]){
swap(a,j-1,j);
}else{
break;
}
}
}
h=h/2;
}
}
private static void swap(int[] a ,int i,int j){
int x=a[i];
a[i]=a[j];
a[j]=x;
}
public static void main(String[] args) {
int[] a = {4, 7, 6, 9, 2, 1};
sort(a);
System.out.println(Arrays.toString(a));
}
}
测试结果:[1, 2, 4, 6, 7, 9]
需要注意的地方:
- 每一个不同的h 各视为一次完整的插入排序!
- 和基本的插入排序一样,外层循环i即待排序数,待排序数依次往后至最后一个数,所以i<a.length
- for内层循环时比较的是待排序数a[j-h]和a[j],所以j=i。每次跨越的步长为h,所以j=j-h。