數據結構和算法-12-希爾排序

這一篇來學習希爾排序,希爾排序是直接插入排序的一種優化算法。

1.希爾排序概念

希爾排序(shell sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法,該方法因DL.Shell於1959年提出而得名。希爾排序是把紀錄按下標的一定增量分組,對每組使用直接插入排序算法排序。隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰好被分層一組,算法便終止。

 

2.希爾排序思想

希爾排序的基本思想是:將數組列在一個表中並對列分別進行插入排序,重複這過程,不過每次用更長的列(步長更長了,列數更少了)來進行。最後整個表就只有一列了。將數組轉換至表是爲了更好地理解這算法,算法本身還是使用數組進行排序。

例如,假設有這樣一組數[54 26 93 17 77 31 44 55 20],如果我們以步長(gap=4)爲4開始進行排序,我們可以通過將這列表放在有4列的表中來更好地描述算法,這樣他們就應該看起來是這樣(豎着的元素是步長組成):

上面是原始需要排序的數列,現在把gap設置4,也就是這樣第一次分組。

上面54下標是0,0+4 = 4, 所以54同組的下一個元素是下標爲4的元素也就是77,同理77下一個元素是20.依次類推,第二組元素是26 31, 第三組元素是93 44, 第四組元素是17 55. 希爾排序就是把每一組元素採用直接插入排序算法,對元素位置交換,進行排序,第一次希爾排序之後數列爲。

得到新數列,把gap設置成2,進行再一次分組。

然後把上面兩組元素,採用直接插入排序算法,交換元素位置,形成新數列。

對上面這個數列,把gap設置爲1,再次採用直接插入排序,最終排序完成。

 

根據數列元素個數,關鍵在於如何設置這個gap的值,這個值決定了最優情況下算法的時間複雜度。

 

 

3.希爾排序代碼實現

python代碼實現

# coding:utf-8


def shell_sort(alist):
    """希爾排序"""

    n = len(alist)
    # gap取值n的一半
    gap = n // 2
    # 最後一次gap需要等於1
    while gap > 0:
        for i in range(gap, n):
            j = i
            while j > 0:
                if alist[j] < alist[j - gap]:
                    alist[j], alist[j - gap] = alist[j - gap], alist[j]
                    j -= gap
                else:
                    break
        # 縮短 gap步長
        gap //= 2


if __name__ == "__main__":
    alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print(alist)
    shell_sort(alist)
    print(alist)

運行結果:

[54, 26, 93, 17, 77, 31, 44, 55, 20]
[17, 20, 26, 31, 44, 54, 55, 77, 93]

時間複雜度分析,如果gap一上來就是等於1,那就是前面一篇的直接插入排序算法,所以最壞時間複雜度是O(n^2), 至於什麼情況下有最優的時間複雜度,這個需要數學方面知識去計算gap的取什麼值纔有最優解。

 

java代碼實現過程

import java.util.Arrays;

public class ShellSort {

    public static void main(String[] args){
        int[] arr = {54, 26, 93, 17, 77, 31, 44, 55, 20};
        System.out.println(Arrays.toString(arr));
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void shellSort(int[] arr){
        int gap = arr.length/2;
        while( gap > 0){
            for(int i = gap; i < arr.length; i++){
                int j = i;
                //插入排序算法核心部分
                while(j > 0){
                    if(  j >= gap && arr[j] < arr[j - gap]){
                        int tmp = arr[j];
                        arr[j] = arr[j - gap];
                        arr[j - gap] = tmp;
                        j -= gap;
                    }else{
                        break;
                    }
                }
            }
            gap /= 2;
        }
    }
}

注意上面的if判斷條件中多寫了一個j 和gap的判斷,不然報空指針錯誤,運行結果:


[54, 26, 93, 17, 77, 31, 44, 55, 20]
[17, 20, 26, 31, 44, 54, 55, 77, 93]

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章