排序算法之希爾排序及其增量序列

希爾排序


其餘排序方法:選擇排序冒泡排序歸併排序快速排序插入排序希爾排序html


思想

希爾排序大概就是,選一組遞減的整數做爲增量序列。最小的增量必須爲1:\(D_M>D_{M-1}>...>D_1=1\)python

  • 先用第一個增量把數組分爲若干個子數組,每一個子數組中的元素下標距離等於增量;
  • 而後對每一個子數組進行簡單插入排序
  • 再使用第二個增量,繼續一樣的操做,直到增量序列裏的增量都使用過一次。
    (增量爲1時,其實就是對整個數組進行簡單插入排序)

圖解

看圖更容易理解吧:
(借用一下慕課的浙大數據結構課件。由於課件本來是ppt,而我只有pdf,因此顏色沒有上齊,請將就將就emmm)
shell

性能

希爾排序快不快主要取決於咱們怎麼取增量序列,原始希爾排序的取法就是:\(D_M=\lfloor N/2 \rfloor, D_k=\lfloor D_{k+1}/2 \rfloor\)
此增量序列也成爲Shell增量序列
原始希爾排序最壞的時間複雜度爲\(O(n^2)\)數組

代碼

# 原始希爾排序
# 增量序列爲D(M)=N/2, D(k)=D(k+1)/2 向下取整
def shellSort(arr):
    size = len(arr)
    # 正整數右移一位至關於除以2且向下取整
    step = size >> 1
    while step > 0:
        for i in range(step, size):
            j = i
            tmp = arr[j]
            while j >= step:
                if tmp < arr[j - step]:
                    arr[j] = arr[j - step]
                    j -= step
                else:
                    break
            arr[j] = tmp
        step = step >> 1

優化

希爾排序的優化主要是針對增量序列的優化。
增量序列若是取得很差,效率比直接插入排序還要低,下面舉個例子(直接借用課件了):數據結構

在這個例子裏,前幾個增量沒有起到任何做用(只起到了拖延時間的做用哈哈)。
因此有人就發現了,若是增量之間不互質的話,那有些狀況就無論用了。app

因而有些大佬們就整出了下面這些增量序列:Hibbard增量序列Knuth增量序列Sedgewick增量序列等等
下面主要介紹Hibbard增量序列Sedgewick增量序列函數

Hibbard增量序列

Hibbard增量序列的取法爲\(D_k=2^k-1\):{1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191...}
最壞時間複雜度爲\(O(N^{3/2})\);平均時間複雜度約爲\(O(N^{5/4})\)性能

先來個Hibbard增量序列的獲取代碼:測試

# Hibbard增量序列
# D(i)=2^i−1,i>0
def getHibbardStepArr(n):
    i = 1
    arr = []
    while True:
        tmp = (1 << i) - 1
        if tmp <= n:
            arr.append(tmp)
        else:
            break
        i += 1
    return arr

排序代碼稍微修改一下就行:大數據

# 希爾排序(Hibbard增量序列)
def shellSort(arr):
    size = len(arr)
    # 獲取Hibbard增量序列
    stepArr = getHibbardStepArr(size)
    # 由於要倒着使用序列裏的增量,因此這裏用了reversed
    for step in reversed(stepArr):
        for i in range(step, size):
            j = i
            tmp = arr[j]
            while j >= step:
                if tmp < arr[j - step]:
                    arr[j] = arr[j - step]
                    j -= step
                else:
                    break
            arr[j] = tmp

至於爲何要用python內置函數reversed(),而不用其它方法,是由於reversed()返回的是迭代器,佔用內存少,效率比較高。
若是先使用stepArr.reverse(),再用range(len(arr))的話,效率會比較低;
並且實測reversed也比range(len(arr) - 1, -1, -1)效率高,故使用reversed();
還有就是先stepArr.sort(reverse=True),再用range(len(arr)),一樣效率低。
這幾種方法比較的測試代碼在這裏,有興趣的朋友能夠看看:Python列表倒序輸出及其效率

Sedgewick增量序列

Sedgewick增量序列的取法爲\(D=9*4^i-9*2^i+1\)\(4^i-3*2^i+1\):{1, 5, 19, 41, 109, 209, 505, 929, 2161...}
最壞時間複雜度爲\(O(N^{4/3})\);平均時間複雜度約爲\(O(N^{7/6})\)

Sedgewick增量序列的獲取代碼:

# Sedgewick增量序列
# D=9*4^i-9*2^i+1 或 4^(i+2)-3*2^(i+2)+1 , i>=0
# 稍微變一下形:D=9*(2^(2i)-2^i)+1 或 2^(2i+4)-3*2^(i+2)+1 , i>=0
def getSedgewickStepArr(n):
    i = 0
    arr = []
    while True:
        tmp = 9 * ((1 << 2 * i) - (1 << i)) + 1
        if tmp <= n:
            arr.append(tmp)
        tmp = (1 << 2 * i + 4) - 3 * (1 << i + 2) + 1
        if tmp <= n:
            arr.append(tmp)
        else:
            break
        i += 1
    return arr

排序代碼稍微修改一下就行:

# 希爾排序(Sedgewick增量序列)
def shellSort(arr):
    size = len(arr)
    # 獲取Sedgewick增量序列
    stepArr = getSedgewickStepArr(size)
    for step in reversed(stepArr):
        for i in range(step, size):
            j = i
            tmp = arr[j]
            while j >= step:
                if tmp < arr[j - step]:
                    arr[j] = arr[j - step]
                    j -= step
                else:
                    break
            arr[j] = tmp

其餘排序方法:選擇排序冒泡排序歸併排序快速排序插入排序希爾排序

相關文章
相關標籤/搜索