基于插入排序的希尔排序-----------java实现

首先看一下之前使用简单插入排序存在的问题:

当存在一个数组,其最后一个数据为最小值,如:arr={5,6,16,34,33,2}

这样的话需要循环到最后一次,也就是第6次的时候,才可以将2排到前边,效率非常低

 

但是希尔排序可以很好的解决这个问题,其是简单插入排序改进后的一个效率较高的排序方式

,也叫缩小增量排序

 

复杂度:

平均时间: O(nlogn)

最差:  O(n²)

最优: O(n^1/3)

空间复杂度:O(1)

稳定性: 不稳定

比插入和选择排序快得多,且数组越大,优势越大

一、基本思想

希尔排序按某个增量分组,对每个分组使用直接插入排序实现,随着增量逐渐减小,每组数据逐渐增加,当增量减少至1时,仅剩下完整的一组,排序结束。

二、排序分析

图片引用自https://blog.csdn.net/qq_28081081/article/details/80598960

三、代码实现

/**
 * 希尔排序
 * @author MaoLin Wang
 * @date 2019/10/2817:43
 */
public class ShellSort {
    public static void main(String[] args) {

       int[] arr={214,32,11,2,3,2,66,33,54,12};

        shellSort(arr);

    
    }
    public static void shellSort(int[]arr){
        int temp=0;
        int count=0;
        for (int group =arr.length/2;group>0;group/=2){
            for (int i=group;i<arr.length;i++){
                for (int j=i-group;j>=0;j-=group){
                    if (arr[j]>arr[j+group]){
                        temp=arr[j];
                        arr[j]=arr[j+group];
                        arr[j+group]=temp;
                    }
                }
            }
            System.out.println("第"+(++count)+"次排序结果:");
            System.out.println(Arrays.toString(arr));
        }
    }
}

结果:

第1次排序结果:
[2, 32, 11, 2, 3, 214, 66, 33, 54, 12]
第2次排序结果:
[2, 2, 3, 12, 11, 32, 54, 33, 66, 214]
第3次排序结果:
[2, 2, 3, 11, 12, 32, 33, 54, 66, 214]

测试100000条数据耗时:

        int[] arr =new int[100000];
        for (int i=0;i<100000;i++){
            arr[i]=(int)(Math.random()*100000);
        }

        long begintime=System.currentTimeMillis();
        System.out.println("开始时间"+begintime);
        shellSort(arr);

        long endtime=System.currentTimeMillis();
        System.out.println("结束时间"+endtime);
        System.out.println("用时:"+(endtime-begintime)+"ms");

结果:

开始时间1572260920132
结束时间1572260930389
用时:10257ms

发现用了10000多ms,不但没有提高效率,反而低了非常多,这是为什么呢?

 

仔细看我们的代码进行交换的条件,发现只要满足arr[j]>arr[j+group]就要进行交换,无疑增加了系统开销

 

接下来对代码进行优化:

public static void shellSort2(int arr[]) {

        for (int group = arr.length / 2; group > 0; group /= 2) {
            for (int i = group; i < arr.length; i++) {
                int j = i;
                int temp = arr[j];
                if (arr[j] < arr[j - group]) {
                    while ((j - group) >= 0 && temp < arr[j - group]) {
                        /**
                         *  开始移动,将比arr[j]大的arr[j-group]、arr[j-group-group]......按顺序移动到后一个增量的位置
                         *   如 3, 2, 11, 21, 66, 32, 214, 4
                         *   在group减少到2,比较32和4的时候
                         *  1. 4<32  且满足while条件, 所以将32移动到4,temp=4,j=5,指向32的位置
                         *   此时的数据为:3, 2, 11, 21, 66, 32, 214, 32
                         *  2.继续while循环
                         *      j-group=3>0   temp=4<arr[3]=21,满足while条件
                         *      执行arr[j]=arr[j-group] -> 将21的位置移动到32,temp仍然为一开始的4,j=3,指向21的位置
                         *    此时数据为:3, 2, 11, 21, 66, 21, 214, 32
                         *  3.继续循环while
                         *      j-group=1>0  temp=4>arr[1]=2 不满足while条件,退出循环
                         *  4.此时j=3,将temp=4赋值给arr[j]
                         *     此时数据为:3, 2, 11, 4, 66, 21, 214, 32
                         */

                        arr[j] = arr[j - group];
                        j -= group;
                    }
                    //结束while循环后,将temp插入到arr[j]
                    arr[j] = temp;
                }
            }
        }
    }

或用for循环:

public static  void shellSort(int[] arr){
        int j;
        for (int gap = arr.length/2; gap >0 ; gap/=2) {
            for (int i = gap; i < arr.length; i++) {
                int temp=arr[i];

                for ( j = i; j -gap >=0&&temp<arr[j-gap] ; j-=gap) {
                    arr[j]=arr[j-gap];
                }
                arr[j]=temp;
            }

        }
    }

同样测试10000条数据排序的耗时:

开始时间1572264172662
结束时间1572264172692
用时:30ms

耗时从1w多ms减小到30ms,多次测试上下波动也不会超过20ms

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值