常见排序算法之希尔排序

希尔排序

原文地址:原文

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。

原理:

在插入排序中,当需要插入的数较小时(从小到大排序),后移的次数将显著的增加,从而对效率产生影响。

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

各组内的排序通常采用直接插入法。由于开始时s的取值较大,每组内记录数较少,所以排序比较快。随着不断增大,每组内的记录数逐步增多,但由于已经按排好序,因此排序速度也比较快。

图解:

{%asset_img 希尔排序交换法.png%}

代码实现:(分析代码)

public class ShellSort {


    public static void main(String[] args) {

        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        System.out.println("排序前:"+Arrays.toString(arr));
        shellSort(arr);
        System.out.println("排序后:"+Arrays.toString(arr));
    }


    private static void shellSort(int[] arr) {


        /**
         * 分析过程
         * 一次循环
         */
        //第一次排序
        int tmp = 0;
        for (int i = 5; i < arr.length; i++) {
            //分组交换,i-5 指向第一个位置,
            for (int j = i - 5; j >= 0; j -= 5) {
                //j+5  指向第6个位置进行比较,因为分为5组
                if (arr[j] > arr[j + 5]) {
                    tmp = arr[j];
                    arr[j] = arr[j + 5];
                    arr[j + 5] = tmp;
                }

            }
        }
        System.out.println("第1次分组:" + Arrays.toString(arr));
        //第二次排序
        for (int i = 2; i < arr.length; i++) {
            //分组交换,i-5 指向第一个位置,
            for (int j = i - 2; j >= 0; j -= 2) {
                //j+5  指向第6个位置进行比较,因为分为5组
                if (arr[j] > arr[j + 2]) {
                    tmp = arr[j];
                    arr[j] = arr[j + 2];
                    arr[j + 2] = tmp;
                }

            }
        }
        System.out.println("第2次分组:" + Arrays.toString(arr));
        //第二次排序
        for (int i = 1; i < arr.length; i++) {
            //分组交换,i-5 指向第一个位置,
            for (int j = i - 1; j >= 0; j -= 1) {
                //j+5  指向第6个位置进行比较,因为分为5组
                if (arr[j] > arr[j + 1]) {
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }

            }
        }
        System.out.println("第3次分组:" + Arrays.toString(arr));


    }


}

最终代码:(交换法)

public class ShellSort {


    public static void main(String[] args) {

        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        System.out.println("排序前:" + Arrays.toString(arr));
        shellSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    private static void shellSort(int[] arr) {

        int tmp = 0;
        for (int group = arr.length / 2; group > 0; group /= 2) {
            for (int i = group; i < arr.length; i++) {
                //分组交换,i-5 指向第一个位置,
                for (int j = i - group; j >= 0; j -= group) {
                    //j+5  指向第6个位置进行比较,因为分为5组
                    if (arr[j] > arr[j + group]) {
                        tmp = arr[j];
                        arr[j] = arr[j + group];
                        arr[j + group] = tmp;
                    }

                }
            }
        }

    }


}

排序前:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

测试时间:

public class ShellSort {

    /**
     * 测试时间
     */
    @Test
    public void testTime() {
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
        }
        System.out.println("排序前:" + Arrays.toString(arr));
        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的时间是=" + date1Str);
        shellSort(arr);
        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序前的时间是=" + date2Str);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    private static void shellSort(int[] arr) {

        int tmp = 0;
        for (int group = arr.length / 2; group > 0; group /= 2) {
            for (int i = group; i < arr.length; i++) {
                //分组交换,i-5 指向第一个位置,
                for (int j = i - group; j >= 0; j -= group) {
                    //j+5  指向第6个位置进行比较,因为分为5组
                    if (arr[j] > arr[j + group]) {
                        tmp = arr[j];
                        arr[j] = arr[j + group];
                        arr[j + group] = tmp;
                    }

                }
            }
        }

    }


}

排序前的时间是=2019-09-10 10:02:47
排序前的时间是=2019-09-10 10:02:53

可以发现,交换法虽然思想可以实现,但是相比直接插入排序,时间反而更加多了,这显然是不对的。

代码优化:(移位法)

优化原理:

在交换法中,是for循环中不断找是否有可以交换的,显示时间大部分都浪费掉了,于是我们采用图解以及原理所说的直接插入法。当然,直接插入是前后进行比较,移位位位1。由于希尔排序分组了,移位就变为每次分组的组数(group)。

public class ShellSort {


    public static void main(String[] args) {

        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        System.out.println("排序前:" + Arrays.toString(arr));
        shellSort3(arr);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 测试时间
     */
    @Test
    public void testTime() {
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
        }
         System.out.println("排序前:" + Arrays.toString(arr));
        Date data1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(data1);
        System.out.println("排序前的时间是=" + date1Str);
        shellSort3(arr);
        Date data2 = new Date();
        String date2Str = simpleDateFormat.format(data2);
        System.out.println("排序前的时间是=" + date2Str);
        System.out.println("排序后:" + Arrays.toString(arr));
    }

    /**
     * 百度百科里的希尔排序
     * @param array
     */
    private static void shellSort(int[] array) {
        //希尔排序
        int gap = array.length;
        while (true) {
            gap /= 2;   //增量每次减半
            for (int i = 0; i < gap; i++) {
                for (int j = i + gap; j < array.length; j += gap) {//这个循环里其实就是一个插入排序
                    int temp = array[j];
                    int k = j - gap;
                    while (k >= 0 && array[k] > temp) {
                        array[k + gap] = array[k];
                        k -= gap;
                    }
                    array[k + gap] = temp;
                }
            }
            if (gap == 1)
                break;
        }
    }


    /**
     * 希尔排序(移位法)
     *
     * @param arr
     */
    private static void shellSort3(int[] arr) {

        int tmp = 0;
        for (int group = arr.length / 2; group > 0; group /= 2) {

            //第i词插入
            for (int i = 1; i < arr.length; i++) {

                //无序部分第一个值,即待插入值
                int value = arr[i];
                //有序部分最后一个值
                int yw = i - group;
                //插入位置结束条件
                while (yw >= 0 && arr[yw] > value) {

                    //待插入值先前移动,有序部分向后移动
                    arr[yw + group] = arr[yw];
                    //让待插入值与有序部分倒数第(yw+) 比较
                    yw -= group;
                }
                //待插入值插入,因为退出循环,说明位置找到(yw+1)
                arr[yw + group] = value;

            }


        }

    }

}

排序前的时间是=2019-09-10 10:35:32
排序前的时间是=2019-09-10 10:35:32

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值