《排序算法系列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) {
            for (int i = gap; i < arr.length; i++) {
                // 遍历各种中所有的元素(共5组,每组2个元素) 步长5
                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秒:

猜你喜欢

转载自www.cnblogs.com/wangxiucai/p/12677191.html
今日推荐