希尔排序

希尔排序

前面我们说过了插入排序,它是三种基本排序中最常用的一种排序,具有排序稳定,空间复杂度低,而且在样本小且基本有序时效率比较高,该篇讲述的希尔排序是对插入排序的一种优化排序,在希尔排序开始阶段,通过增量的方式使排序的样本小化,在希尔排序的最后阶段蜕变成插入排序,但可以完美实现样本基本有序,从而使得对样本的排序相比于直接使用插入排序,在很大程度上得到优化。
希尔排序中存在一个关键因素——增量,通过增量的存在将原数组分解成多个子数组,然后在每个子数组中使用插入排序来使元素有序化,一轮排序过后,缩小增量值,按上述方式继续对重新得到的子数组进行排序,最后增量会变为1,此时就是对整个数组进行最后的一轮排序,最后得到的就是经过希尔排序之后的有序数组。
假定初始数组为:{2,5,7,1,3,4,6}

人为设置初始增量为4,那么得到的四个子数组为:{2,3}、{5,4}、{7,6}、{1}

每个子数组经过插入排序之后得到的数组为:{2,4,6,1,3,5,7}

将增量变为2,那么得到的两个子数组为:{2,6,3,7}、{4,1,5}

每个子数组经过插入排序之后得到的数组为:{2,1,3,4,6,5,7}

将增量变为1,进行最后一轮的插入排序得到的结果为:{1,2,3,4,5,6,7}

对应的算法代码为:

private static void shell(int[] arr) {
    int gap = 4;
    for (; gap >= 1; gap >>= 1) {
        for (int i = gap; i < arr.length; i++) {      //从gap处开始遍历,gap之前的值都是各数组第一个值,单值数组直接就是有序的
            for (int j = i; j > gap - 1; j -= gap) {  //每次都跳过gap长度与前一个值进行比较
                if (arr[j - gap] > arr[j]) {          //跳过gap长度的前一个值小于待插入值
                    Utils.swap(arr, j - gap, j);      //交换
                }
            }
        }
    }
}

希尔排序关键的一点就在于增量的选择,直接使用1,2,4,8,16...的方式并不能最大限度的提高希尔排序的效率,网上也有很多其他的增量序列可以选择,这里提供一种Knuth序列的实现方式

//Knuth序列
n=1
n=3*n+1
序列:1,4,13,40,121...

使用Knuth序列对应的希尔排序代码为:

private static void shell(int[] arr) {
    int gap = 1;
    while (gap < arr.length / 3) {
        gap = gap * 3 + 1;                  //按照Knuth序列增大增量值
    }
    for (; gap >= 1; gap = (gap - 1) / 3) { //按照Knuth序列减小增量值
        for (int i = gap; i < arr.length; i++) {
            for (int j = i; j > gap - 1; j -= gap) {
                if (arr[j - gap] > arr[j]) {
                    Utils.swap(arr, j - gap, j);
                }
            }
        }
    }
}

测试排序结果

通过对数器的方式进行排序结果的验证

//测试代码
public static void main(String[] args) {
    int size = 15;
    int[] arr1 = Utils.generateRandomArray(size, 100);
    int[] arr2 = new int[size];
    System.arraycopy(arr1, 0, arr2, 0, size);
    System.out.println("原数组:\t\t\t" + Arrays.toString(arr1));
    Arrays.sort(arr1);
    System.out.println("系统方法排序后:\t" + Arrays.toString(arr1));
    shell(arr2);
    System.out.println("希尔方法排序后:\t" + Arrays.toString(arr2));
}
原数组:		[70, 49, 88, 24, 51, 36, 33, 17, 55, 16, 13, 79, 96, 75, 0]
系统方法排序后:	[0, 13, 16, 17, 24, 33, 36, 49, 51, 55, 70, 75, 79, 88, 96]
希尔方法排序后:	[0, 13, 16, 17, 24, 33, 36, 49, 51, 55, 70, 75, 79, 88, 96]

如果对你有帮助,点个赞,或者打个赏吧,嘿嘿
整理不易,请尊重博主的劳动成果

posted @ 2020-07-06 18:22  Mango_SF  阅读(174)  评论(0编辑  收藏  举报