八大排序——希尔排序

来源:互联网 发布:itemcf算法 编辑:程序博客网 时间:2024/05/17 05:03

希尔排序(Shell Sort),是插入排序的一种。是直接插入排序的改进版,它是非稳定排序算法。其得名于它的提出者D.L.Shell,Shell Sort等算法的提出打破了“排序算法不可能突破O(n^2)”的魔咒,它将排序算法的时间复杂度提成到了O(nlogn),不可能超越的O(n^2)彻底成为了历史。
既然希尔排序是直接插入排序的改进版,那么它肯定是在直接插入排序的优势上做文章。了解直接插入排序的都知道,当待排序的序列基本有序的时候,我们只需要进行少量的插入操作,就可以完成整个记录的排序工作,这时的直接插入排序是最高效的。希尔排序就是先将待排序序列调整到基本有序,然后再对整个序列进行一次直接插入排序,这样的话会比直接进行直接插入排序效率高的多。所谓的基本有序,就是小的关键字基本在前面,大的基本在后面,不大不小的在中间。可是怎样才能将序列调整为基本有序呢?这里我们采取跳跃分割的策略,即将相聚某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。当然,一次调整并不能保证基本有序,我们可以逐渐缩小这个“增量”,每缩小一次进行一次调整,直到“增量”缩小为1,我们就认为它基本有序了,然后再进行一次“增量”为1的调整,也就是对整个序列进行一次直接插入排序,就可以得到我们想要的有序序列。
那么“增量”到底该以怎样的形式进行缩小呢?这目前还是一个数学难题,这里我们将以increment=length;increment=increment/2;increment>=1的形式缩小,increment即是“增量”,length是序列长度。也就是说,初始增量是length/2,其后每调整一次缩小一半,直到为1。代码如下:

void ShellSort(int a[],int length){    int i,j;    int temp;    int increment=length;    do{        increment=increment/2;        for(i=increment;i<length;i++)        {            if(a[i]<a[i-increment])            {                temp=a[i];                for(j=i-increment;j>=0 && a[j]>temp;j-=increment)                {                    a[j+increment]=a[j];                }                a[j+increment]=temp;            }        }    }while(increment>1);}

假设我们所要排序的序列为a[9]={9,1,5,8,3,7,4,6,2},那么length=9,do-while进入第一次循环,“增量”increment=4,排序操作如下:
这里写图片描述
第一步:比较a[0]与a[0+4],9>3,将3赋给”哨兵”temp,进入代码第13行循环,将9后移increment位,也就是4位,执行j-=increment,不符合循环条件,循环结束,将”哨兵”temp的值赋给a[j+increment],也就是a[0]的位置,这一步操作就是一个直接插入排序的操作。

这里写图片描述
第二步:比较a[1]与a[1+4],1<7,不满足if条件,循环进入下一层

这里写图片描述
第三步:比较a[2]与a[2+4],5>4,进入if语句(过程与第一步相同不再阐述)。

这里写图片描述
第四步:比较a[3]与a[3+4],8>6,进入if语句(过程与第一步相同不再阐述)。

这里写图片描述
第五步(这一步比较特殊):比较a[4]与a[4+4],9>2,将2赋给”哨兵”temp,进入代码第13行循环,将9后移increment位,也就是4位,执行j-=increment后,仍然符合for循环条件,循环进入下一层,将3后移increment位,也就是4位,执行j-=increment后,不再符合循环条件,循环结束,将”哨兵”temp的值赋给a[j+increment],也就是a[0]的位置,这一步操作就是一个直接插入排序的操作。第一层do-while循环最终的序列如下图:
这里写图片描述
我们可以看到,上图序列中任意位置的关键字都符合a[i+4]>a[i],当increment=2的时候也是同样的道理,细心的同学会发现,不管length是多少,增量increment最终都会等于1,其实,当增量为1时的操作,就是一次全面的直接插入排序,只不过这时它所操作的序列已经基本有序了。

延伸

既然能将直接插入排序改进为希尔排序,那冒泡排序能代替直接插入排序吗?答案是当然的,代码如下:

void ShellSort(int a[],int length){    int i,j;    int increment=length;    do{        increment=increment/2;        for(i=increment;i<length;i++)        {            if(a[i]<a[i-increment])            {                a[i]=a[i]^a[i-increment];                a[i-increment]=a[i]^a[i-increment];                a[i]=a[i]^a[i-increment];                for(j=i-increment;j-increment>=0 && a[j]<a[j-increment];j-=increment)                {                    a[j]=a[j]^a[j-increment];                    a[j-increment]=a[j]^a[j-increment];                    a[j]=a[j]^a[j-increment];                }            }        }    }while(increment>1);}

注:转载请说明出处

2 0
原创粉丝点击