• 《排序算法系列4》希尔排序


    1  概述

     2 排序思想

    一天,一尘拿着扑克自己在那玩,刚被师傅看见了

    首先它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高

    可以看出,他是按下标相隔距离为4分的组,也就是说把下标相差4的分到一组,比如这个例子中a[0]与a[4]是一组、a[1]与a[5]是一组...,这里的差值(距离)被称为增量

     每个分组进行插入排序后,各个分组就变成了有序的了(整体不一定有序)

    此时,整个数组变的部分有序了(有序程度可能不是很高)

     然后缩小增量为上个增量的一半:2,继续划分分组,此时,每个分组元素个数多了,但是,数组变的部分有序了,插入排序效率同样比高

    同理对每个分组进行排序(插入排序),使其每个分组各自有序

     

     最后设置增量为上一个增量的一半:1,则整个数组被分为一组,此时,整个数组已经接近有序了,插入排序效率高

    同理,对这仅有的一组数据进行排序,排序完成

    希尔排序动态演示

    3 时间复杂度

    4 希尔排序推导

      //希尔排序推导
      public static void main(String[] args) {
            int[] arr = {5,7,8,3,1,2,4,6};
            shellSort(arr);
      }
      // 使用逐步推导的方式
        public static void shellSort(int[] arr) {
            // 希尔排序第1轮
            int temp;
            // 因为第一轮排序,将8个数据分成了4组
            // 跨度为4
            for (int i = 4; i < arr.length; i++) {
                // 遍历各种中所有的元素(共5组,每组2个元素) 步长5
                for (int j = i - 4; j >= 0; j -= 4) {
                    // 如果当前的元素大于步长后的那个元素 说明交换
                    if (arr[j] > arr[j + 4]) {
                        temp = arr[j];
                        arr[j] = arr[j + 4];
                        arr[j + 4] = temp;
                    }
                }
            }
            System.out.println("第1趟排序结果为:");
            System.out.println(Arrays.toString(arr));
    
            // 希尔排序第2轮
            // 因为第一轮排序,将8个数据分成了4/2 = 2组
            // 跨度为2
            for (int i = 2; i < arr.length; i++) {
                // 遍历各种中所有的元素(共2组) 步长2
                for (int j = i - 2; j >= 0; j -= 2) {    
                    // 如果当前的元素大于步长后的那个元素 说明交换
                    if (arr[j] > arr[j + 2]) {
                        temp = arr[j];
                        arr[j] = arr[j + 2];
                        arr[j + 2] = temp;
                    }
                }
            }
            System.out.println("第2趟排序结果为:");
            System.out.println(Arrays.toString(arr));
    
            // 希尔排序第3轮
            // 因为第一轮排序,将10个数据分成了 2/2 = 1组
            for (int i = 1; i < arr.length; i++) {
                // 遍历各种中所有的元素(共5组,每组2个元素) 步长5
                for (int j = i - 1; j >= 0; j -= 1) {
                    // 如果当前的元素大于步长后的那个元素 说明交换
                    if (arr[j] > arr[j + 1]) {
                        temp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = temp;
                    }
                }
            }
            System.out.println("第3趟排序结果为:");
            System.out.println(Arrays.toString(arr));
        }

    推导结果:

    5 交换法实现希尔排序(代码方便理解,速度慢)

      // **********************交换法********************************
        // 根据前面的逐步分析,使用循环处理
        public static void shellSort2(int [] arr) {
            //辅助变量 插入排序时进行交换
            int temp;
            //通过循环 进行分组,每次的增量为长度的一半
            for(int gap = arr.length / 2;gap > 0;gap /= 2) {
                //从gap开始  开始实现插入排序
                for (int i = gap; i < arr.length; i++) {
                    //循环数组前面的数组 进行比较  幅度变换为gap
                    for (int j = i - gap; j >= 0; j -= gap) {
                        //如果发现值比较小 进行交换
                        if (arr[j] > arr[j + gap]) {
                            temp = arr[j];
                            arr[j] = arr[j + gap];
                            arr[j + gap] = temp;
                        }
                    }
                }
            }    
        }

    6 移位法实现希尔排序(代码难理解,速度快)

      // **********************移位法************************************
        public static void shellSort3(int[] arr) {
            // 增量gap 逐个对其所在的组进行直接插入排序
            for (int gap = arr.length / 2; gap > 0; gap /= 2) {
                // 从第gap个元素,逐个对其所在的组进行直接插入排序
                for (int i = gap; i < arr.length; i++) {
                    int j = i;
                    int temp = arr[j];
                    if (arr[j] < arr[j - gap]) {
                        while (j - gap >= 0 && temp < arr[j - gap]) {
                            // 移动
                            arr[j] = arr[j - gap];
                            j -= gap;
                        }
                        // 当退出这个循环时,就给temp找到插入的位置
                        arr[j] = temp;
                    }
                }
            }
        }

     7 希尔排序速度测试

      public static void main(String[] args) {
            // *********************希尔数组 交换法速度测试*******************************888
            int[] arr2 = new int[8000000];
            for (int i = 0; i < arr2.length; i++) {
                arr2[i] = (int) (Math.random() * 800000);
            }
            // 显示排序前的数组
            //System.out.println("选择排序测试数组:" + Arrays.toString(arr2));
    
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date1 = new Date();
            String time1 = simpleDateFormat.format(date1);
            System.out.println("排序前的时间为:" + time1);
            
            //希尔排序 交换法
            shellSort2(arr2);
            //希尔排序 移位法
            shellSort3(arr2);
    
            Date date2 = new Date();
            String time2 = simpleDateFormat.format(date2);
            System.out.println("排序后的时间为:" + time2);
        }

    交换法-----速度测试结果:

    8万数据排序结果(交换法) 大约需要10秒:

    80万数据排序结果(交换法) 大约需要无数秒:(等不到结果了)

    移位法-----速度测试结果:

     8万数据排序结果(移位法) 需要不到1秒:

    80万数据排序结果(移位法) 需要不到1秒:

    800万数据排序结果(移位法) 需要不到3秒:

  • 相关阅读:
    Element ui 的使用
    Vue基本介绍
    静态界面传值javascript
    手机网页怎么禁止缩放、拖放、如何屏蔽到手机本身键盘
    jQuery学习之prop和attr的区别示例介绍
    jquery复选框 选中事件 及其判断是否被选中
    手机端html5触屏事件(touch事件)
    页面滚动到底部自动 Ajax 获取文章
    转Python RegEx正则
    转Python 日期
  • 原文地址:https://www.cnblogs.com/wangxiucai/p/12677191.html
Copyright © 2020-2023  润新知