高級排序比簡單排序要快的多,簡單排序的時間複雜度是O(N^2),希爾(shell)排序大約是O(N*(logN)^2),而快速排序是O(N*logN)。算法
說明:下面以int數組的從小到大排序爲例。shell
希爾(shell)排序小程序
希爾排序是基於插入排序的,首先回顧一下插入排序,假設插入是從左向右執行的,待插入元素的左邊是有序的,且假如待插入元素比左邊的都小,就須要挪動左邊的全部元素,以下圖所示:數組
==>
數據結構
圖1和圖2:插入右邊的temp柱須要outer標記位左邊的五個柱子都向右挪動app
如圖3所示,相比插入排序,希爾排序是這樣作的:對固定間隔的元素作插入排序,而後減少間隔,重複作插入排序,直到間隔減少爲1。ui
==>
spa
圖3和圖4: outer位置和inner-h位置的柱子作插入排序 code
數據量大的圖形看這個過程更容易形象地把握算法特色,如圖5和6,總元素數量等於100:blog
圖5和圖6:間隔分別爲40和13執行完插入排序後的效果
相比簡單插入排序,大間隔地作插入排序有兩個好處:
1、大間隔直接致使須要挪動的數據稀少,且數據挪動的效率高,圖5中一次挪動能夠跨越40個位置;
2、通過前一步大間隔的插入排序後,整個數組從總體上粗略地看已經有了明顯的順序,後一步小間隔的插入排序時,一部分操做是不須要挪動數據的,再次減小了挪動數據的次數。
間隔的序列:間隔的經常使用序列,經過遞歸表示:h=3*h+1。(1,4,13,40,121 ...)
希爾排序的效率:「沒有理論上分析希爾排序的效率的結論,各類基於實驗的評估,估計它的時間級從O(N^(3/2))到O(N^(7/6))」--[1]。
參考代碼:
1 /** 2 * Shell排序能夠說是插入排序的升級版,相比較插入排序, 3 * Shell排序首先使用大間隔、少元素的插入排序,使得每次移動的數據少,可是移動的跨度大; 4 * 而後再使用小間隔,略多數量元素的插入排序,通過上一步插入排序,不少數據已經距離排序位置不遠了, 5 * 這樣第二次插入排序時,須要移動的數據的數量就會減小, 6 * 間隔最終減少到1. 7 */ 8 public static int[] shellSort(int[] input) { 9 int length = input.length; 10 int inner, outer; 11 int temp; 12 13 int h = 1; 14 while (h <= length / 3) { 15 h = h * 3 + 1; 16 } 17 18 while (h > 0) { 19 for (outer = h; outer < length; outer++) { 20 temp = input[outer]; 21 inner = outer; 22 23 while (inner > h - 1 && input[inner - h] >= temp) { 24 input[inner] = input[inner - h]; 25 inner -= h; 26 } 27 input[inner] = temp; 28 } 29 h = (h - 1) / 3; 30 } 31 32 return input; 33 }
快速排序
快速排序算法的策略是這樣的:首先把數組用某個值分爲兩個子數組,且稱這個值爲分組值,一個子數組中的元素均小於分組值,另外一子數組則均大於等於分組值,這裏的子組內並不排序;而後,再分別對兩個子組進行再分組,重複遞歸這個過程,直到最後每兩個元素做爲一組進行再分組,整個數組就排好序了。
分組過程具體以下:同時從左往右和從右往左掃描數組,記掃描標記位爲LP和RP。在LP一邊,若發現元素小於分組值則跳過(即向右移動一位檢查下一個元素),不然等待RP的掃描;RP若發現元素大於等於分組值跳過,直到找到小於分組值的元素,而後LP和RP位置的元素交換,重複這個過程,直到LP和RP相遇。
如圖7,8所示,以11號元素爲分組值,LP停在0號位置,RP跳過10號,停在圖7中的9號位置(粉色柱),而後0號和9號交換,後續重複這個過程。
=>
圖7和圖8:以11號柱做爲分組值,0號和9號柱子將交換
圖9:100個元素的數組通過兩次分組後的效果
分組值的選擇,能夠想見,理想的分組值應該是待分組元素的中值,這樣分組後子組在數量少幾乎是一半對一半,不過找中值無疑增長了算法的工做量。圖7中採用了更簡單的方式,直接選數組最右邊的元素爲分組值,分組結束後,再把這個值交換到LP和RP相遇的位置,假如初始數組是從大到小排序的,這種狀況下,選擇最右邊的元素做爲分組值,其區分度就不好了。更好用的方法是所謂的取首尾中三項數據的中值或者平均值。
經過對算法過程的描述可知,其時間複雜度應該爲:O(N*logN),比簡單排序和希爾排序都要快。
參考程序以下:
1 /** 2 * Time Complexity: O(N*logN) 3 */ 4 public static void quickSort(int[] input, final int left, final int right) { 5 if (left < right) { 6 int partition = partition(input, left, right); 7 quickSort(input, left, partition - 1); 8 quickSort(input, partition, right); 9 } 10 } 11 12 13 public static int median(int i1, int i2, int i3) { 14 int[] arrTmp = {i1, i2, i3}; 15 int min = arrTmp[0]; 16 int max = arrTmp[0]; 17 for (int i = 1; i < arrTmp.length; i++) { 18 min = min < arrTmp[i] ? min : arrTmp[i]; 19 max = max > arrTmp[i] ? max : arrTmp[i]; 20 } 21 return (i1 + i2 + i3) - min - max; 22 } 23 24 public static int partition(int[] input, final int left, final int right) { 25 final int mid = (left + right) / 2; 26 int pivotValue = median(input[left], input[mid], input[right]); 27 28 int leftPtr = left; 29 int rightPtr = right; 30 while (leftPtr < rightPtr) { 31 if (input[leftPtr] < pivotValue) { 32 leftPtr++; 33 continue; 34 } 35 if (input[rightPtr] > pivotValue) { 36 rightPtr--; 37 continue; 38 } 39 int temp = input[leftPtr]; 40 input[leftPtr] = input[rightPtr]; 41 input[rightPtr] = temp; 42 leftPtr++; 43 rightPtr--; 44 } 45 46 // 找出大於pivotalValue的第一個值的位置。 47 if (input[rightPtr] > pivotValue) { 48 return rightPtr; 49 } else { 50 return rightPtr + 1; 51 } 52 }
參考文獻
【1】Java數據結構與算法 Rober Lafore 2nd
說明: 文中的圖片來自文獻【1】附帶的Java applet演示小程序。