希尔排序原理与实现

希尔排序,要从插入排序的劣势说起,如果插入的数据需要排到有序数组最前方,那插入排序的效率会非常低下。因为插入的数据要逐个和有序数组数据进行比较。

为了解决这个问题,就有了希尔排序,其实就是在插入排序的基础上,做了一定的优化。

以数组int arr = {9, 4, 7, 8, 5, 0, 6, 2, 3, 1}为例,在做排序之前,依据下标进行分组。

第一次分为五组:{arr[0],arr[5]},{arr[1],arr[6]},{arr[2],arr[7]},{arr[3],arr[8]},{arr[4],arr[9]},然后对五个分组内数据进行排序。此时数组中相对较小的数据,基本排列在了数据前一半索引。

在第一次排序的基础上,继续进行第二次分组。

第二次分为二组:{arr[0],arr[2],arr[4],arr[6],arr[8]},{arr[1],arr[3],arr[5],arr[7],arr[9]},然后对这2个分组分别进行排序。

第三次,对所有数据进行排序,此时数组已接近有序,所以排序速度会很快。

在上面例子的基础上进行归纳,则可以得出希尔排序的步骤,便是先对数据进行分组,对各个分组进行排序,构造接近有序集合,最后对所有数据进行排序。

希尔排序使用的排序算法又分为交换排序和插入排序

为了方便理解,第一次使用交换排序进行分组内排序,代码如下:

//    static int[] list = {9, 4, 7, 8, 5, 0, 6, 2, 3, 1};
    static int[] list = new int[800];

    static {
        for (int i = 0; i < list.length; i++) {
            list[i] = (int) (Math.random() * 8000000);
        }
    }
long l = System.currentTimeMillis();
System.out.println(Arrays.toString(list));
//分为五组
for (int i = 0; i < list.length - 5; i++) {
    for (int j = i; j < list.length - 5; j += 5) {
        if (list[j] > list[j + 5]) {
            int temp = list[j];
            list[j] = list[j + 5];
            list[j + 5] = temp;
        }
    }
}

System.out.println(Arrays.toString(list));
//分为两组
for (int i = 0; i < list.length - 2; i++) {
    for (int j = i; j < list.length - 2; j += 2) {
        if (list[j] > list[j + 2]) {
            int temp = list[j];
            list[j] = list[j + 2];
            list[j + 2] = temp;
        }
    }
}
System.out.println(Arrays.toString(list));

//分为一组
for (int i = 0; i < list.length - 1; i++) {
    for (int j = i; j < list.length - 1; j += 1) {
        if (list[j] > list[j + 1]) {
            int temp = list[j];
            list[j] = list[j + 1];
            list[j + 1] = temp;
        }
    }
}
System.out.println(Arrays.toString(list));
long s = System.currentTimeMillis();
System.out.println(s - l);

归纳总结后,使用交换排序的希尔排序代码如下:

long l = System.currentTimeMillis();
//二分数组
for (int gap = list.length / 2; gap >= 1; gap /= 2) {
    for (int i = 0; i < list.length - gap; i++) {
        for (int j = i; j < list.length - gap; j += gap) {
            if (list[j] > list[j + gap]) {
                int temp = list[j];
                list[j] = list[j + gap];
                list[j + gap] = temp;
            }
        }
    }
}
long s = System.currentTimeMillis();
System.out.println(s - l);

经过实际测试发现,使用交换排序的希尔排序速度并没有明显提升,8w条数据,耗时6s。

那么我们在此基础上,继续改进,使用插入排序来实现:

long l = System.currentTimeMillis();
//拆分数组需要的次数
for (int gap = list.length / 2; gap >= 1; gap /= 2) {
    //正式拆分数组,根据步长得到每一个拆分后的数组
    for (int i = 0; i < gap; i++) {
        //遍历拆分数组
        for (int j = i + gap; j < list.length; j += gap) {
            //对数组进行插入排序
            int insertValue = list[j];
            int insertIndex = j;
            //如果插入值的索引大于等于数组第一位的索引,且插入值小于有序数组数据,用有序数组最末值覆盖插入索引值。
            //此时连续2个索引会存在相同的有序数组最大值,数组索引向前一位,把插入值插入
            while (insertIndex >= i + gap && insertValue < list[insertIndex - gap]) {
                list[insertIndex] = list[insertIndex - gap];
                insertIndex -= gap;
            }
            list[insertIndex] = insertValue;
        }

    }
}
long s = System.currentTimeMillis();
System.out.println(s - l);

 经过测试,8w数据耗时14ms,80w耗时148ms,800w耗时2.4s,相对于普通排序,有了明显提升。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值