希爾排序
(1)演算法介紹 希爾排序又叫做縮小增量排序,其本質還是插入排序,只不過是將待排序的序列按照某種規則分成幾個子序列,分別對這幾個子序列進行直接插入排序。這個規則就是增量。 例如,先以增量5來分割序列,即將下標為1,6,11,16........的記錄分成一組,將下標為2,7,12,17,........的記錄分成另外一組等,然後分別對這些組進行直接插入排序,這就是一趟希爾排序。將上面排列好序的整個序列,按照同樣的方式再以增量3分割,在進行一趟希爾排序,最後以增量1分割整個序列,其實就是對整個序列進行一趟直接插入排序。,從而完成整個希爾排序。 注意到增量5、3、1是逐漸縮小的,這就是縮小增量排序的由來。而我們知道,直接插入排序適合於序列基本有序的情況,希爾排序的每趟排序,都會使整個序列變得更加有序,等整個序列基本有序了,再來一趟直接插入排序,這樣會使排序效率更高,這就是希爾排序的意思。
關於直接插入排序可參考:點擊開啟連結
(2)執行流程 下面舉例說明希爾排序的執行過程 原始序列:3 5 2 9 7 8 4 1 6 10 1)以增量5分割序列,得到以下幾個子序列: 子序列1:3 8 子序列2: 5 4 子序列3: 2 1 子序列4: 9 6 子序列5: 7 10 分別對這5個子序列進行直接插入排序,得到: 子序列1:3 8 子序列2: 4 5 子序列3: 1 2 子序列4: 6 9 子序列5: 7 10 一趟希爾排序結束,結果為:3 4 1 6 7 8 5 2 9 10 2)再以增量3分割序列,得到以下幾個子序列: 子序列1:3 6 5 10 子序列2: 4 7 2 子序列3: 1 8 9 分別對這3個子序列進行直接插入排序,得到: 子序列1:3 5 6 10 子序列2: 2 4 7 子序列3: 1 8 9 一趟希爾排序結束,結果為:3 2 1 5 4 8 6 7 9 10 3)最後以增量1分割,即對上面的結果全體進行一次直接插入排序,從而完成整個希爾排序。 最後希爾排序的結果為:1 2 3 4 5 6 7 8 9 10
代碼實現如下:
#include <iostream>#include <vector>using namespace std;/*希爾排序演算法:平均時間複雜度為 O(1);空間複雜度為 O(1) 注意:希爾排序的增量序列的最後一個值一定是1,其次增量序列中的值每一除1之外的公因子*/void shellSort(vector<int>& R){int gap; //用來設定增量for (gap = 5; gap > 0; gap=gap-2) //設定增量{for (int i = 0; i < gap; i++) //對增量之間的值迴圈進行直接插入排序{for (int j = i + gap; j < R.size(); j = j + gap) //對每一組元素進行直接插入排序{int temp = R[j]; //相當於無序序列中的第一個數int k = j - gap; //想當於有序序列中的數while (k >= 0 && R[k] > temp){R[k + gap] = R[k];k = k - gap;}R[k + gap] = temp;}}}}void main(){vector<int> R = { 3, 5, 2, 9, 7, 8, 4, 1, 6, 10 };cout << "排序前:";for (auto x:R)cout << x << " ";cout << endl;shellSort(R);cout << "排序後:";for (auto x:R)cout << x << " ";cout << endl;}
運行結果:
複雜度分析:
(1)時間複雜度
平均時間複雜度為O(nlogn)
(2)空間複雜度
希爾排序的空間複雜度同直接插入排序一樣,為O(1)。
(3)希爾排序模板
/*shell排序模板*//*一種較好的增量選取方式為{1,5,19,41,109,...},該序列中的項或者是9*4i(4的i次方,後同)-9*2i+1的形式,或者是4i-3*2i+1的形式*/template <typename Comparable>void shellsort(vector<Comparable> & a){for (int gap = a.size() / 2; gap > 0; gap /= 2) //增量選取方式(不理想)採用Shell建議的 h(t)=N/2、h(t-1) = h(t)/2、.......{for (int i = gap; i < a.size(); ++i){Comparable tem = std::move(a[i]);int j = i;for (; j >= gap&&tem < a[j - gap]; j -= gap) // j 為什麼要>=gap。因為最前面一個元素為j-gap,所以j最小為gapa[j] = std::move(a[j - gap]);a[j] = std::move(tem);}}}