1. 程式人生 > >希爾排序詳解

希爾排序詳解

基本概念:

希爾排序按其設計者希爾(Donald Shell)的名字命名,它是一種基於插入排序的快速排序演算法,要了解希爾排序,必須先掌握插入排序的原理與實現

希爾排序通過將比較的全部元素分為幾個區域來提升插入排序的效能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步。然後演算法再取越來越小的步長進行排序,演算法的最後一步就是普通的插入排序,但是到了這步,需排序的資料幾乎是已排好的了(此時插入排序較快)。

步長的選擇是希爾排序的重要部分。只要最終步長為1任何步長序列都可以工作(且步長要小於陣列長度)。演算法最開始以一定的步長進行排序。然後會繼續以一定步長進行排序,最終演算法以步長為1進行排序。當步長為1時,演算法變為插入排序,這就保證了資料一定會被排序。

關於希爾排序的時間複雜度,與步長息息相關:


過程如圖:


簡單例子分析:

待排序陣列:
{6, 5, 3, 1, 8, 7, 2, 4, 9, 0}

第一次步長h=4,
那麼陣列按照步長可以拆分成4個小陣列([0]6的意思是下標[0]的值為6)

{[0]6, [4]8, [8]9}
{[1]5, [5]7, [9]0}
{[2]3, [6]2}
{[3]1, [7]4}

對這4個小陣列分別進行插入排序後,4個小陣列變成:
{[0]6, [4]8, [8]9}
{[1]0, [5]5, [9]7}
{[2]2, [6]3}
{[3]1, [7]4}
合併起來就是:{6, 0, 2, 1, 8, 5, 3, 4, 9, 7}

第二次步長h=1,
那麼陣列按照步長只有1個數組了
{6, 0, 2, 1, 8, 5, 3, 4, 9, 7}

對這個陣列進行一次插入排序後,最終順序就成為:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

原始碼:

#include <stdio.h>

void shell_sort(int arr[], int size)
{
    if (arr == NULL)
        return ;
    int h = 1; /* 關於步長,取值沒有統一標準,必須小於size,最後一次步長要為1 */
    
    /* 計算首次步長 */
    while (h < size/3)
        h = 3*h + 1;

    int i, j, temp;
    while (h >= 1) {
        for (i = h; i < size; ++i) {
            /* 將a[i]插入到a[i-h]、a[i-2h]、a[i-3h]...中 */ 
            for (j = i; j >= h && (arr[j] < arr[j-h]); j -= h) {
                temp = arr[j];
                arr[j] = arr[j-h];
                arr[j-h] = temp;
            }
        }

        /* 每輪內迴圈後輸出陣列的現狀 */
        int k;
        printf("the step=%d : ", h);
        for (k = 0; k < size; ++k) {
            printf("%d ", arr[k]);
        }
        printf("\n");

        /* 計算下一輪步長 */
        h = h / 3;
    }

}

int main()
{
    int arr[] = {6, 5, 3, 1, 8, 7, 2, 4, 9, 0};
    int size = sizeof(arr) / sizeof(int);

    //sort
    shell_sort(arr, size);

    //print
    int i = 0;
    for (i; i < size; ++i) {
        printf("%d\n", arr[i]);
    }

    return 0;
}

編譯執行:


End;