初级排序算法4 Shell Sort希尔排序

图片来源于网络
希尔排序

        算法思想介绍: 

        希尔排序思想的精髓: 让数组中任意间隔为 h 的元素,都是有序的。这样的数组 我们叫它 h有序数组。 换句话说:一个h有序数组 就是h个相互独立的有序数组,编织在一起的一个数组。在进行排序的时候,如果h很大,我们就能将元素移动到很远的地方。由于每轮的 步长step逐渐缩小,希尔排序又叫为“缩小增量排序”。 
        shell 排序的比插入排序 更高效的原因是 : 它权衡了子数组的规模和有序性。

        算法代码实现:

辅助函数

    private static boolean less(Comparable a, Comparable b) {
        return a.compareTo(b) < 0;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    @Test
    public void shellSort0() {
        int[] a = {14, 13, 12 ,11, 10, 9, 8, 7,// 15/2 = 7 , 左边多一个元素。
                6, 5, 4, 3, 2, 1, 0};

        int length = a.length;
        System.out.println("数组长度为:"+length);
        int group = 2;
        // 增幅步长
        int step = 1;

        // 引入一个while循环,让h按照 递增序列 进行递减。
        while (step < length / group) {  // step < (15/2 即为 7); 除以2的话,步长 能循环到 step=7。
            step = group * step + 1;
            System.out.println("group * step + 1 公式计算ING... 计算中 step => " + step);
        }
            System.out.println("group * step + 1 最终的h值:" + step);

        while (step >= 1) {
            System.out.println("while 循环开始 , step ==> " + step);
            //将数组变为h有序。
            for (int i = step; i < length; i++) {  //通过i的递增,实现
                // ps 为什么  j -= step 写这个代码,因为插入排序是从后面向前面插入的。 将arr[i] 插入到 a[i-step],a[i-2h],a[i-3h]中。
                // ps  j>=step 为什么?  j-step 这个索引必须合法 >=0. 否则 a[j - step] 索引没有意义了。
                for (int j = i; j >= step; j -= step) {
                    System.out.println(i + " VS " + "[" + (j - step) + "<=" + j + "-" + step + "]");
                    if (less(a[j], a[j - step])) {
                        swap(a, j, j - step);
                    }
                }
            }
            //  由于每轮的 步长step逐渐缩小,希尔排序又叫为“缩小增量排序”。
            step = step / group;
        }

        System.out.println("\n What the final arr looks like 最终数组的模样: \n" + Arrays.toString(a));
    }

              关于最内层for循环,比较容易困惑的问题有:

  • 为什么 写 j -= h 这样的代码?因为利用的是插入排序思想,前面我们介绍过,插入排序是从后面向前面插入的。 将a[i] 插入到 a[i-h],a[i-2h],a[i-3h]中。
  •  j>=step 为什么?  因为 j-step 这个索引必须合法 >=0. 否则 a[j - step] 索引没有意义了。

另外一种实现,供参考:

   @Test
    public void shellSort() {

        int[] a = {6, 2, 4, 0, 7, 3, 8, 1, 9, 5};
        int n = a.length;
        //定义变量 :i和j控制循环; inc偏移增量; key插入的时候临时保存的变量。
        int i, j = 0, inc, key;
        int count = 0;

        //第一次 inc 为 长度的一半。 跑完了,再来一次对半。直到最后一次为1为止。 inc用于控制偏移的步长
        for (inc = n / 2; inc > 0; inc /= 2) { //分组,计算分组步长。
            //这里i 为什么等于 inc ,因为第一个元素本身就是有序的。
            for (i = inc; i < n; i++) { // 使得key a[i] 可以递增 到 (length-1)
                // ps 每一轮 采用插入排序     i开始为inc=7,然后依次取值 => 7,8,9,10,11,12,13,14
                // 待插入的元素 key 即为 a[i]
                key = a[i];   //a[7] 数据 作为 key 数据
                System.out.println(i + " VS " + (j - inc) + " ,次数" + (++count));
                for (j = i; j >= inc && key < a[j - inc]; j -= inc) { // j =7 开始,  7 >=7 && a[7] < a[7-7]
                    a[j] = a[j - inc]; // a[7]  = a[0]
                }

          //此时,key 找到归宿索引位置j,插入排序中用key变量来记录,应该是方便减少交换操作。
                a[j] = key;
            }
            System.out.println(Arrays.toString(a));
        }
        System.out.println("\n What the final a looks like 最终数组的模样: \n" + Arrays.toString(a));
    }

        个人感觉,代码实现,还是比之前的几个初级算法有更有一些实现难度的。唯有多练,勤能补拙。

算法分析 希尔排序的核心在于间隔序列的h设定和h之间的数学性质,比如公因子等,目前为止最好的间隔序列尚未有定论。当然你可以提前设定好间隔序列,或者可以动态的定义间隔序列。本文的第1种算法实现,你可以通过控制group变量来动态定义间隔序列的算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值