希尔排序笔记

希尔排序(算法)

基本介绍

简单插入排序存在的问题:

我们看简单的插入排序可能存在的问题.
数组 arr = {2,3,4,5,6,1} 这时需要插入的数 1( 最小), 这样的过程是:
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
结论:当 需要插入的数是较小的数时, 后移的次数明显增多,对 效率有影响.

希尔排序是希尔(Donald Shell)于 1959 年提出的一种排序算法。希尔排序也是一种 插入排序,它是简单插入

排序经过改进之后的一个 更高效的版本,也称为 缩小增量排序。

算法思想与图解

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

在这里插入图片描述

在这里插入图片描述

代码实现

有一群小牛, 考试成绩分别是 {8,9,1,7,2,3,5,4,6,0} 请从小到大排序. 请分别使用

  1. 希尔排序时, 对有序序列在插入时采用 交换法, 并测试排序速度.
  2. 希尔排序时, 对有序序列在插入时采用 移动法, 并测试排序速度

注: main方法里注释掉的代码是用来测试该算法所用时间的

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {8,9,1,7,2,3,5,4,6,0};
        System.out.println("排序前:");
        System.out.println(Arrays.toString(arr));
//        int[] arrTest = new int[80000];
//        for (int i = 0; i < arrTest.length; i++) {
//            arrTest[i] = (int)(Math.random()*80000); //生成一个【0,80000】的数
//        }

//        Date date1 = new Date();
//        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM--dd HH:mm:ss");
//        String date1Str = simpleDateFormat.format(date1);
//        System.out.println("排序前的时间是="+date1Str);

//        shellSort(arrTest); //测试交换式希尔排序的时间
//        shellSort(arr); // 交换式的希尔排序
//        shellSort2(arrTest); //测试移动式希尔排序的时间
        shellSort2(arr); //移位式的希尔排序

        System.out.println("排序后:");
        System.out.println(Arrays.toString(arr));

//        Date date2 = new Date();
//        SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM--dd HH:mm:ss");
//        String date2Str = simpleDateFormat2.format(date2);
//        System.out.println("排序后的时间是="+date2Str);

    }

    //交换法
    // 希尔排序时, 对有序序列在插入时采用交换法,
    // 思路(算法) ===> 代码
    public static void shellSort(int[] arr){

        int temp = 0;
        int count = 0;
        //根据逐步分析,使用循环处理
        for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                // 遍历各组中所有的元素(共 gap 组,每组有  个元素), 步长 gap
                for (int j = i - gap; j >= 0; j -= gap) {
                    // 如果当前元素大于加上步长后的那个元素,说明交换
                    /*
                    注: 下面这个if语句里一定不能把 j+gap 写成 i
                      因为第三层for循环里 是为了把当前一组的元素 的大小顺序给排好
                        当 i 的值小的时候,能比较的元素个数也少, 当i逐渐加到最大时, 各个组(注:是不同的组)的顺序会排好

                        举例: 当gap = 2(即第二次希尔排序)时,
                          i一开始为2 , 所以只能arr[0] 和 arr[2]比较
                             接着为3 ,       arr[1]    arr[3]                (1,3为一组)
                                 4          arr[2]和arr[4] 、 arr[0]和arr[2] (0,2,4为一组)
                                 (注: 这时,0,2,4之间的顺序是排好了的,1、3也是)
                                 到了最后一个元素时,各个组内部就都拍好了序
                                 。。。。。。。。。
                     */
                    if (arr[j] > arr[j+gap]){
                        temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
            //System.out.println("希尔排序第"+(++count)+"一轮后="+ Arrays.toString(arr));
        }

        //使用逐步推导的方式编写
//        int temp = 0;
//        //希尔排序的第一轮,是将10个数据分成了5组 (10/2)
//        for (int i = 5; i < arr.length; i++) {
//            // 遍历各组中所有的元素(共 5 组,每组有 2 个元素), 步长 5
//            for (int j = i - 5; j >= 0; j -= 5) {
//                // 如果当前元素大于加上步长后的那个元素,说明交换
//                if (arr[j] > arr[j+5]){
//                    temp = arr[j];
//                    arr[j] = arr[j+5];
//                    arr[j+5] = temp;
//                }
//            }
//        }
//        System.out.println("希尔排序第一轮后="+ Arrays.toString(arr));
//
//        //希尔排序的第二轮,是将10个数据分成了5/2 = 2组
//        for (int i = 2; i < arr.length; i++) {
//            // 遍历各组中所有的元素(共 2 组,每组有 5 个元素), 步长 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("希尔排序第二轮后="+ Arrays.toString(arr));
//
//        //希尔排序的第三轮,是将10个数据分成了2/2 = 1组
//        for (int i = 1; i < arr.length; i++) {
//            // 遍历各组中所有的元素(共 1 组,每组有 10 个元素), 步长 1
//            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("希尔排序第三轮后="+ Arrays.toString(arr));
    }

    //对交换式的希尔排序进行优化 -> 移位法
    public static void shellSort2(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]; //存放要插入元素的值
                //跟插入排序算法类似,要找到元素最终插入的位置 (但要注意 步长)
                while (j - gap >= 0 &&  temp < arr[j-gap]){
                    //移动
                    arr[j] = arr[j-gap];
                    j -= gap;
                }
                //当退出while循环时,说明已经给temp找到了插入的位置
                arr[j] = temp;
            }
        }
    }
}

运行结果

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值