希尔排序——难理解,也难分析,凑合看吧

对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要N-1次移动。希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
希尔排序的思想是使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。换句话说,一个h有序数组就是h个互相独立的有序数组编织在一起组成的一个数组。

这么看根本看不懂啊。

换个说人话的模式:

假设你书架上有一堆书,高度各不相同,乱七八糟地放着。你的目标是把它们按从矮到高的顺序排好。

1. 笨方法:插入排序
一种最直接的方法是使用插入排序
你从第二本书开始,把它拿出来,和它前面的所有书比较,找到它应该插入的位置,然后插进去。然后再处理第三本,以此类推。
问题:如果一本很矮的书恰好在最右边,你需要把它和它左边的每一本书都交换位置,一步一步地“挪”到最左边,效率非常低。就像在堵车时,你排在车队最后,只能一辆一辆地往前挪。

2. 聪明的方法:希尔排序
希尔排序想出了一个聪明的点子:“我们为什么不先大踏步地整理一下,让整体看起来‘差不多’有序,然后再做精细的微调呢?”

它的核心思想是:分组插入排序。

我们还用整理书来比喻:

第一步:先“大步长”地分组整理

  • 你决定,先每隔 4本书 分成一组。(这个“3”就是“步长”或“间隔”)

  • 那么,第1、5、9、13...本书成为一组。

  • 第2、6、10、14...本书成为另一组。

  • 第3、7、11、15...本书成为第三组。

  • 现在,你只在每个小组内部,用插入排序的方法把它们排好序。

效果如何?
经过这次“大步长”整理后,虽然整体顺序还不完美,但你会发现一个神奇的现象:那些特别矮的书已经从右边大步地“跳”到了比较靠左的位置,特别高的书也“跳”到了右边。整体上,这堆书已经‘基本有序’了。

第二步:减小步长,再整理

  • 你觉得“4”这个步长还是太大了,现在把步长缩小,比如缩小到 2

  • 你再重新分组:每隔2本书一组(第1、3、5、7...本一组;第2、4、6、8...本一组)。

  • 然后,再次在每个小组内部进行插入排序。

因为现在整体已经“基本有序”了,所以这次小组内的插入排序会进行得非常快。

第三步:最后微调

  • 最后,你把步长缩小到 1。这意味着什么?意味着你把所有书看成一个大组。

  • 此时,因为这堆书已经非常接近完全有序的状态了,所以你只需要进行最后一次全体的插入排序,稍微调整一下个别书籍的位置,整个排序就迅速完成了!

总结一下希尔排序的精髓:

  1. 化整为零:它不像插入排序那样一开始就处理整个序列,而是先将一个大序列分割成若干个小序列。

  2. 由粗到精:它先使用一个较大的“步长”进行排序,让元素能进行大距离的移动,快速消除那些极度无序的情况。

  3. 逐步细化:然后逐步减小步长,序列也随之变得越来越有序。

  4. 最终整合:当步长减小到1时,序列已经基本有序,此时进行最后一次标准的插入排序,效率会非常高。

为什么它比简单的插入排序好?

  • 减少了不必要的移动:在初期大步长的时候,一个小元素可以“跳”过很多位置,快速接近它的最终位置,不用像插入排序那样一步一步地挪。

  • 利用了基本有序的优势:插入排序对“基本有序”的序列排序效率极高。希尔排序的前期工作就是为了创造一个“基本有序”的局面。

所以,你可以把希尔排序看作是插入排序的一个威力加强版,它通过“先宏观,再微观”的策略,极大地提升了排序效率。

实际上希尔排序用的是1,4,13,40,121,264,1093……这个h /= 3来操作的,最后给出代码,代码是我抄的,我写不出来sign

import java.util.Random;

public class ShellSort {

    public static void sort(int [] ints){
        if(ints == null || ints.length <= 1) return;
        int n = ints.length;
        int h = 1;
        while (h < n/3) h = 3 * h + 1;//1,4,13,40,121……
        while(h >= 1){
            for (int i = h;i < n;i++){
                for(int j = i;j >= h && ints[j] < ints[j-h];j -= h){
                    int temp = ints[j];
                    ints[j] = ints[j-h];
                    ints[j-h] = temp;
                }
            }
            h /= 3;
        }
    }

    public static void main(String [] args){
        int [] a = new int[20];
        Random r = new Random();
        for ( int i = 0;i < a.length;i++) a[i] = r.nextInt(200);
        System.out.print("排序前:");
        for(int outprint : a){
            System.out.print(outprint + " ");
        }
        sort(a);
        System.out.print("\n\r排序后:");
        for(int outprint : a){
            System.out.print(outprint + " ");
        }

    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值