经典排序算法之希尔排序及其优化与改进

上一篇: 直接插入排序
下一篇: 快速排序

希尔排序是 基本插入排序 的升级优化版。一个叫希尔的人于1959年提出的一种排序算法。本值也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

希尔排序基本思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
大部分场景中,增量是可以提前定义好的,也可以动态生成。一般使用数组的长度除以2作为首次间隔增量,然后在除以2,直到间隔缩小的为1位置。

核心:确定增量划分序列分组,对不同分组的数据进行插入排序。实际上每次都是在拿当前位置与间隔增量的距离中进行比较

动图可以很好的看出希尔排序的过程
在这里插入图片描述
希尔排序的分组交换过程
在这里插入图片描述
1 分组间隔为5,数组被划分为五组数据–[9,4],[1,8] ,[2,6],[5,3],[7,5] 作为待排序序列
2 对五组子序列进行插入排序

  1. 从当前分组开始位置即i=grep=5的位置开始,后续每一个元素都与对应间隔为grep=5的元素进行比较,10个数据,则需要扫描5轮

  2. 如果当前的元素 小于分组里已经排好的序的元素(分组的最后一个),
    比如4 的元素小于9则需要交换

  3. 继续扫描分组里剩下元素如果是逆序,则进行交换,直到分组里元素都排好。

3 缩小增量 grep/2=5/2=2; 此时数据被分成2组每组5个----[4,2,5,8,5],[1,3,9,6,7]
重复步骤2,继续对此分组的2个子序列进行插入排序

4 缩小 增量为grep/2=2/2=1,此时数据被分成一组,存在10个数据,对其进行插入排序
5 缩小增量为0则停止循环,排序结束

时间复杂度
在这里插入图片描述

//希尔交换排序
    public static void shellSwapSort(int[] arr){
        long start=System.currentTimeMillis();
        int len= arr.length;

        for (int grep = len/2; grep>0; grep/=2) {//分组次数
            for (int i = grep;i<len;i++) {//分组位置的每一个元素都需要与间隔k位置进行比较,比如10个数若分成5组则每组为2个元素则arr[5]比较arr[0],arr[6]比较arr[1]

                if ( arr[i]<arr[i-grep]){//如果当前的元素 小于分组里已经排好的序的元素(分组的最后一个),例如:arr[5]<arr[0];arr[6]<arr[1]
                    int temp=arr[i];//当前需要处理的元素
                    //参考插入排序,当前元素与分组内所有元素进行比较
                    for (int j = i - grep; j >= 0&&temp<arr[j]; j -= grep) {//对每组内元素进行判断与交换,在分组内每遇到比当前元素大的都进行交换

                        arr[j] = arr[j] ^ arr[j + grep];
                        arr[j + grep] = arr[j] ^ arr[j + grep];
                        arr[j] = arr[j] ^ arr[j + grep];
                    }
                }
            }
        }
        long end= System.currentTimeMillis();
        System.out.println(Arrays.toString(arr));
        System.out.println("交换排序消耗时间:"+(end-start));

    }

优点:

  • 空间复杂度较好,O(1);作为改进版的插入排序,是一种相对高效的基于交换元素的排序方法。

缺点:

  • 不稳定,在交换的过程中,会改变元素的相对次序。
  • 希尔排序的时间复杂度依赖于增量序列函数,所以分析起来比较困难,当n在某个特定范围的时候,希尔排序的时间复杂都约为O(n1.3)

改进:在扫描分组数据使不必要每次都交换,找到最终的位置直接插入待处理元素即可

和插入排序基本相似,只不过此时的间隔为grep.插入排序的间隔是1罢了。

 //希尔插入排序
    public  static  void  shellInsertSort(int[] arr){
        int len = arr.length;
        long start=System.currentTimeMillis();
        for (int grep= len/2; grep >0 ; grep/=2) {
            for (int j = grep; j <len; j++) {
               if (arr[j]< arr[j-grep]) {//当前元素比分组的最后一个元素小
                   int temp = arr[j];//当前需要处理(插入)的元素
                   int k;
                   for (k=j-grep; k>=0&&temp<arr[k]; k-=grep) {//扫描分组数据在分组中找插入位置
                         arr[k+grep] = arr[k];
                   }
                    arr[k+grep] = temp;//插入元素
               }
            }
        }
        long end= System.currentTimeMillis();
        System.out.println(Arrays.toString(arr));
        System.out.println("希尔插入排序消耗时间:"+(end-start));
    }

同样8000个数据,效率还是不错的,比插入排序要高许多

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值