我們之前學過插入排序,它在數組大部分元素已經有序時效率很高,但如果數組完全無序或者逆序,每次只能移動一個元素,效率就比較低。希爾排序(Shell Sort)是插入排序的改進版,它通過分組的方式,讓數組先變得基本有序,再用插入排序處理,從而提高效率。

希爾排序的基本思想

希爾排序的核心是“分組”和“插入排序”。它不像插入排序那樣只比較相鄰元素,而是將數組分成若干組,每組內的元素間隔一定的距離(這個距離稱爲“增量”或“步長”),然後對每組內的元素進行插入排序。隨着增量逐漸減小,分組越來越細,直到增量爲1時,整個數組已經基本有序,此時插入排序就能快速完成。

希爾排序的步驟

  1. 選擇初始增量:通常初始增量取數組長度的一半(n//2),然後每次將增量減半,直到增量爲0。
  2. 分組與插入排序:對於每個增量gap,數組被分成gap個“組”,每個組內的元素下標相差gap。例如,數組長度爲10,gap=3時,組1是下標0、3、6、9,組2是下標1、4、7,組3是下標2、5、8。然後對每組內的元素進行插入排序。
  3. 重複調整增量:當增量減小到1時,整個數組已基本有序,此時插入排序能快速完成。

代碼實現(Python)

def shell_sort(arr):
    n = len(arr)
    gap = n // 2  # 初始增量爲數組長度的一半
    while gap > 0:
        # 遍歷每個組內的元素
        for i in range(gap, n):
            temp = arr[i]  # 保存當前元素,避免被覆蓋
            j = i - gap    # 組內前一個元素的下標
            # 將前面比temp大的元素後移
            while j >= 0 and arr[j] > temp:
                arr[j + gap] = arr[j]  # 後移一位
                j -= gap               # 繼續向前比較
            arr[j + gap] = temp        # 將temp插入到正確位置
        gap = gap // 2  # 增量減半,逐步縮小
    return arr

代碼解釋
- n = len(arr):獲取數組長度,用於計算初始增量。
- gap = n // 2:初始增量爲數組長度的一半,這是最常用的簡單增量方式。
- while gap > 0:循環處理不同的增量,直到增量爲0。
- for i in range(gap, n):遍歷每個組內的元素(從第gap個元素開始)。
- temp = arr[i]:保存當前元素,避免後續賦值覆蓋。
- j = i - gap:計算當前元素所在組的前一個元素下標。
- while j >= 0 and arr[j] > temp:若前一個元素比temp大,後移該元素並繼續向前比較;否則停止。
- arr[j + gap] = temp:找到合適位置後,將temp插入。
- gap = gap // 2:增量減半,逐步縮小分組,直到分組爲1(即普通插入排序)。

舉例說明(以數組[5, 3, 8, 4, 2]爲例)

  1. 初始數組[5, 3, 8, 4, 2]n=5,初始增量gap=5//2=2

  2. 處理gap=2
    - 分組:組1(下標0、2、4:元素5、8、2),組2(下標1、3:元素3、4)。
    - 組1插入排序

    • i=2(元素8):j=0i-gap=0),arr[0]=5 <=8,無需移動。數組不變:[5,3,8,4,2]
    • i=4(元素2):j=4-2=2arr[2]=8>2),後移8到arr[4],數組變爲[5,3,2,4,8]j=0arr[0]=5>2),後移5到arr[2],數組變爲[5,3,5,4,8]j=-2(找到位置),插入2到arr[0],組1排序後:[2,3,5,4,8]
    • 組2插入排序
    • i=3(元素4):j=3-2=1arr[1]=3 <=4),無需移動。數組不變:[2,3,5,4,8]
  3. 處理gap=1(此時增量減半爲1,即普通插入排序):
    - i=1(元素3):j=0arr[0]=2 <=3),不動。
    - i=2(元素5):j=1arr[1]=3 <=5),不動。
    - i=3(元素4):j=2arr[2]=5>4),後移5到arr[3],數組變爲[2,3,5,5,8]j=1arr[1]=3 <=4),插入4到arr[2],數組變爲[2,3,4,5,8]
    - i=4(元素8):j=3arr[3]=5 <=8),不動。

  4. 最終結果[2, 3, 4, 5, 8]

總結

  • 時間複雜度:平均約爲O(n log n),最壞情況爲O(n²),比插入排序更高效。
  • 空間複雜度:O(1),原地排序,僅需臨時變量。
  • 適用場景:適用於中等規模數組,尤其當元素分佈不均勻時,分組排序可減少移動次數。

通過分組和逐步縮小增量,希爾排序讓數組先“粗排”再“精排”,有效提升了插入排序的效率,是一種實用的排序算法。

小夜