算法导论------ShellSort希尔排序

目录

  Shellsort是最古老的排序算法之一,该算法以其发明者Donald L. Shell的名字命名(1959)。在ShellSort排序算法之前的算法时间复杂度基本都是 O(n2) ,该算法是突破这个时间复杂度的第一批算法之一。另外 Shellsort 是快速、易于理解和易于实现的。 然而,其复杂度分析有点复杂。


1.Shellsort的思想

  学过InsertSort排序的都应该了解,直接插入排序算法适用于 基本有序和数据量不大的排序序列。基于这两点1959年Donald L. Shell提出了希尔排序,又称“缩小增量排序”。

基本有序:就是小的关键字基本都在前面,大的关键字基本都在后面,不大不小的关键字基本都在中间。例如:{ 213647589 }就可以称为基本有序,但{ 159378246 }就谈不上基本有序。—–《大话数据结构》

Shellsort的思路如下:

a. 将待排序的数据序列排列在二维数组中
b. 对数组的列进行排序

  经过上面两步处理后数据序列被部分排序。 重复上述过程,但每次使用较窄的二维数组,即数组具有较少数量的列。 在最后一步中,数组只包含一列。 在每个步骤中,序列的排序增加,直到在最后一步中它被完全排序。 然而,由于在前面的步骤中获得的序列的预排序,每个步骤中必需的排序操作的数量是有限的。

示例: 37905168420615734982 是要排序的数据序列。 首先,它被排列在具有7列(左)的数组中,然后列被排序(右):

将待排序的数据序列排列在二维数组中对数组的列进行排序后
38774392400956811265
37834724900956811256

  经过一次ShellSort后,序列变成 33205157440616879982 。数据元素 8 9现在已经到达序列的末尾,但是小元素 2 也仍然存在。 在下一步中,序列被排列成3列,这些列又被排序:

将待排序的数据序列排列在二维数组中 对数组的列进行排序后
30541783570692214689

01345780235679124689

  目前序列变为 00112233445656877989 。序列几乎完全排序。 在最后一步中将该序列布置在一列中时,只需将 6 8 9 移动到它们正确的位置。在经过每一步之后,序列的有序性都得到了提高,并最终得到一个排序完成的序列。
  由上面各个重排矩阵的列数够成的逆序列,称作增量序列


2.代码实现

  实际上在用代码实现Shellsort算法时,数据序列不是布置在真实的二维数组中,而是布置在用适当索引控制的一维数组中。 例如,可以将数组索引为0,5,10,15...等处的数据元素视为是具有5列的二维数组的第一列。 通过这种以增量控制数组索引获得的“列” 用插入排序进行排序,因为插入排序方法在预排序序列中具有良好的性能。

  以下程序数组 a[0,n1) 进行排序。 用于在每个步骤中排列数据的列数(增量序列)已保存在数组cols中。 因此,第一次数据排列时,数据序列被布置为1391376列;第二次被布置为463792列;在最后一步中被布置在 1 列中。 (注意,如果列的数量h大于数据元素的数量 n ,则基本上什么也不做。)每个列内的数据元素通过插入排序来排序。在执行插入排序时首先,第二行(当i >= h)的数据被插入排序到其列中的正确位置,然后第三行(当i>=2h时)的数据被插入排序到其列中的正确的位置等等。

void shellSort (int[] a, int n)
{
    int i, j, k, h, tmp;
    int[] cols = {1391376, 463792, 198768, 86961, 33936, 13776, 4592,
                    1968, 861, 336, 112, 48, 21, 7, 3, 1}
    for (k=0; k<16; k++)
    {
        h=cols[k];
        for (i=h; i<n; i++)           //i=h,意味着从第二行开始对每列InsertSort
        {
            tmp=a[i];                 //插入排序需要的临时变量。
            j=i;
            while (j>=h && a[j-h]>tmp)
            {
                a[j]=a[j-h];
                j=j-h;
            }
            a[j]=tmp;
        }
    }
}

3.增量序列

  1. 希尔序列:[1,2,4,8,16,32,...,2k,...]
    该序列由希尔本人提出。这种序列在最坏情况下的时间复杂度为 O(n2) 。原因很复杂,可以参考《数据结构 邓俊辉》著。

    • PS序列: [1,3,7,15,31,63,...,2k1,...]
      这个序列中各项两辆互素。采用这个序列,希尔排序的效率可以改进至 O(n3/2) ,其中n为排序规模。

    • Pratt序列 [1,2,3,4,6,8,9,12,16,...,2p3q,...]
      其中各项除2 和3 外均不含其它素数因子。采用Pratt序列,希尔排序时间复杂度至多为 O(nlog2n)

    • Knuth序列(没找到出处)
      Knuth提出了他自己的增量序列,遵循公式(3k-1)/ 2或[1,4,14,40,121,…]

    • 4.算法分析

      空间复杂度: 仅使用了常数个辅助单元,因而空间复杂度为 O(1)
      时间复杂度:ShellSort排序准确的说是一类排序。当使用不同的增量序列进行布置时,效率差异极大。这个int [] cols数组的选取(增量序列)选取非常的关键,究竟什么样的布置方案(增量序列cols)才是最好,目前还是一个数学难题,所以时间复杂度分析也比较困难,前人对这方面的研究也比较多,可以查阅相关论文。这里仅仅可以讨论下PS,Pratt和Donald Shell序列的复杂性。 对于Shell的序列,复杂度是 O(n2) ,而对于Pratt的序列,它是 O(nlog2n) 。 PS序列,其中复杂性是 O(n3/2)

      稳定性:(不稳定)
      希尔排序的关键是 把排序序列 用相隔某个“增量” 将元素组成一个“列”,每个“列”各自进行插入排序,从而实现元素的跳跃式移动,使得排序效率提高。当相同的关键字被划分到不同的“列”时,可能会改变它们之间的相对次序,因此希尔排序是一个不稳定的算法。


      5.参考资料

      1.参考了外国网站上一篇英文文章,需要参考原文学习的,可以点击英文原文
      2.《2013年数据结构 联考复习指导》王道论坛
      3.《大话数据结构》 程杰   著
      4.《数据结构》    邓俊辉 著

    • 2
      点赞
    • 2
      收藏
      觉得还不错? 一键收藏
    • 5
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值