排序算法-4-希尔排序

一、基本思想

希尔排序是希尔提出的,是通过引入增量对简单插入排序的改进高效版本,也叫缩小增量排序。
希尔排序是把数据按照下标的一定增量分组,对每组数据进行直接插入排序算法进行排序。随着增量主键减小,每一组包含的数据越来越多,当增量减至1时,所有数据恰被分成一组,算法进行最后一轮排序,使所有数据顺序排列。

希尔排序示意图:

在这里插入图片描述
在这里插入图片描述

二、代码实现

此处以从小到大排序为例,用两种方式实现:
1)当发现待插入的数比同组数据大时,直接交换位置;
2)当发现待插入的数比同组数据大时,将较小的数向后移动,直到找到合适位置时插入。

package sort;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * 希尔排序
 * 1)对插入排序的优化,改善了插入排序将最小(大)的数移动到最前面,导致其他数移动次数很多的问题。
 *
 * 2)引入增量,将一组数按照增量分组,并将每一组的数进行插入排序,然后在缩小增量,直到增量为1,对数组做最后一次插入排序,令整个数组有序排列为止。
 * 增量的计算:
 * 3)第一次排序的增量为 数组长度除以2,取整。第二次增量为第一次增量除以2,取整。依此类推,直到增量为1,做最后一次排序(即:能得到几个大于零的增量,就循环几轮,循环条件是:增量>0)。
 * 4)每一轮循环中,将数组按照增量分组(数组长度除以增量=分成的组的数量)并将每一组中的数进行插入排序。
 *   分组逻辑:从第一个数开始,其下标值+增量,所得到的下标位置对应的数就是和它一组的数。
 *
 */
public class ShellSort {

    public static void main(String[] args) {
        int[] arr = new int[80000];
        for(int i = 0;i<arr.length;i++){
            int value = (int)(Math.random()*10);
            arr[i] = value;
        }

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        System.out.println("排序前:"+ format.format(new Date() ));
//        System.out.println(Arrays.toString(arr));
        shellSort2(arr);
        System.out.println("排序后:"+format.format(new Date() ));
//        System.out.println(Arrays.toString(arr));
    }

    /**
     * 交换的方式(从小到大)
     * @param arr
     */
    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++){ //找到增量值对应的下标所在位置的数,该数和第一个数属于同一组,接着往后循环,直到数组的最后一个数向前找到对应的同组数据。
                for(int j = i-gap; j>=0; j-=gap){// j为和下标为gap的数对应的同一组数据
                    if(arr[j] > arr[j+gap]){
                        temp = arr[j+gap];
                        arr[j+gap] = arr[j];
                        arr[j] = temp;
                    }
                }

            }
//            System.out.println("希尔排序第"+(++count)+"轮排序结果:");
//            System.out.println(Arrays.toString(arr));
        }
    }

    /**
     * 没找到可插入的位置时,向后移动数值,找到位置时插入
     * @param arr
     */
    public static void shellSort2(int[] arr){
        int count = 0;
        for(int gap=arr.length/2 ; gap>0; gap/=2){
            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]){// 此处比较的是 temp 和 arr[j-gap]的大小,如果temp>=arr[j-gap] 或者将数组遍历完,说明j-gap上的数比temp小了,此时可插入的位置就是下标为j的位置
                        arr[j]=arr[j-gap];
                        j -= gap; //按照增量向前找下一个同组数据
                    }
                    arr[j] = temp;
                }
            }
//            System.out.println("希尔排序第"+(++count)+"轮排序结果:");
//            System.out.println(Arrays.toString(arr));
        }
    }

}

三、性能对比

1、交换位置的方式:80000个数,大概需要6S左右,而直接插入排序耗时大概500ms左右,可以看出,交换位置的方式反而更慢了。
在这里插入图片描述

2、向后移动位置的方式:
80000个数,大概需要17ms左右,而直接插入排序耗时大概500ms左右,可以看出,向后移动位置的方式在效率上比直接插入排序有较高的改善。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值