1. 程式人生 > >希爾排序演算法(縮小增量排序)及C語言實現

希爾排序演算法(縮小增量排序)及C語言實現

希爾排序,又稱“縮小增量排序”,也是插入排序的一種,但是同前面幾種排序演算法比較來看,希爾排序在時間效率上有很大的改進。

在使用直接插入排序演算法時,如果表中的記錄只有個別的是無序的,多數保持有序,這種情況下演算法的效率也會比較高;除此之外,如果需要排序的記錄總量很少,該演算法的效率同樣會很高。希爾排序就是從這兩點出發對演算法進行改進得到的排序演算法。

希爾排序的具體實現思路是:先將整個記錄表分割成若干部分,分別進行直接插入排序,然後再對整個記錄表進行一次直接插入排序。

例如無序表{49,38,65,97,76,13,27,49,55,4}進行希爾排序的過程為:
  • 首先對 {49,13},{38,27},{65,49},{97,55},{76,4} 分別進行直接插入排序(如果需要調換位置也只是互換儲存位置
    ),如下圖所示:

上圖中兩兩進行比較,例如 49 和 13 進行比較,13<49,所以交換儲存位置。
 
  • 通過一次排序,無序表中的記錄已基本有序,此時還可以再進行一次分割,如下圖所示:

  • 經過兩次分割,無序表中已基本有序,此時對整張表進行一次直接插入排序(只需要做少量的比較和插入操作即可),最終希爾排序的結果為:


  希爾排序的過程中,對於分割的每個子表,其各自包含的記錄在原表中並不是相互挨著的,而是相互之間相隔著某個固定的常數。例如上例中第一次排序時子表中的記錄分割的常量為 5,第二次排序時為 3。

通過此種方式,對於關鍵字的值較小的記錄,其前移的過程不是一步一步的,而是跳躍性的前移,並且在最後一次對整表進行插入排序時減少了比較和排序的次數。

一般在記錄的數量多的情況下,希爾排序的排序效率較直接插入排序高。

希爾排序的具體程式碼實現:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 15
typedef struct {
    int key;   //關鍵字的值
}SLNode;
typedef struct {
    SLNode r[SIZE];//儲存記錄的陣列
    int length;//記錄陣列中記錄的數量
}SqList;
//希爾排序的實現函式,其中 dk 表示增值量
void ShellInsert(SqList * L,int dk){
    //從 dk+1 個記錄起,每間隔 dk 個記錄就調取一個進行直接插入排序演算法
    for (int i=dk+1; i<=L->length; i++) {
        //如果新調取的關鍵字的值,比子表中最後一個記錄的關鍵字小,說明需要將該值調換位置
        if (L->r[i].key<L->r[i-dk].key) {
            int j;
            //記錄表中,使用位置下標為 0 的空間儲存需要調換位置的記錄的值
            L->r[0]=L->r[i];
            //做直接插入排序,如果下標為 0 的關鍵字比下標為 j 的關鍵字小,則向後一行下標為 j 的值,為新插入的記錄騰出空間。
            for (j=i-dk; j>0 && (L->r[0].key<L->r[j].key); j-=dk){
                L->r[j+dk]=L->r[j];
            }
            //跳出迴圈後,將新的記錄值插入到騰出的空間中,即完成了一次直接插入排序
            L->r[j+dk]=L->r[0];
        }
    }
}
//希爾排序,通過呼叫不同的增量值(記錄),實現對多個子表分別進行直接插入排序
void ShellSort(SqList * L,int dlta[],int t){
    for (int k=0; k<t; k++) {
        ShellInsert(L, dlta[k]);
    }
}
int main(int argc, const char * argv[]) {
    int dlta[3]={5,3,1};//用陣列來儲存增量值,例如 5 代表每間隔5個記錄組成一個子表,1表示每間隔一個,也就是最後一次對整張表的直接插入排序
    SqList *L=(SqList*)malloc(sizeof(SqList));
    L->r[1].key=49;
    L->r[2].key=38;
    L->r[3].key=64;
    L->r[4].key=97;
    L->r[5].key=76;
    L->r[6].key=13;
    L->r[7].key=27;
    L->r[8].key=49;
    L->r[9].key=55;
    L->r[10].key=4;
    L->length=10;
    //呼叫希爾排序演算法
    ShellSort(L, dlta, 3);
    //輸出語句
    for (int i=1; i<=10; i++) {
        printf("%d ",L->r[i].key);
    }
    return 0;
}
執行結果: 4 13 27 38 49 49 55 64 76 97

提示:經過大量的研究表明,所選取的增量值最好是沒有除 1 之外的公因子,同時整個增量陣列中最後一個增量值必須等於 1 ,因為最後必須對整張表做一次直接插入排序演算法。