step1:希尔排序是什么?
希尔排序是插入排序的优化版本,并不稳定。重点是通过设置间隔h来进行多次插入排序排序,直至最后h = 1 时排序完毕
思想,对于数据量比较大的数组,直接从间隔h = 1进行排序会产生元素位置多次转换,而在位置交换这一操作是比较消耗资源的。所以通过先部分有序的方式,减少元素的位置交换次数,以求达到提升排序的效率,这就是希尔排序相对于插入排序的优点
举例:数组:9,1,2,5,7,4,8,6,3,5,要求数组从小到大排序
插入排序的方法:对于9而言,它一开始位于数组最前端,需要将其置于数组的最后,此时通过插入排序,需要以间隔为1的距离慢慢挪动数组至数组末端,进行9次交换位置
对比以h = n/2为间隔的希尔排序。同样的数组与要求,将9置于数组最后端,只需要经过3次交换位置的操作
所以希尔排序产生意义。
增量h = 3*h + 1;效率更高
代码如下:
package datastructures.shell;
/**
* 完成希尔排序
* h的取值为n/2
*/
public class Shell {
public static void main(String[] args) {
//提供一个未排序的数组
// int[] a = {99,5,69,33,56,13,22,55,77,48,2,69,99};
int[] a = {35,33,42,10,14,19,27,44};
int[] ints = shellSort(a);
for (int elems: ints
) {
System.out.print(elems+" ");
}
}
public static int[] shellSort(int[] a){
int N = a.length;
//获取h
int h = 1;
while (h < N/3) {
h = 3*h+1;
}
//进入循环
while (h>0){
for (int i = 0; i < h; i++) {//这里的循环意义在于分组,获取每个组的第一个元素
for (int j = i + h; j < N; j += h ){//通过上一个循环获取到的分组的第一个元素,以此为根进行h间隔的操作,从这个循环开始其实就是插入排序,这个循环的实际意义在于遍历小组内的未排序元素,一开始给j赋值为i+h的意义在于保证i+h这个元素存在,不然直接使用j = i,结果下面循环对比a[j]和它右边的这个元素的时候,可能会出现a[j]是该小组的最后一个元素,造成空指针异常
int k = j - h;
while ( k >= 0 && a[k] > a[k + h]){//这个循环是进行对比,将未排序边界的与其下一个元素进行比较大小,然后获取比较小的元素,再将其通过两两对比的方法,插入已排序好的元素组中
int temp = a[k];
a[k] = a[k + h];
a[k + h] = temp;
k -= h;
}
}
}
h = (h -1)/3;
if(h < 1){
break;
}
}
return a;
}
}
我此前未能较好的理解希尔排序的原因,是因为我把选择排序和插入排序混淆了,导致在希尔排序的插入排序部分代码未能理解,如今根据代码理解运行过程后再去查看插入排序和选择排序的动态图,就能发现我错误的原因了。
在此复习一下选择排序与插入排序:
选择排序:在未排序的数组部分,查找到最小的元素,与未排序的前边界元素进行位置交换,所以每确定一个元素的位置,只进行了一次位置交换操作,时间复杂度为n*(n+1)/2---->O(n^2)
插入排序:在未排序的数组部分,前边界的第一个元素与第二个元素对比大小,然后小的元素放在前边界第一个位置,再把该元素与其左边第一个元素进行同样的操作进行对比大小判断是否需要交换位置,重复此操作直至遍历完已排序好的数组