知识点10:常见的排序算法–希尔排序

希尔排序的原理

这次要介绍的希尔算法,对我而言也是个挑战,因为我对它的理解也仅限于皮毛,或者说,仅仅侷限在知道原理,如何实现和使用区域上。并且就算是原理,也不清楚对不对,姑且先记下来,希望有路过的大神们指点指点迷津吧。
故事先回到我的上一章博客:常见的排序算法——插入排序。在这个算法中,我们知道了插入排序的原理是在一个近乎有序的数组中使用起来比较高效,因为它本身就是按照从低到高一步一步把相应的数字插入到相关位置,进而实现排序的方法。但是,如果在这里,我们的第一个元素就是数据的最大值呢?那我们就要把这个元素从第一个位置逐渐移动到最后一个位置,这样对插入排序的性能而言也是极有影响的。因此,为了解决这个问题,或者说为了使插入排序也能适应乱序数组,我们已插入排序为基础,发展出了希尔排序。
什么意思呢?这里就要从原理而言了。
希尔排序的原理是假设数组中的每个元素,和与其相隔的第n个元素之间是有序的。这样的数组叫叫做n有序数组。也就是说,一个n有序数组就是有n个互相独立的有序数组按照相应的顺序混合在一起的。就相当于我们把一个数组分治为几个模块,模块之间进行数据交换。如此,当第一个元素为最大值时,那我们就可以将其一瞬间移动到第n个元素的位置而不用一步一步地移动,进而便提高了性能。就好比【9,1,2,6,5,7,8,4,3】这样的数组,我们可以在第一次判断的时候,如果间隔为4,那么便可移动到第元素5的位置->【5,1,2,6,5,7,8,4,3】,而当程序执行到arr[4],也就是移动后9的位置的时候,我们还可以继续将其移动到下一个间隔为4的元素上。由此,我们只需要交换9两次,便实现了插入算法中交换8次9的效果。效率怎么能不快呢?同时,我们每一次交换,都是把相对大的值往后挪,把相对小的值往前靠,由此便为执行插入算法准备了条件。也就是说,当我们逐渐缩短间隔n的个数时,那么数组就会逐渐倾向于一个有序数组,所以我们在最后,把n设为1的时候,你就发现,其实就变成了我们的插入排序。也就是说,希尔排序的核心思想还是插入排序,但是它通过数据的大幅度交换,一来提高效率,二来也为插入排序的高效实现准备好了条件。那么,双管齐下,便大幅度提升了它的性能,使其适合中等大小数组的排序。另外,由于它的代码量比较小,不需要额外的内存空间,所以也就成为了我们比较喜欢用的排序方法。

希尔排序的实现

对于希尔排序的算法,代码见下面,可能看起来有些复杂,但实际上还是比较容易理解的。

void sort(int[] arr){
    int length =  arr.length;
    /*
    *按照一定的规律确定space(间隔空间)的空间
    *原因:我们为了实现把一个乱序数组逐渐倾向于有序数组
    *     需要多次逐渐缩小间隔的差距,使其最终为1
    *     为了方便运行,一般用某种公式来限定,这里对间隔空间(也叫做增量)的
    *     选取,其实有很多种选法,目前也有相关论文阐述如何选取一个最佳增量,有兴趣的可以去看一下。
    *     这里,为了举例,就假设以数列的一半为增量,以后每次减半,直到增量为1;也就是说,增量最好为基数
    */
    int space = (length/2)-1;

    while(space >= 1){
        int i,j;
        for(i = space;i<length;i++){

            for(j = i;j>=space;j-=space){
                if(arr[j]<arr[j-space]){
                    int temp = arr[j];
                    arr[j] = arr[j-space];
                    arr[j-space] = temp;
                }else{
                    break;
                }
            }
        }
        space/=2;
    }

}

当然,以上实现也只是思路上的表现,真正敲代码的时候还需要考虑优化,解耦和边界检测的一些问题。这里就不多阐述啦。
如果对文章有疑问,欢迎下方评论指出,谢谢~
下一章:知识点11:常见的排序算法——归并排序。敬请期待~

点赞