基本介绍
希尔排序是希尔于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进后的高效版本,也称为缩小增量排序。
基本思想
在排序算法之插入排序中插入排序很循规蹈矩,不管数组分布是怎么样,都会一步一步的对元素进行比较,移动,插入;假设这种序列[1,2,3,4,5,0],数组末端的0要回到首位置很是费劲,比较和移动元素均需n-1次。而希尔排序在数组中采用跳跃式分组的策略,通过某个增量将数组元素划分为若干组,然后分组进行插入排序,随后逐步缩小增量,继续按组进行插入排序操作,直至增量为1。希尔排序通过这种策略使得整个数组在初始阶段达到从宏观上看基本有序,小的基本在前,大的基本在后。然后缩小增量,到增量为1时,其实多数情况下只需微调即可,不会涉及过多的数据移动。
案例推导
我们先看个图解:
首先我们将初始数组根据数组长度/2的步长进行划分成5组[8,3], [9,5], [1,4],[7,6],[2,0];
我们对每一组进行插入排序,如[8,3]插入排序后就是[3,8];按照这种规则,第一轮希尔排序就变成了3,5,1,6,0,8,9,4,7,2;在按照数组长度/2/2的步长进行分组:[3,1,0,9,7],[5,6,8,4,2],对这两组进行插入排序,数组就变成了0,2,1,4,3,5,7,6,9,8;此时在缩小步长为数组长度/2/2/2,直到步长不大于0,最后将数组在进行简单插入排序就可以还得排好序的数组。
代码实现
分步实现代码:
int[] arr = new int[]{8,9,1,7,2,3,5,4,6,0};
//按照数组长度/2=10/2=5的步长进行分组
int temp;
for (int i = 5; i < arr.length; i++) {
//开始从下标为0开始,依次找下标为 0,5;1,6;2,7;3,8;4,9
for (int j = i - 5; j >= 0; j-=5) {
//如果发现当前下标的比下一个步长的下标的值大,则进行替换
if (arr[j] > arr[j+5]) {
temp = arr[j];
arr[j] = arr[j+5];
arr[j+5] = temp;
}
}
}
System.out.println("第一轮希尔排序结束:"+Arrays.toString(arr));
//第二轮步长为5/2=2
for (int i = 2; i < arr.length; i++) {
for (int j = i - 2; j >= 0; j -= 2) {
if (arr[j] > arr[j+2]) {
temp = arr[j];
arr[j] = arr[j+2];
arr[j+2] = temp;
}
}
}
System.out.println("第二轮希尔排序结束:"+Arrays.toString(arr));
//第三轮步长为2/2=1
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j -= 1) {
if (arr[j] > arr[j+1]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println("第三轮希尔排序结束:"+Arrays.toString(arr));
运行结果:
第一轮希尔排序结束:[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
第二轮希尔排序结束:[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
第三轮希尔排序结束:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
将代码合并整理为:
int[] arr = new int[]{8,9,1,7,2,3,5,4,6,0};
int temp,count = 0;
//将数组长度/2,只要步长大于0就循环
for (int gap = arr.length /2; gap > 0; gap/=2) {
for (int i = gap; i < arr.length; i++) {
//开始从下标为0开始,依次找下标为 0,5;1,6;2,7;3,8;4,9
for (int j = i - gap; j >= 0; j-=gap) {
//如果发现当前下标的比下一个步长的下标的值大,则进行替换
if (arr[j] > arr[j+gap]) {
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
System.out.println("第"+(++count)+"轮希尔排序结束:"+Arrays.toString(arr));
}
运行结果:
第1轮希尔排序结束:[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
第2轮希尔排序结束:[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
第3轮希尔排序结束:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
上面的希尔排序采用的交换法,我们可以使用排序算法之插入排序中的移位法来实现。
移位法代码实现如下:
int[] arr = new int[]{8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
int temp, count = 0;
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap, j; i < arr.length; i++) {
int insertVal = arr[i];
for (j = i - gap; j >= 0; j -= gap) {
if (insertVal < arr[j]) {
//移动数据
arr[j + gap] = arr[j];
} else {
break;
}
}
if (j + gap != i) {
arr[j + gap] = insertVal;
}
}
System.out.println("第" + (++count) + "轮希尔排序结束:" + Arrays.toString(arr));
}