希爾排序演算法(縮小增量排序)及C語言實現
阿新 • • 發佈:2018-12-23
希爾排序,又稱“縮小增量排序”,也是插入排序的一種,但是同前面幾種排序演算法比較來看,希爾排序在時間效率上有很大的改進。
在使用直接插入排序演算法時,如果表中的記錄只有個別的是無序的,多數保持有序,這種情況下演算法的效率也會比較高;除此之外,如果需要排序的記錄總量很少,該演算法的效率同樣會很高。希爾排序就是從這兩點出發對演算法進行改進得到的排序演算法。
上圖中兩兩進行比較,例如 49 和 13 進行比較,13<49,所以交換儲存位置。
希爾排序的過程中,對於分割的每個子表,其各自包含的記錄在原表中並不是相互挨著的,而是相互之間相隔著某個固定的常數。例如上例中第一次排序時子表中的記錄分割的常量為 5,第二次排序時為 3。
通過此種方式,對於關鍵字的值較小的記錄,其前移的過程不是一步一步的,而是跳躍性的前移,並且在最後一次對整表進行插入排序時減少了比較和排序的次數。
執行結果:
4 13 27 38 49 49 55 64 76 97
在使用直接插入排序演算法時,如果表中的記錄只有個別的是無序的,多數保持有序,這種情況下演算法的效率也會比較高;除此之外,如果需要排序的記錄總量很少,該演算法的效率同樣會很高。希爾排序就是從這兩點出發對演算法進行改進得到的排序演算法。
例如無序表希爾排序的具體實現思路是:先將整個記錄表分割成若干部分,分別進行直接插入排序,然後再對整個記錄表進行一次直接插入排序。
{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; }
提示:經過大量的研究表明,所選取的增量值最好是沒有除 1 之外的公因子,同時整個增量陣列中最後一個增量值必須等於 1 ,因為最後必須對整張表做一次直接插入排序演算法。