Loading

16-希尔排序

1. 引入

插入排序存在的问题: 现在有这么一个数组,arr={2,3,4,5,6,1};现在需要插入的数 1 (最小),过程是:

{2,3,4,5,6,6} → {2,3,4,5,5,6} → {2,3,4,4,5,6} → {2,3,3,4,5,6} → {2,2,3,4,5,6} → {1,2,3,4,5,6}

[结论] 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响。

2. 概述

  • 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序
  • 基本思想
    • 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序
    • 随着增量逐渐减少,每组包含的关键词越来越多
    • 增量减至1时,整个文件恰被分成一组,算法便终止
  • 较之于〈插入排序〉效率高的原因:
    • 在间隔大的时候,挪动的次数少
    • 在间隔小的时候,挪动的距离短

3. 标配

  • 平均时间复杂度:O(n^1.3)
  • 最坏时间复杂度:O(n^2)
  • 最好时间复杂度:O(n)
  • 空间复杂度:O(1),内排序
  • 不稳定 // 比方说有俩 1,排之前 ..., a1, ..., b1, ... 排之后可能会出现 b1, a1, ...

4. 举例

  • 希尔排序的思想是使数组中任意间隔为 h 的元素都是有序的。这样的数组被称为h 有序数组。换句话说,一个h 有序数组就是h 个互相独立的有序数组编织在一起组成的一个数组
  • 在进行排序时,如果 h 很大,我们就能将元素移动到很远的地方,为实现更小的 h 有序创造方便。用这种方式,对于任意以 1 结尾的 h 序列,我们都能将数组排序 → 这就是希尔排序。

5. 代码实现

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {9, 6, 11, 3, 5, 12, 8, 7, 10, 15, 14, 4, 1, 13, 2};
        print(arr);
        sortBySwap(arr);
        print(arr);
    }

    public static void sortBySwap(int[] arr) {
        // 比每次按半劈效率高
        // Knuth 序列: h = 1, h = 3 * h - 1
        int h = 1;
        // 退出时 h 等于 arr.length 分割时的最大间隔
        while (h <= arr.length / 3) h = h * 3 + 1;
        /*
         * i = gap;
         * - 相隔 gap 个的元素构成一个数组,对这些 [子数组(h数组)] 进行插入排序
         * - 回忆插入排序的思想,外层 for 循环为啥 i 上来就是 1?
         *     把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只
         *     包含 1 个元素,无序表中包含有 n-1 个元素 (这其中第一个元素在原始
         *     数组中的索引不就是 1 吗),排序过程中每次从无序表中取出第一个元素,
         *     把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的
         *     适当位置,使之成为新的有序表。
         * - Shell = 插排思想 + [h数组]
         *     → i 初始化为 [h数组] 中的无序表的第 1 个元素,也就是 i = gap。
         * -----------------------------------------------------------------
         * i++;
         * - i+=gap 只能实现第 1 个子数组有序,应该把间隔为 gap 的子数组都排序
         * - 多个 [h数组] 编制在一起构成原始数组,i++ 直到 length-1,则保证了对
         *     每个 [h数组] 的元素都进行了插入排序
         * - 外层的一个循环周期(gap次)之后,就会使每个 [h数组] 中的有序表元素数+1
         * -----------------------------------------------------------------
         * j > gap-1
         * - 因为条件的右半部分是 a[j] < a[j-gap]
         *     如果条件左边写的是 j > 0,再配合着 a[j-gap] 可不就数组下标越界了吗?
         * - 又因为这是 [h数组], 每个 [h数组] 的第1个元素:0, 1, ... , gap-1
         *     故结合 a[j-gap],j 最小应等于 gap -> a[j-gap] = a[0]
         */
        for (int gap = h; gap > 0; gap = (gap-1)/3)
            for (int i = gap; i < arr.length; i++)
                for (int j = i; j > gap-1 && arr[j] < arr[j-gap]; j -= gap)
                    swap(arr, j, j-gap);
    }

    public static void sortByMove(int[] arr) {
        int insertIndex, insertValue;
        for (int gap = arr.length >> 1; gap > 0; gap/=2) {
            for (int i = gap; i < arr.length; i++) {
                insertIndex = i;
                insertValue = arr[insertIndex];
                while (insertIndex > gap-1 && insertValue < arr[insertIndex-gap]) {
                    arr[insertIndex] = arr[insertIndex-gap];
                    insertIndex -= gap;
                }
                arr[insertIndex] = insertValue;
            }
        }
    }

    public static void print(int[] arr) {
        for (int i = 0; i < arr.length; i++)
            System.out.print(arr[i] + " ");
        System.out.println();
    }

    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

6. sortBySwap全过程

posted @ 2020-02-18 19:33  tree6x7  阅读(229)  评论(0编辑  收藏  举报