zoukankan      html  css  js  c++  java
  • 《排序算法系列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秒:

  • 相关阅读:
    GNU make manual 翻译(九十九)
    GNU make manual 翻译( 九十五)
    Shell的 for 循环小例子
    makefile中对目录遍历的小例子
    GNU make manual 翻译(九十三)
    GNU make manual 翻译( 一百)
    GNU make manual 翻译( 九十七)
    GNU make manual 翻译( 九十八)
    mapserver4.8.3 的readme.win32的中文翻译文件
    遥控器编程
  • 原文地址:https://www.cnblogs.com/wangxiucai/p/12677191.html
Copyright © 2011-2022 走看看