常用内部排序算法之五:希尔排序

前言

在前面介绍常用内部排序算法的文章中,我们知道在简单排序算法中,直接插入排序算法的时间复杂度是要优于冒泡排序和简单选择排序的。而这里要讲的希尔排序则是优于直接插入排序的。而且希尔排序打破了原来简单排序中时间复杂度不能超过O(n2)的神话。这也是希尔排序伟大的地方,而且希尔排序不同于之前三种简单排序,希尔排序是以一个科学家的名字进行命名的。

希尔排序的原理

由于希尔排序是在直接插入排序的基础上进行改进的,所以为了更好理解希尔排序,需要再次将直接插入排序请出来。我们知道直接插入排序的基本思想是将一个记录插入到有序表中,构成一个新的有序表。而且直接插入排序的使用情况是基本有序、记录少。在实际的情况中,这两个条件是有点苛刻的,很多情况下都是无序在记录数比较大。这种情况下再使用直接插入排序无疑是影响效率的。基于此,希尔提出了把大记录数的列表分割为一组组更少的记录列表,这个过程的实现采用的跳跃分割策略。所谓跳跃分割策略,就是将相距某个增量的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序

希尔排序的实现算法与分析

下面是希尔排序具体的实现代码:

package com.rhwayfun.algorithm.sort;

public class ShellSort2 {

    public void shellSort(int[] a) {
        //定义一个增长序列,就是分割数组的增量
        int inc = a.length,i,j,k;
        do{
            inc = inc / 3 + 1;
            for (i = inc; i < a.length; i++) {
                if(a[i] < a[i - inc]){
                    //将a[i]插入有序表中
                    k = a[i];
                    //记录后移
                    for (j = i - inc; j >= 0 && k < a[j]; j-=inc) {
                        a[j + inc] = a[j];
                    }
                    //把需要插入的值插入到那个位置,这里就是j+inc
                    a[j + inc] = k;
                }
            }
        }while(inc > 1);//循环的终止条件是增量变为1的时候

        for (i = 0;i < a.length; i++) {
            System.out.println(a[i]);
        }
    }

    public static void main(String[] args) {
        new ShellSort2().shellSort(new int[]{0,9,1,5,8,3,7,4,6,2});

    }
}

上面有几个难以理解的地方。第一点是为什么增量的选取是inc=a.length/3+1。因为目前增量的选取还没有一个定论,但是有研究表明当增量序列为dlta[k]=2tk+110<k<t<log2(n+1)的时候,可以获得不错的效率。第二点,第二个循环是干嘛的。实际上就是把排在序列前面的更大的记录往后挪,然后循坏之后的那个赋值语句就是把要插入的那个值放入原先被移走的那个数的位置。还有一点是为什么循坏的终止条件是增量为1的时候,而不是其他值的时候呢?假设现在增量变为了1,可以发现在从增量为5到增量为1,这个过程已经基本使得整个序列是有序的,当增量变为1的时候,可交换的元素的个数大为减少,所以增量为1是循环结束的条件(其实从另一个角度看,增量必然是一个正整数,而1是最小的正整数)。

然后,我们综合看一下希尔排序与直接插入排序的代码实现:

public void insertSort(int[] a) {
        int i,j,temp;
        for(i = 1; i < a.length; i++){
            if(a[i] < a[i-1]){
                temp = a[i];
                for(j = i - 1; j >= 0 && a[j] > temp; j--){
                    a[j+1] = a[j];
                }
                a[j+1] = temp;
            }
        }

        for(i = 0;i < a.length; i++){
            System.out.println(a[i]);
        }
    }

可以发现大部分是相同的代码,只不过希尔排序采用了跳跃式的分割数组的策略,在i与j那里改成了增量序列,所以大大提高了排序的整体性能。那么希尔排序的时间复杂度是多少呢?答案是O(n3/2),明显是优于O(n2)的时间复杂度的,这也是希尔排序伟大的地方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值