# 快速排序(Quick Sort) 快速排序是對泡沫排序的一種改進。通過一輪排序將要排序的數據分割成獨立的兩部分,其中一部分的數據都比另外一部分的數據要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個數據變成有序。 ## 基本思想 1. 先從陣列中取出一個數作為基準數。 2. 以基準數做分區,將比基準數大的數放到它的右邊,小於或等於它的數全放到它的左邊。 3. 再對左右區間重複第二步,直到各區間只有一個數。 快速排序法比較難用文字去理解,建議大家可以看這個動畫演示,一下子就能懂: https://www.bilibili.com/video/BV1at411T75o ## 舉例說明 **一、設有一組數據 `[2,10,5,11,34,28,21]` 使用快速排序法由小到大排序** 其過程為: ![](https://i.imgur.com/w0RxiR1.png) 上面的例子使用圖解,下面的例子用文字敘述。 **二、使用快速排序法將陣列 [19,97,9,17,1,8] 由小到大排序** 1. 首先我們需要有 i , j 兩個索引 2. 暫定陣列第一個值,也就是 19 為基準值 3. i 索引由 0 開始由左至右移動,j 索引由 陣列最末尾開始由右至左移動 [19(i), 97, 9, 17, 1, 8(j)] 4. 因為我們是將基準值設為左邊第一個,因此我們比較要從最末尾開始比較。 這個陣列的最末尾為 8,將之與基準值 19 做比較,如果比基準值小,放置於i位置,如果比基準值大則不動。8 < 19,因此 8 要放到 i 位置,此時 i+1 進行下一位數比較。 此時陣列為 [8, 97(i), 9, 17, 1, 19(j)] 5. 此時 i 指向的數為 97,與基準值比較 97 > 19,因此將 97放到 j 位置,此時 j-1,繼續下一位比較。此時陣列為 [8, 19(i), 9, 17, 1(j), 97] 6. 此時 j 指向 1,與基準值比較 1 < 19,因此要將 1 放到 i 位置,然後 i+1繼續進行下一個比較。此時陣列為 [8, 1, 9(i), 17, 19(j), 97] 7. 此時 i 指向 9,與基準值比較 9 < 19,因此9要放到左邊,但9本來就在左邊,所以不用動,但是要記得 i+1 指向下一個數比較。此時陣列還是 [8, 1, 9, 17(i), 19(j), 97] 8. 此時 i 指向 17,與基準值比較 17 < 19,因此依然不動,還是一樣 i+1 指向下一個位置,此時陣列還是 [8, 1, 9, 17, 19(i,j), 97]。 9. 直到此刻,i索引和j索引重疊,指向同一個位置,即將基準值 19 擺在重疊的位置,完成排序。 但是我們能發現 [8, 1, 9, 17, 19, 97] 其實還並沒有完成由小到大排序,我們需要再對基準值左右子序列再繼續以上步驟排序,這邊比基準值 19 小的左子序列為 [8, 1, 9, 17],右子序列為[97],只剩下一個數的序列已然排序,所以我們只需要再對左子序列進行以上步驟排序。 最終得到排序後的陣列為 [1, 8, 9, 17, 19, 97]。 ## 程式碼實現 ```java= package Sort; import java.util.Arrays; public class QuickSort { static int []arr = {19,4,5,21,19,4}; public static void main(String[] args) { System.out.println("原序列為:"+Arrays.toString(arr)); quickSort(arr); } public static void quickSort(int [] arr) { sort(0,arr.length-1); System.out.println("排序後為:"+Arrays.toString(arr)); } /** * * @param left => 設為基準值 * @param right => arr.length - 1 */ public static void sort(int left,int right) { if(left < right) { int i = left; // 由左至右的索引 int j = right + 1; // 由右至左的索引 while(true) { while( i+1 < arr.length && arr[++i] < arr[left]); // 向右找, 直到找到比基準值大的數 while( j-1 >= 0 && arr[--j] > arr[left]); // 向左找, 值到找到比基準值小的數 if( i >= j) break; // 若i,j重疊或i超過j後則退出循環 swap(i , j); } swap(left , j); // 基準點與 j 交換 sort(left , j - 1); // 遞迴排序基準點左子序列 sort(j + 1 , right); // 遞迴排序基準點右子序列 } } public static void swap(int i, int j){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } ``` ## 速度測試 ```java= public static void main(String[] args) { // System.out.println("原序列為:"+Arrays.toString(arr)); int arr[] = new int[80000]; for(int i = 0; i < 80000; i++) { arr[i] = (int)(Math.random() * 80000); } Instant startTime = Instant.now(); quickSort(arr); Instant endTime = Instant.now(); System.out.println("共耗時:" + Duration.between(startTime, endTime).toMillis() + " 毫秒"); } ``` output ```java= 共耗時:20 毫秒 共耗時:17 毫秒 共耗時:27 毫秒 共耗時:15 毫秒 共耗時:21 毫秒 ``` 八萬筆資料使用快速排序法平均排序需耗時 20 ms ## 時間複雜度 假設今天要對一陣列使用快速排序法由小到大排序。 ### 最佳:O(nlogn) 第一個基準值的位置剛好是中位數,將資料平均分成兩等分。 ### 平均:O(nlogn) ### 最差:O(n^5/4^) 當資料的順序恰好為由大到小或由小到大時,有分割與沒分割都一樣。