# 排序法小結與複雜度比較 再次複習相關術語解釋: * 穩定(Stable):相同鍵值的資料,排序後順序和排序前一樣; * 不穩定(Unstable):相同鍵值的資料,排序後順序不一定和排序前一樣; * 內部排序(Internal Sort):所有排序操作都在內部記憶體中完成; * 外部排序(External Sort):由於數據太大,因此把數據放在外部記憶體中,而排序通過外部記憶體的數據傳輸才能進行; * 時間複雜度(Time Complexity):一個演算法執行所耗費的時間。 * 空間複雜度(Space Complexity):運行完一個程式所需記憶體的大小。 * 原地置換(In-place):使用資料原來的資料結構(陣列)進行排序,不需使用暫存的輔助資料結構,不佔用額外記憶體。 * 非原地置換(Out-place):需使用暫存的輔助資料結構,佔用額外記憶體。 ## 常見排序法重點整理 * 以下假設由小到大排序。 * 陣列長度設為 `arr.length = n` * 速度測試同為八萬筆隨機產生數據,執行時間依本人電腦為主。 ### 泡沫排序法 相鄰元素兩兩比較,若右邊的數比左數大就交換,否則不動。 * 需排序 `n - 1` 輪 * 時間複雜度為 O(n^2^) * 速度測試為 7 秒 ![](https://i.imgur.com/iG93iYA.png) ### 選擇排序法 從未排序的元素中找到最小值將之與前面的值做交換。 * 需排序 `n - 1` 輪 * 時間複雜度為 O(n^2^) * 速度測試為 1 秒 ![](https://i.imgur.com/6Djt5jk.png) ### 插入排序法 將待排序元素看作一個有序表和一個無序表,開始時有序表只包含一個元素(設為`arr[0]`),無序表中包含 `n-1`個元素,排序過程中每次從無序表中取出第一個元素,把它依次與有序表元素進行比較,將它插入到有序表中的適當位置。 * 需排序 `n - 1` 輪 * 第 n 輪需比較 n 個數 * 時間複雜度為 O(n^2^) * 速度測試為 1400 ms => 1.4 s 秒 ![](https://i.imgur.com/RkUNTPg.png) ### 希爾排序法 是插入排序的一種更高效的改進版本。 步長設為陣列長度除以2,且每輪步長都會再除以2,直至以1為步長進行排序。 假設 n 長度為 10,第一輪排序步長為 5,則依次比較 arr[0] arr[5] , arr[1] arr[6] ... * 時間複雜度為 O(n^1.3^) * 速度測試為14 ms ### 快速排序 是對泡沫排序的一種改進。 先從陣列中找出一個基準值,將比基準值小的數值置於基準值的左邊,比基準值大的數值放在基準值的右邊,再對左右子序列重複以上步驟。 動畫理解:https://www.bilibili.com/video/BV1at411T75o * 時間複雜度為`O(nlogn)` * 速度測試為20 ms ![](https://i.imgur.com/w0RxiR1.png) ### 合併排序 採用經典的分治策略(Divide and Conquer) 會先將陣列每次拆分成兩組同大小的子陣列,直到每組都只有一個元素再進行排序後合併。 會需要三個索引: ```java mid = (left + right) / 2 i = 0 // 左子序列第一個元素 j = mid + 1 // 右子序列第一個元素 ``` 和一個中轉陣列 `temp` i,j 比較,誰比較小就放入 temp,直到左或右某一子序列遍歷結束,則將沒遍歷結束的子序列剩下元素全部放進 temp 陣列中,最後將 temp 陣列 copy 回 arr 陣列。 * 時間複雜度為`O(nlogn)` * 速度測試為10~11 ms ![](https://i.imgur.com/KJuTraA.png) ### 基數排序 又稱 **桶排序(Bucket Sort)**,將整數按位數切割成不同的數字,然後按每個位數分別比較。 * 屬於穩定性的排序。 * 基數排序是經典的空間換時間的方式,佔用記憶體很大,當對海量數據排序時容易造成OutOfMemoryError。 * 時間複雜度為`O(n*k)` k 為 key 的範圍,以 10 為基數,就是 0-9 之間 k=10 * 速度測試為15 ms ![](https://i.imgur.com/dbx8Ak2.png) ## 時間複雜度 ![](https://i.imgur.com/zczFmPc.png) * n:數據規模。 * k:"桶"的個數。 ## 排序耗時 同樣**八萬筆**隨機產生資料,以本人電腦為主,各測試3-5次取平均值,目前測試的結果是: * 堆積排序:7.6 ms * 合併排序:10~11 ms * 希爾排序(移動):14 ms * 基數排序:15 ms * 快速排序:20 ms * 選擇排序:634 ms => 0.6 s * 插入排序:1400 ms => 1.4 s * 泡沫排序:7 s 在本人電腦執行八萬筆亂數資料排序,所耗費時間順序為: :::warning 堆積(7.6 ms) < 合併(10 ms) < 希爾(14 ms) < 基數(15 ms) < 快速(20 ms) < 選擇(634 ms) < 插入(1400 ms) < 泡沫(7s) :::