1、算法由來
我們知道,簡單插入排序在數據規模較小和數據基本有序時十分高效。
那如果數據規模較大,並且無序時,有什麼樣的方法可以提高簡單插入排序的效率呢?
希爾排序正是爲此而生,它通過構造(1)小規模數據和(2)讓數據基本有序的條件來達到高效排序。
2、算法思想
希爾排序的基本思想是分組進行插入排序。即每次根據增量序列(從大到小)中的一個增量值,將數組劃分成若干組,然後對這若干組分別進行簡單插入排序。直到數組有序位置。
以數組array=[5,7,8,3,1,2,4,6]爲例,希爾算法的具體過程如下:
(1)首先它把較大的數據集合分割成若干個小組(邏輯上分組),然後對每一個小組分別進行插入排序,此時,插入排序所作用的數據量比較小(每一個小組),插入的效率比較高。
可以看出,他是按下標相隔距離爲4分的組,也就是說把下標相差4的分到一組,比如這個例子中a[0]與a[4]是一組、a[1]與a[5]是一組…,這裏的差值(距離)被稱爲增量
(2) 每個分組進行插入排序後,各個分組就變成了有序的了(整體不一定有序)
此時,整個數組變的部分有序了(有序程度可能不是很高)
(3) 然後縮小增量爲上個增量的一半:2,繼續劃分分組,此時,每個分組元素個數多了,但是,數組變的部分有序了,插入排序效率同樣比高。
同理對每個分組進行排序(插入排序),使其每個分組各自有序
(4) 最後設置增量爲上一個增量的一半:1,則整個數組被分爲一組,此時,整個數組已經接近有序了,插入排序效率高
同理,對這僅有的一組數據進行排序,排序完成
3、代碼實現
希爾排序分組實現:
template<typename T>
void shellSort(T* array,int len){
//對數組進行分組,最開始的增量(gap)爲數組長度的一半;
for (int gap = len / 2; gap >= 1; gap = gap / 2){
//對以gap爲增量形成的分組進行插入排序
for (int i = gap; i < len; i++){
insertSort(array, i, gap);
}
}
}
組內進行插入排序實現:
template<typename T>
void insertSort(T* array, int i, int gap){
/*
將array[i]插入到所在分組的正確位置上;
array[i]所在的分組:
...array[i-gap*2],array[i-gap],array[i],array[i+gap],array[i+gap*2]...
*/
/*寫法1:
T inserted = array[i];
int j = i - gap;
while (j >= 0 && inserted < array[j]){
array[j + gap] = array[j];
j -= gap;
}*/
/*寫法2:*/
T inserted = array[i];
int j;
for (j = i - gap; j >= 0 && inserted < array[j]; j -= gap){
array[j + gap] = array[j];
}
array[j + gap] = inserted;
}
4、算法分析
- 時間複雜度:希爾排序的複雜度和增量序列是相關的。
這種序列並不是很好的增量序列,使用這個增量序列的時間複雜度(最壞情形)是,Hibbard提出了另一個增量序列,這種序列的時間複雜度(最壞情形)爲,Sedgewick提出了幾種增量序列,其最壞情形運行時間爲,其中最好的一個序列是。 - 空間複雜度:。
- 穩定性:簡單插入雖是穩定的,但希爾排序是不穩定的,詳細原因見下圖。