排序演算法:希爾排序演算法實現及分析
阿新 • • 發佈:2018-12-23
希爾排序演算法介紹
希爾排序是D.LShell 與1957年提出來的一種排序演算法,在這之前排序演算法的時間複雜度都是O(n^2),希爾排序演算法是突破這個時間複雜度的第一批演算法之一。我們知道直接插入排序演算法(不知道的請看:排序演算法:直接插入排序演算法實現及分析),在某些情況下的效率是很很高的,1.當我們的記錄本身就是基本有序(小的關鍵字基本在前面,大的基本在後面)的,我們只需要少量的插入操作,就可以完成排序。2.還有就是記錄數比較少時,直接插入的優勢也是比較明顯的。希爾排序可以說就是直接插入排序的升級版,希爾排序通過構造上面的2個條件 使排序更快的實現。希爾排序是怎樣來構造這2個條件的呢
希爾排序演算法實現
//希爾排序 升序 //根據插入排序的原理,將原來的一個大組,採用間隔的形式分成很多小組,分別進行插入排序 //每一輪結束後 繼續分成更小的組進行 插入排序,直到分成的小組長度為1,說明插入排序完畢 void ShellSort_Up(int* arr,int length) { int increase = length; int i, j, k, temp; do { increase = increase / 3 + 1;//每個小組的長度,也就是增量 //每個小組的第0個元素 for (i = 0; i < increase; i++) { //對每個小組進行插入排序,因為是間隔的形式分組,每個小組的下一個元素為 j+=increse for (j = i + increase; j < length; j += increase) { temp = arr[j];//待插入元素 for (k = j - increase; k >= 0 && temp < arr[k]; k -= increase) { arr[k + increase] = arr[k]; } arr[k + increase] = temp; } } } while (increase>1); }
希爾排序演算法複雜度分析
希爾排序的關鍵並不是隨便分組後各自排序,而是將相隔某個“增量”的記錄組成一個字序列,實現跳躍式的移動,使得排序的效率提高。這的“增量”選取就非常關鍵了。我們採用的是increase/3+1的方式選取。可究竟選取一個什麼樣的值,最好呢?目前還是數學難題,不過科學研究表明,當增量序列為dlta[k] = 2^(t-k+1) - 1 ,0 <=k<=t<=log2(n+1)時,可以獲得不錯的效率,其時間複雜度為O(n^(3/2)),要好於O(n^2)。
完整程式碼
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #include <sys/timeb.h> #define MAXSIZE 10 //交換值 void Swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } //直接插入排序 升序 void InsertSort_Up(int* arr, int length) { //假定第0個元素是有序表,從第1個元素開始往有序表中插入資料 for (int i = 1; i < length; i++) { int temp = arr[i]; int j; for (j = i - 1; j >= 0 && arr[j] > temp; j--) { arr[j + 1] = arr[j];//往前挪 } arr[j + 1] = temp; } return; } //希爾排序 升序 //根據插入排序的原理,將原來的一個大組,採用間隔的形式分成很多小組,分別進行插入排序 //每一輪結束後 繼續分成更小的組進行 插入排序,直到分成的小組長度為1,說明插入排序完畢 void ShellSort_Up(int* arr,int length) { int increase = length; int i, j, k, temp; do { increase = increase / 3 + 1;//每個小組的長度 //每個小組的第0個元素 for (i = 0; i < increase; i++) { //對每個小組進行插入排序,因為是間隔的形式分組,每個小組下一個元素為 j+=increse for (j = i + increase; j < length; j += increase) { temp = arr[j];//待插入元素 for (k = j - increase; k >= 0 && temp < arr[k]; k -= increase) { arr[k + increase] = arr[k]; } arr[k + increase] = temp; } } } while (increase>1); } //希爾排序 降序 void ShellSort_Down(int* arr, int length) { int increase = length; int i, j, k, temp; do { increase = increase / 3 + 1;//每個小組的長度 //每個小組的第0個元素 for (i = 0; i < increase; i++) { //對每個小組進行插入排序,因為是間隔的形式分組,每個小組下一個元素為 j+=increse for (j = i + increase; j < length; j += increase) { temp = arr[j];//待插入元素 for (k = j - increase; k >= 0 && temp > arr[k]; k -= increase) { arr[k + increase] = arr[k]; } arr[k + increase] = temp; } } } while (increase>1); } //列印陣列元素 void PrintArr(int* arr, int length) { for (int i = 0; i < length; i++) { printf("%d ", arr[i]); } printf("\n"); return; } long GetSysTime() { struct timeb tb; ftime(&tb); return tb.time * 1000 + tb.millitm; } int main(int argc, char *argv[]) { srand((size_t)time(NULL));//設定隨機種子 int arr[MAXSIZE] = { 0 }; int arr2[MAXSIZE] = { 0 }; //給每個元素設定一個隨機值 for (int i = 0; i < MAXSIZE; i++) { int num = rand() % MAXSIZE; arr[i] = num; arr2[i] = num; } printf("排序前:\n"); PrintArr(arr, MAXSIZE); printf("希爾排序升序:\n"); ShellSort_Up(arr, MAXSIZE); PrintArr(arr, MAXSIZE); printf("希爾排序降序:\n"); ShellSort_Down(arr, MAXSIZE); PrintArr(arr, MAXSIZE); //long start1 = GetSysTime(); //InsertSort_Up(arr, MAXSIZE); //long end1 = GetSysTime(); //long start2 = GetSysTime(); //ShellSort_Up(arr2, MAXSIZE); //long end2 = GetSysTime(); //printf("直接排序%d個數據 耗費時間%d毫秒\n", MAXSIZE, end1 - start1); //printf("希爾排序%d個數據 耗費時間%d毫秒\n", MAXSIZE, end2 - start2); return 0; }
執行結果檢測
希爾排序正確性校驗
直接插入排序和希爾排序比較
測試10萬個資料,直接插入排序和希爾排序的效率