插入排序(直接、折半、希尔、java)

一.概念

基本操作:

插入位置:a[i]即将要插入的位置j,有可能是中间、开始、结尾。

插入排序种类:按寻找插入位置划分

二.直接插入排序

代码1:

两层循环,第一层遍历所有元素将其排序,第二层寻找插入位置和移动元素

    /**
     * 普通插入,  问题每次循环需要对比两个条件 j>0 x<a[j]
     */
    public static void normal(){
        int[] a=new int[]{9,7,8,6,5,4,3,2,1};
        int x;
        int j;
        for (int i=1;i<a.length;i++){
            x=a[i];
            //边寻找插入位置,边将有序数组后移
            for (j=i-1;j>=0&&x<a[j];j--){
                a[j+1] = a[j];
            }
            //j+1为应该插入的位置,循环退出时 j-1
            a[j+1] = x ;
        }

        for (int i=0;i<a.length;i++){
            System.out.print(a[i]);
        }
    }

代码2:哨兵

    /**
     * 哨兵直接插入,  每次循环对比一个条件
     */
    public static void sentinel(){
        //数组的第一个元素做哨兵,不参与排序
        int[] a=new int[]{0,9,7,8,6,5,4,3,2,1};
        int j;
        for (int i=2;i<a.length;i++){
            //如果当前遍历符合顺序,就不需要再往前比对
            if (a[i]>a[i-1])
                continue;
            a[0] = a[i];
            for (j=i-1;a[0]<a[j];j--){
                a[j+1] = a[j];
            }
            a[j+1] = a[0];
        }

        for (int i=1;i<a.length;i++){
            System.out.print(a[i]);
        }
    }

效率分析

最好情况,数组本身有序,只需要逐次对比,不需要移动。

最坏的情况,数组本身逆序, 则第2个元素需要对比1次,第3个对比2次,第4个对比3次,对比总数为 1+2+...(n-1)。

每次对比,都要把前面元素往后移动,并且哨兵每次都移动。

三.折半插入

直接插入在寻找插入位置时,是将前面的有序数组从后往前遍历,折半插入就是用二分法寻找插入位置。找到插入位置后,就插入并后移数组,和直接插入操作一样。

    private static void half2() {
//        int[] a = new int[]{0, 1, 2, 3};
        int[] a = new int[]{0, 9, 7, 8, 6, 5, 4, 3, 2, 1};

        for (int i = 2; i < a.length; i++) {
            a[0] = a[i];//赋值哨兵
            int low = 1;
            int high = i - 1;//有序队列的尾结点索引
            while (low < high) {
                int mid = (low + high) / 2;
                if (a[0] < a[mid]) {
                    if (mid > 1) {   //mid有时会出现1 的情况,比如 low=1 high=2,如果不判断会导致 high=0,把哨兵也排序
                        high = mid - 1;
                    } else {
                        high = 1;
                    }
                } else {
                    low = mid + 1;
                }
            }

            //high就是插入位置,为什么是high? 不管最初有多少元素,折半查找最后只会剩下两个相邻的元素,如 3 4,此时mid等于较小的那个数,
            //如果a[mid]<a[0], high = 4,low =4,high就是插入位置
            //如果a[mid] >=a[0],high = 3,low=3,high也是插入位置,最终high 和low会相等,都是插入位置

            if (a[0] < a[high]) {  //如果原数组本身顺序,如{0, 1, 2, 3},会变成 2 1 3 ,再判断一次是否需要排序
                for (int j = i; j > high; j--) {
                    a[j] = a[j - 1];
                }
                a[high] = a[0];
            }
        }

        for (int k = 1; k < a.length; k++) {
            System.out.print(a[k]);
        }
    }

四.希尔排序

希尔排序由直接插入排序思想而来:直接插入,在原数组基本有序时,效率高,在待排序的元素少时,效率高。希尔排序想比较一次,可以移动一大步

    private static void studyShell(){
        int arr[] = new int[]{1,8,6,9,9,4,7,3,8,9,3,2,7};

        //step不管最初多少,最后都会是1,保证都会排一遍
        for (int step=arr.length/2;step>0;step/=2){
            //首先记住,插入排序 是将一个数 在一个有序数组中,找到合适位置插入, 先有有序数组 再去插入
            //假设step =3 执性i=3时还没有有序数组,但执行i=6时,  0 和 3是有序的, 这个有序数组是逐渐得到的,第一个step还没有有序数组,第二个就有了,再往后就是正常插入排序
            //i=3 6 9会将同组排序,i递增,i= 4 7 10也会将同组排序,这样就按照希尔分组都排了
            for(int i=step;i<arr.length;i++){
                int x = arr[i];
                int j = i-step;
                while (j>=0&&x<arr[j]){
                    arr[j+step] = arr[j];
                    j-=step;
                }
                arr[j+step] = x;
            }
        }

        for (int i=0;i<arr.length;i++){
            System.out.print(arr[i]);
        }
    }

不宜在链式存储,无法直接根据索引获取值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值