先貼上代碼
1 #include<stdio.h> 2 #define N 12 3 //length統計數組的長度 返回最后元素的下標 4 int length(int a [N]){ 5 for(int i = 0;i<=N;i++){ 6 if(a[i]==0) return i-1; 7 8 } 9 } 10 //打印輸出數組元素 11 void show(int a[N]){ 12 for(int i= 0;i<N;i++){ 13 if(a[i]!=0) printf("%4d",a[i]); 14 15 } 16 } 17 //希爾排序 (插入排序升級版) 18 void shellsort(int a [N]){ 19 int gap = length(a)/2 ; 20 do{ 21 int temp=0; 22 for(int i = gap;i<=length(a);i++){ 23 for(int j = i;j>=gap;j-=gap){ 24 if(a[j-gap]>a[j]){ 25 temp = a[j-gap] ; 26 a[j-gap]=a[j] ; 27 a[j]=temp;} 28 } 29 } printf("%d\n",gap); //打印輸出每次循環時gap的數值 輸出值依次為為4 2 1 30 gap/=2; 31 }while(gap!=0); //這里可以使用for循環也可以使用do while循環 32 } 33 int main(void){ 34 int a [N] ={2,3,467,1,22,3,5,34,4,7} ; //手動生成數組a 35 printf("%d\n",length(a)); 36 shellsort(a); 37 show(a); //輸出 1 2 3 3 4 5 7 22 34 467 結果正確 38 39 return 0 ; 40 }
簡單希爾排序算法需要注意的三個關鍵點在於gap值的選擇,以及數組下標i,j和gap的位置關系,要保證程序在gap==1時算法退化成插入排序
以一個簡單的數組 7 6 3 2 4 1為例循環開始時,gap簡寫為g,以括號表示g,i,j所在數字的位置,初始gap=3,數組下標初始值為0,用[]表示需要交換數值的兩個元素,需要注意的是gap處在最外層循環,當i的循環結束時,gap的值才會改變
[7](j-g) 6 3 [2](g,i,j) 4 1 -->2 [6](j-gap) 3 7(g) [4](i,j) 1 -->2 4 [3](i,j) 7(g) 6 [1](i,j) //從這里第一次i循環結束,gap/2 =1 退化為簡單的插入排序 -->[2](j-g) [4](g,i,j) 1 7 6 3 -->2 [4](g) [1](i) 7 6 3 --> [2](i-g) [1](g,i,j) 4 7 6 3 --> 1 2 4 7 6 3 依此類推-------->>> 1 2 4 6 7 3 ---->> 1 2 3 6 7 4 --> 1 2 3 4 6 7 (這三次迭代執行的都是j循環 i一直指在數組的最后位置)
--------------------------------------------------------------------------------------------------
對於希爾排序的時間復雜度:
希爾算法的時間復雜度與gap的值選取有關
在之前的插入排序算法中已經描述過:對於兩個需要交換元素,它們相距越遠,需要交換的次數就越多,於是衍生出了希爾算法,希望解決這個問題。
但是在希爾算法中有兩個不確定因素,①原始數組的元素個數和每個元素的大小是不確定的 ②gap值的選取是不確定的
理論上每個確定數組都應該有一個最優的gap值選取使得希爾排序的時間復雜度為最小的O(NlogN),對於確定的數組我們也很容易選擇每一次循環的gap使得分組排序無效,希爾排序退化成插入排序
這時的時間復雜度為O(N^2)
所以希爾排序的時間復雜度在O(NlogN) 和O(N^2)之間
