希爾排序是一種基於插入排序(參見http://blog.csdn.net/a19881029/article/details/21624081)的排序算法java
插入排序的問題是,若是一個極小的數據處於數組的右端,那麼在數組排序過程當中,每一個數據項平均須要移動N/2次,全部數據大約須要移動N*N/2次,時間複雜度爲O(N*N),也就是說在排序過程當中,數據項須要移動的次數過多,嚴重下降了排序的效率算法
如何下降排序過程當中數據項的移動次數呢?若是原始數組是一個基本有序的數據,那麼平均每一個數據項只需移動1次,或不須要移動,全部數據大約只需移動N次,時間複雜度將變爲O(N)數組
因此提升插入排序效率的關鍵就在於如何將原始數組變爲一個基本有序的數組,這就須要在原有插入算法的基礎上作一點改進this
希爾排序經過加大插入排序中數據項之間的間隔,而且對這些有間隔的數據項進行插入排序,使數據項能夠大跨度的移動,當數組排過一次後,縮小數據項之間的間隔再次進行排序,依次進行下去。最後一次排序數據項之間的間隔爲1,是一次普通的插入排序spa
一個長度爲10的數組升序排序,首先進行間隔爲4的插入排序.net
此時數組已經基本有序(數組項離其最終位置不超過2個單元),對最後的結果再進行普通的插入排序,這時大約只須要O(N)的時間code
當間隔比較大時,每次排序須要移動的數據項較少,可是移動距離較大,這是很是有效率的,當間隔變小時,雖然每次排序須要移動的數據項不少,但此時數組已經基本有序,這對插入排序來講也是很是有效率的blog
排序間隔:排序
通常經過公式h=h*3+1獲得間隔序列,在進行希爾排序時,使用該序列的逆向序列做爲間隔,固然初始間隔不能超過數組的長度ip
固然也可使用其它的間隔序列,只是要保證逐漸減少的間隔最後必定要等於1,這樣能夠保證希爾排序過程當中最後一次排序是一次普通的插入排序
使用某些間隔序列會使運行時間下降爲O(N*N),爲了不這種狀況,須要保證間隔序列中的各個數字之間互質,這樣可使每一次排序更有可能保持前一次排序已經排好的效果
固然間隔序列必須能夠被簡單高效的計算出來,避免影響算法的執行效率
public class ShellSort { private int[] data; public ShellSort(int[] data){ this.data = data; } public void sort(){ int num = data.length; int h = 1; while(h*3 + 1 < num) h = h*3 + 1; while(h > 0){ System.out.println(h); for(int i = h ; i < data.length ; i++){ int tmp = data[i]; int insertPoint = i; while(insertPoint - h >= 0 && data[insertPoint-h] > tmp){ data[insertPoint] = data[insertPoint-h]; insertPoint -= h; } data[insertPoint] = tmp; } h = (h - 1)/3; } } public void display(){ for(int i = 0 ; i < data.length ; i++){ System.out.print(data[i] + " "); } System.out.print("\n"); } public static void main(String[] args) { int[] data = {7, 10, 1, 9, 2, 5, 8, 6, 4, 3}; ShellSort ss = new ShellSort(data); ss.sort(); ss.display(); } }
4 1 1 2 3 4 5 6 7 8 9 10
希爾排序的效率:
除了某些特殊的狀況,沒法在理論上分析希爾排序的效率
有不少基於實驗的評估,希爾排序的效率大約在O(N3/2)和O(N7/6)之間,比時間複雜度爲O(N*N)的排序方式要快不少
希爾排序在最壞狀況下的執行效率和平均狀況下的執行效率相比沒有差太多,不像快速排序在最壞條件下,執行效率會很是差,而且不像表排序和歸併排序那樣須要額外的存儲空間