希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序算法。html
希爾排序的基本思想是:先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,待整個序列中的記錄"基本有序"時,再對全體記錄進行依次直接插入排序。git
希爾排序實質上是一種分組插入方法。它的基本思想是:對於n個待排序的數列,取一個小於n的整數gap(gap被稱爲步長)將待排序元素分紅若干個組子序列,全部距離爲gap的倍數的記錄放在同一個組中;而後,對各組內的元素進行直接插入排序。 這一趟排序完成以後,每個組的元素都是有序的。而後減少gap的值,並重復執行上述的分組和排序。重複這樣的操做,當gap=1時,整個數列就是有序的。github
如今有一個序列:{h1,h2,… hn}:算法
1. 設先取定一個小於n的整數 di 做爲一個增量,全部間隔爲 di 的記錄放在同一個子序列,而後在每一個子序列內進行直接插入排序。shell
2. 而後取第二個增量d2(<d1),重複上述的分組和排序,直至所取的增量dt = 1 (d1>d2> … >dt-1>dt),即全部記錄放在同一組中進行直接插入排序爲止。數據結構
增量 di 的值是隨便取的,可是這並不表明增量 di 的值能夠隨便取。也就是說,在定義增量 di 時,定義增量的序列爲:dn>dn−1>...>d1=1,通常使用Shell建議的序列:di=n/2。post
下面以數列{80,30,60,40,20,10,50,70}爲例,演示它的希爾排序過程。性能
第1趟:(gap=4)學習
當gap=4時,意味着將數列分爲4個組: {80,20},{30,10},{60,50},{40,70}。 對應數列: {80,30,60,40,20,10,50,70}
對這4個組分別進行排序,排序結果: {20,80},{10,30},{50,60},{40,70}。 對應數列: {20,10,50,40,80,30,60,70}url
第2趟:(gap=2)
當gap=2時,意味着將數列分爲2個組:{20,50,80,60}, {10,40,30,70}。 對應數列: {20,10,50,40,80,30,60,70}
注意:{20,50,80,60}實際上有兩個有序的數列{20,80}和{50,60}組成。
{10,40,30,70}實際上有兩個有序的數列{10,30}和{40,70}組成。
對這2個組分別進行排序,排序結果:{20,50,60,80}, {10,30,40,70}。 對應數列: {20,10,50,30,60,40,80,70}
第3趟:(gap=1)
當gap=1時,意味着將數列分爲1個組:{20,10,50,30,60,40,80,70}
注意:{20,10,50,30,60,40,80,70}實際上有兩個有序的數列{20,50,60,80}和{10,30,40,70}組成。
對這1個組分別進行排序,排序結果:{10,20,30,40,50,60,70,80}
希爾排序是按照不一樣步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,因此插入排序的元素個數不多,速度很快;當元素基本有序了,步長很小,插入排序對於有序的序列效率很高。
希爾排序的時間複雜度與增量(即,步長gap)的選取有關。例如,當增量爲1時,希爾排序退化成了直接插入排序,此時的時間複雜度爲O(N²),而Hibbard增量的希爾排序的時間複雜度爲O(N3/2)。
希爾排序時間複雜度的下界是O(n*log2n)。
希爾排序過程當中用到了直接插入排序,須要臨時變量存儲待排序元素,所以空間複雜度爲O(1)。
希爾排序是不穩定的算法,對於相同的兩個數,可能因爲分在不一樣的組中而致使它們的順序發生變化。
不能歸位,好比最後一個數爲最小值,那麼全部的值都未在最終的位置。
那麼希爾排序算法爲何比直接插入排序好呢?
假設如今要對10個元素進行排序。
若是使用直接插入排序,大約花費的時間爲 =10^2=100。
若是使用希爾排序,當增量di = 5時,分爲5組,時間爲5×2^2=20。當增量di = 2時,分爲2組,時間爲2×5^2=50。當di = 1時,分爲1組,此時幾乎是有序的,時間約爲10,而後把每一個分組的時間都加起來,總的時間約爲20+50+10=80。
public int[] shellSort(int[] sourceArray) throws Exception { // 對 arr 進行拷貝,不改變參數內容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int gap = arr.length/2; while (gap > 0) { for (int i = gap; i < arr.length; i++) { int tmp = arr[i]; int j = i - gap; while (j >= 0 && arr[j] > tmp) { arr[j + gap] = arr[j]; j -= gap; } arr[j + gap] = tmp; } gap = (int) Math.floor(gap / 2); } return arr; }