希尔排序(二)
另一種寫法
上一篇博文希爾排序(一)中的代碼是基于希爾排序的原理,“直譯”過來的。還有一種更簡(jiǎn)單的寫法:
void shellsort_2(int a[], int n) { int j, gap;for (gap = n / 2; gap > 0; gap /= 2) { #ifdef PRINT_PROCEDUREprintf("-------- gap = %d--------\n",gap);print_array(a, n,gap); #endiffor (j = gap; j < n; j++)//從數(shù)組第 gap個(gè)元素開始 ,直到 n-1 { if ( a[j - gap] > a[j] )//每個(gè)元素與自己組內(nèi)的數(shù)據(jù)進(jìn)行直接插入排序 { int temp = a[j]; int k = j - gap; while (k >= 0 && a[k] > temp) //&&優(yōu)先級(jí)更低 { a[k + gap] = a[k]; // a[k]向后移動(dòng) ,騰出一個(gè)空位k -= gap; }// 考察空位前面的元素a[k+gap] = temp; //插入 #ifdef PRINT_PROCEDUREprintf("[%d] insert to [%d]\n", j, k+gap);print_array(a, n,gap); #endif }}} }對(duì)希爾排序的改進(jìn)
希爾排序的運(yùn)行時(shí)間依賴于增量序列的選擇。 舉個(gè)例子,如果使用 Shell 建議的序列,假使上一個(gè)增量為 4,那么當(dāng)前的增量就是 2。因?yàn)?是4的因子,所以對(duì)于上一輪已經(jīng)比較過的元素,這一輪會(huì)重復(fù)比較,這就造成了時(shí)間的浪費(fèi)。更好的增量序列選擇是增量序列中的任何2個(gè)元素都是互素的。目前已有學(xué)者提出了一些更有效的增量序列,這里僅展示其中的2個(gè)。
Hibbard (希巴德)序列
Hibbard(希巴德)序列:
1,3,7,...,2k?11,3,7,...,2k?1 (kk 為大于 00 的自然數(shù))
使用 Hibbard 增量的希爾排序,其最壞情形的運(yùn)行時(shí)間為 Θ(n3/2)Θ(n3/2);
其平均情形的運(yùn)行時(shí)間被認(rèn)為是 O(n5/4)O(n5/4)(基于模擬結(jié)果)。
Sedgewick (塞奇威克)序列
已知的最好的增量序列是由 Sedgewick(塞奇威克) 提出的: 1,5,19,41,109,....1,5,19,41,109,.... 這個(gè)序列中的項(xiàng)交替地取自以下2個(gè)序列:
1,19,109,505,2161,...,9?(4k?2k)+11,19,109,505,2161,...,9?(4k?2k)+1 (k=0,1,2,3,...)(k=0,1,2,3,...)
5,41,209,929,3905,...,2k+2(2k+2?3)+15,41,209,929,3905,...,2k+2(2k+2?3)+1 (k=0,1,2,3,...)(k=0,1,2,3,...)
使用 Hibbard 增量的希爾排序平均運(yùn)行時(shí)間猜測(cè)為O(n7/6)O(n7/6),最壞情形為O(n4/3)O(n4/3)。
改進(jìn)后的C語言代碼
void shell_sort_improved(int A[], int N, int inc_seq[]) {int inc = 0; int max_inc_idx = 0; int i,j,k,tmp;while (inc_seq[ max_inc_idx + 1 ] < N) max_inc_idx++; //找到最大的且小于N的那個(gè)增量 for (i = max_inc_idx; i >= 0; --i){ inc = inc_seq[i]; #ifdef PRINT_PROCEDUREprintf("-------- gap = %d--------\n",inc); #endif for (j = inc; j < N; ++j) { if( A[j-inc] > A[j] ){tmp = A[j]; k = j - inc; while (k >= 0 && A[k] > tmp) { A[ k + inc ] = A[k]; // A[k]向后移動(dòng) inc個(gè)位置,騰出一個(gè)空位k -= inc; // 繼續(xù)考察空位前面的元素 } A[k + inc] = tmp; // tmp 插入到k的后面 #ifdef PRINT_PROCEDUREprintf("[%d] insert to [%d]\n", j, k+inc); #endif }} } }最后一個(gè)參數(shù)是增量序列。
完整代碼及測(cè)試
#include <stdio.h> #include <stdlib.h> #include <time.h>#define PRINT_GAP 10 #define ARRAY_LEN 100void print_array(int a[], int len, int gap) // 用于打印數(shù)組 {for(int i=0; i<len; ++i){printf("[%2d]:%2d ", i, a[i]);if((i + 1) % gap == 0)printf("\n");}printf("\n\n"); }void shell_sort_improved(int A[], int N, int inc_seq[]) {int inc = 0; int max_inc_idx = 0; int i,j,k,tmp;while (inc_seq[ max_inc_idx + 1 ] < N) max_inc_idx++; //找到最大的且小于N的那個(gè)增量 for (i = max_inc_idx; i >= 0; --i){ inc = inc_seq[i]; #ifdef PRINT_PROCEDUREprintf("-------- gap = %d--------\n",inc); #endif for (j = inc; j < N; ++j) { if( A[j-inc] > A[j] ){tmp = A[j]; k = j - inc; while (k >= 0 && A[k] > tmp) { A[ k + inc ] = A[k]; // A[k]向后移動(dòng) inc個(gè)位置,騰出一個(gè)空位k -= inc; // 繼續(xù)考察空位前面的元素 } A[k + inc] = tmp; // tmp 插入到k的后面 #ifdef PRINT_PROCEDUREprintf("[%d] insert to [%d]\n", j, k+inc); #endif }} } } int main(void) {printf("\n");int array[ARRAY_LEN]; srand((unsigned int) time(NULL)); //設(shè)置隨機(jī)數(shù)種子// 產(chǎn)生100個(gè)隨機(jī)整數(shù),范圍在 [0, 100]for(int i=0; i<ARRAY_LEN; ++i){array[i] = rand() % 101;}print_array(array,sizeof(array)/sizeof(array[0]),PRINT_GAP);int Sedgewick_seq[] = {1, 5, 19, 41, 109};shell_sort_improved(array, sizeof(array)/sizeof(array[0]),Sedgewick_seq);print_array(array,sizeof(array)/sizeof(array[0]),PRINT_GAP);return 0; }運(yùn)行結(jié)果如下圖:
【完】
參考資料
《數(shù)據(jù)結(jié)構(gòu)與算法分析(原書第2版)》(機(jī)械工業(yè)出版社,2004)
總結(jié)