希尔排序


希尔排序,有时称为递减增量排序,是在插入排序基础上,把列表拆成几个较小的子表,然后对每个子表使用插入排序的方法。选出子表的方法是希尔排序的关键,它并不是把列表的中相近的元素取出来组成子表,而是使用了一个增量值I,有时也叫做“间隙”,然后每隔一个间隙选中一个元素来组成子表。

这可以从图6中看出来,列表中有9个元素,如果我们使用增量3,就有3个子表,每个子表单独做插入排序。完成之后的列表如图7,现在看这个表虽然没有完全排序,但对子表排序后,元素已经很接近它们的最终位置。



python 递减计数_算法



图6 增量为3的希尔排序




python 递减计数_算法_02


图7 子表排序之后的希尔排序




图8所示为增量是1的插入排序,或者说,这就是个标准的插入排序。得益于前面的子表排序过程,现在需要移动操作要少得多。在这个例子中,只需要移动4次就完成了排序。



python 递减计数_数据结构_03



图8 希尔排序最后一步:增量为1的插入排序




python 递减计数_数据结构_04


图9  希尔排序 初始化子表


前面我们说过,希尔排序的独特性就是增量的选择,下面的函数使用了一个不同的增量的集合,从n/2个子表开始,下一步就是n/4个子表要排序,最终是1个子表进行插入排序。图9所示是这种增量的第一批4个子表。

下面的shellSort函数对每个增量值进行一次子表排序,最终使用插入排序完成

def shellSort(alist):
    sublistcount = len(alist)//2
    while sublistcount > 0:

      for startposition in range(sublistcount):
        gapInsertionSort(alist,startposition,sublistcount)

      print("After increments of size",sublistcount,
                                   "The list is",alist)

      sublistcount = sublistcount // 2

def gapInsertionSort(alist,start,gap):
    for i in range(start+gap,len(alist),gap):

        currentvalue = alist[i]
        position = i

        while position>=gap and alist[position-gap]>currentvalue:
            alist[position]=alist[position-gap]
            position = position-gap

        alist[position]=currentvalue

alist = [54,26,93,17,77,31,44,55,20]
shellSort(alist)
print(alist)



乍看起来,希尔排序不见得比插入排序更好,因为最后一步就完全是一个插入排序。但是,最后一步的插入排序,不需要很多步骤来完成比较和移动,因为通过前面的增量插入排序,列表已经做了“预排序”,也就是说,这个列表已经比普通列表“更有序”,所以在效率上有很大的不同。

对希尔排序的详细分析超出本书的范围,不过我们可以说,它趋向于O(n) 和 O(n2) 之间。对listing5中的增量,性能是O(n2),变更增量,例如使用 2k−1 (1, 3, 7,15, 31, 等),性能可达到O(n3/2).