Java随笔-希尔排序

143 阅读3分钟

希尔排序

希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本,同时该算法是冲破O(n2)的第一批算法之一,是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

基本思想

算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。

增量

先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2...。一般的初次取序列的一半为增量,以后每次减半,直到增量为1。假设待排序数组array长度为10:49,38,65,97,76,13,27,49,55,04。 在这里插入图片描述 那么增量d = array.length / 2,取值依次为5,2,1。

增量d=5

增量为5,将待排序数组array分成5组子序列,分别是[14,13]、[38,27]、[65,49]、[97,55]、[76,04]。 在这里插入图片描述 子序列中使用插入排序。 在这里插入图片描述 排序后的结果: 在这里插入图片描述

增量d=2

增量为2,将d=5排序后的结果分成2组子序列,分别是[13,49,04,38,97]、[27,55,49,65,76]。缩小增量后进行插入排序。 在这里插入图片描述 排序后的结果: 在这里插入图片描述

增量d=1

当增量为1时,则希尔排序已进行至最后一步,元素之间依然使用插入排序。 在这里插入图片描述 排序结果:

![在这里插入图片描述](https://img-blog.csdnimg.cn/de663b3976764ff4af9cd0d72b4181ff.png

代码实现

最坏的情况:长度为100,0000的倒序整型数组为例进行排序。

希尔排序

  1. 初始化数据
    /**
     * 初始化数据
     * @param length    数组长度
     * @return 数据
     */
    private static int[] initData(int length) {
        int[] array = new int[1000000];
        for (int i = 1000000; i > 0; i--) {
            array[1000000 - i] = i;
        }
        return array;
    }
  1. 计算增量进行插入排序
    /**
     * 希尔排序
     */
    private static void shellSort() {
    	// 获取待排序数据
        int[] array = initData(1000000);
        // 希尔排序开始时间
        long startTime = System.currentTimeMillis();
        // 计算增量
        int gap = array.length;
        while (true) {
            // 增量每次减半
            gap /= 2;
            for (int i = 0; i < gap; i++) {
                // 插入排序
                for (int j = i + gap; j < array.length; j += gap) {
                    int k = j - gap;
                    // 若增量前元素大于增量后元素则交换位置
                    while (k >= 0 && array[k] > array[k + gap]) {
                        // 交换位置
                        int temp = array[k];
                        array[k] = array[k + gap];
                        array[k + gap] = temp;
                        // 开始增量下一个元素
                        k -= gap;
                    }
                }
            }
            if (gap == 1)
                break;
        }
        // 希尔排序结束时间
        long endTime = System.currentTimeMillis();
        System.out.println("希尔排序时间:" + (endTime - startTime) + "毫秒");
    }

插入排序

  1. 初始化数据。为了和希尔排序比较排序速度,排序数组保持一致。
  2. 插入排序。
    /**
     * 插入排序
     */
    private static void insertSort() {
        // 获取待排序数据
        int[] array = initData(1000000);
        // 插入排序开始时间
        long startTime = System.currentTimeMillis();
        int l = array.length;
        for (int i = 1; i < l; i++) {
            // 若前面元素大于后面元素则交互位置
            for (int j = i; j > 0 && (array[j] < array[j - 1]); j--) {
                int temp = array[j];
                array[j] = array[j - 1];
                array[j - 1] = temp;
            }
        }
        // 插入排序开始时间
        long endTime = System.currentTimeMillis();
        System.out.println("插入排序时间:" + (endTime - startTime) + "毫秒");        
    }

排序比较

在这里插入图片描述 由上图中排序时间可以看出:对长度为100,0000的int倒序数据进行升序(最坏情况),希尔排序时间远小于插入排序时间。

总结

  • 希尔排序是非稳定排序算法
  • 希尔排序是基于插入排序的一种算法。
  • 希尔排序最坏时间复杂度为O( n^3/2^ )
  • 希尔排序最后一个增量必须为1
  • 希尔排序的时间性能优于直接插入排序
  • 希尔排序在中等大小规模表现良好,对规模非常大的数据排序不是最优选择