数据结构(Java)-排序算法-希尔排序

        希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序

基本思想

        希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

 代码思路与演示

一、交换式希尔排序

        1.设定步长,也就是最外层循环,每次循环gap减半

        2.设置外层循环,可假设为一个后向指针,执行 n-gap 次,向后遍历

        3.设置内层循环

                3.1 让指定元素与本组前面的元素做比较[ arr(i) vs arr(i-gap)],有逆序就交换

                3.2 这里类似一个前向指针!每次都要不断前向遍历直到本组的首位,像是冒泡排序

        4.当gap减到1,执行完前面步骤后,整个序列就是有序的了

  //交换式的希尔排序(相当于先分组然后使用冒泡,效率较低)
    public static Integer[] ShellSort1(Integer[] arr) {
        //每次排序步长减半(步长是几就相当于分了几组,n/stride是每组元素的个数)
        for (int stride = 50; stride > 0; stride /= 2) {
            //相当于指针,从前向后取元素,正好执行 n-stride 个大循环
            for (int i = stride; i < arr.length; i++) {
                //用于比较同一组的元素(两两比较),有逆序就交换,还要跟冒泡一样从后往前让它前面同组的所有相邻元素互相比较,从而实现每组都是有序的。
                for (int j = i - stride; j >= 0; j -= stride) {
                    if (arr[j] > arr[j + stride]) {
                        Integer temp = arr[j];
                        arr[j] = arr[j + stride];
                        arr[j + stride] = temp;
                    }
                }
            }
        }
        return arr;
    }

二、移位式希尔排序(感觉这个才是正统的)

        1.设定步长,也就是最外层循环,每次循环gap减半

        2.设置外层循环,可假设为一个后向指针,执行 n-gap 次,从前往后遍历

        3.设置内层循环

                3.1 这里要先设置两个辅助变量

                        1)保存待插值,就是 2 中指针指向的值   

                        2)待插索引,初始指向待插值前一位(注意有gap)

                3.2 让待插值与本组前面的元素分别做比较,有逆序就让该元素后挪一位(注意gap)!

                3.3 直到到达首位或者遇到比待插值小的元素,就插入到它的后面一位(同样注意gap)

        4.当gap减到1,执行完前面步骤后,整个序列就是有序的了

//移位式的希尔排序(相当于先分组然后使用插入排序,效率较高)
    public static Integer[] ShellSort2(Integer[] arr) {
        //每次排序步长减半(步长是几就相当于分了几组,n/stride是每组元素的个数)
        for (int stride = 50; stride > 0; stride /= 2) {
            //相当于指针,从前向后取元素,正好执行 n-stride 个大循环
            for (int i = stride; i < arr.length; i++) {
                Integer insertVal = arr[i];//取出待插值
                Integer insertIndex = i-stride;//待插位置
                    //如果某元素比本组的上一个元素小,就执行后移位操作重复向前遍历直到遇到比待插元素小的元素
                    while (insertIndex - stride >= 0 && insertVal < arr[insertIndex - stride]) {
                        arr[insertIndex] = arr[insertIndex - stride];
                        insertIndex -= stride;
                    }
                    //待插元素插入到该元素的后面一位(注意步长)
                    arr[insertIndex + stride] = insertVal;
            }
        }
        return arr;
    }

测试与速度对比

  @Test
    public void testShellSort() {
        Integer[] arr = init();
        System.out.println("原始数据为:");
        print(arr);
        Long beginTime = System.currentTimeMillis();
        Integer[] result = ShellSort1(arr);
        Long endTime = System.currentTimeMillis();
        System.out.println("交换式排序结果为:");
        print(result);
        System.out.println("耗时:" + (endTime - beginTime) + " ms");
        System.out.println();
        beginTime = System.currentTimeMillis();
        result=ShellSort2(arr);
        System.out.println("移位式排序结果为:");
        print(result);
        endTime = System.currentTimeMillis();
        System.out.println("耗时:" + (endTime - beginTime) + " ms");
    }

 

总结 

        上面提到的两个方法可以理解为 分组+冒泡分组+插入 。很明显第二种方法是更高效的,也是更符合算法描述的。这个算法理解上简单,但实际实现时却感觉无从下手,尤其是第一种方法的代码,能看懂就不容易,更不要说自己敲了!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值