1. 程式人生 > >資料結構與演算法 排序演算法 快速排序【詳細步驟圖解】

資料結構與演算法 排序演算法 快速排序【詳細步驟圖解】

# 快速排序 給定一個序列:`22 33 49 47 33' 12 68 29` 進行快速排序 ## 主要思想 - 從序列中,任選一個記錄`k`作為**軸值 `pivot`** 選擇策略: - 第一個元素 - 最後一個元素 - 中間元素 - 隨機選擇 - 將剩餘的元素,分割成 **左子序列 L** 和 **右子序列 R** - L 中所有元素都 < k, R 中所有元素都 > k - 對 L 和 R遞迴進行快排,直到子序列中有 0 個 或者 1 個元素,退出 ## 圖解 初始陣列: 選定`47`為軸值`pivot` ![image-20200522200024944](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522200026.png) `pivot`與最後一個值`29`進行交換(**==把`pivot`放到最後面==**) ![image-20200522200152016](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522200153.png) **接下來,以`pivot=47`為界,分成左子序列 `L `和右子序列 `R`** 比`47`大的都放在右邊,比`47`小的都放在左邊(用的交換) **遍歷陣列** - 兩個指標`left`和`right` - 當`left != right`的時候 - 若`arr[left]`的,小於等於`pivot`,且`left < right`的時候,`left`右移 - 如果`left`和`right`未相遇,把`left`的值賦給`right`對應的值 - `arr[right] = arr[left]` - `left`指標停止移動,輪到`right`移動 - 當`arr[right]`的值,大於等於`pivot`,且`right > left`的時候,`right`左移 - 如果`left`和`right`未相遇。把`right`的值賦給`left`對應的值 - `arr[left] = arr[right]` - `right`指標停止移動,輪到`left`移動 - **注意:軸值用`pivot`儲存** ### 第一輪分割序列 ![image-20200522213014584](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522213015.png) `pivot=47`和最後一個值互換 ![image-20200522205447912](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522205454.png) `22 <= 47`,`left`向右移動 ![image-20200522205623534](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522210121.png) `33 <= 47`,`left`向右移動 ![image-20200522205648022](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522210109.png) `49 >
47`,不滿足`arr[left] <= pivot` 把`left`的值賦給`right` `arr[right] = arr[left]` ![image-20200522210144784](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522210148.png) **賦值過後,`left`不動,`right`向左移動** ![image-20200522211704648](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522211706.png) `68 >= 47`,right向左移動 ![image-20200522211823993](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522211830.png) `12 < 47`,不滿足`arr[right] >= pivot` 把`right`的值賦給`left` `arr[left] = arr[right]` ![image-20200522211941103](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522211942.png) **賦值過後,`right`不動,`left`向右移動** ![image-20200522212237784](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522212242.png) `29 < 47`,`left`向右移動 ![image-20200522212313112](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522212314.png) `33' < 47`,`left`向右移動 ![image-20200522215707618](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522215708.png) 向右移動後,`left == right`,退出迴圈 將`pivot`賦給`arr[left]` ![image-20200522215538138](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522215539.png) **至此,第一輪分割序列完成** ### 第二輪分割序列 --- 左子序列 經過第一輪分割,`47`左邊的是左子序列,右邊是右子序列 **第二輪對左子序列分割,選擇中間值作為`pivot`** ![image-20200522223949257](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_氣泡排序20200522223950.png) `12和33'`進行交換 ![image-20200522220137128](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522220142.png) `22 >
12`,不滿足`arr[left] <= pivot` 把`arr[left]`賦給`arr[right]` `arr[right] = arr[left]` ![image-20200522220327264](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522220328.png) **賦值過後,`left`不動,`right`向左移動** `29、33'、33`都比`12`大,所以`right`一直移動到下圖位置 ![image-20200522220446889](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522220448.png) `33 > 12`,`right`繼續向左移動 ![image-20200522220515361](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522220516.png) 此時`right == left`,終止迴圈 把`pivot`賦給`arr[left]` ![image-20200522220557616](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522220559.png) **至此,左子序列1也分割完成了** #### 小結 快排就是一個遞迴的過程,分割得到左子序列 再對左子序列進行快排分割,得到左子序列的左子序列.... 處理完左邊,再去處理右邊的右子序列 ### 第三輪分割序列 --- 右子序列 右子序列只有`47、68、49`,選擇`48`作為**軸值 pivot** ![image-20200522220854832](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522220929.png) `pivot`和最後一個值交換 ![image-20200522221012928](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522221014.png) `47、49`都比`pivot=68`小,`left`一直向右移動,直到`left == right` ![image-20200522221104705](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522221106.png) 分割之後,只剩下左子序列:`47、49` `47、49`,選`49`作為軸值,得到左子序列`47` 子序列只剩下一個元素`47`,就不必排序了,右邊排序結束 **結果:`47、49、68`** ## C++實現 **選擇中間的值作為軸值** ```C++ #include #include #include #include #include #include #include #include #include using namespace std; /** * * @param arr 待分割的序列 * @param left 左邊界 * @param right 右邊界 * @return 分割後軸值的位置 */ template int PartitionArr(vector& arr, int left, int right) { T temp = arr[right]; while (left != right) { while (arr[left] <= temp && left < right) { left++; } if (left < right) { arr[right] = arr[left]; // 賦值後,left不動,right向左移 right--; } while (arr[right] >
= temp && right > left) { right--; } if (left < right) { arr[left] = arr[right]; // 賦值後,right不動,left向右移 left++; } } // 當left == right,把軸值放回left上 arr[left] = temp; return left; } /** * * @param arr 待排序陣列 * @param left 左邊界 * @param right 右邊界 */ template void quickSort(vector& arr, int left, int right) { // 子序列剩下0或1個元素,排序結束 if (right <= left) { return; } // 選擇陣列中間作為軸值 int pivot = (left + right) / 2; // 把軸值放到陣列最後面 swap(arr[right], arr[pivot]); // 分割後軸值的位置 // 分割後,左邊值 < 軸值 < 右邊值 pivot = PartitionArr(arr, left, right); quickSort(arr, left, pivot - 1); quickSort(arr, pivot + 1, right); } int main() { vector arr = { 22,33,49,47,33,12,68,29 }; for (auto& i : arr) { cout << i << ' '; } cout << endl << endl; quickSort(arr, 0, arr.size() - 1); for (auto& i : arr) { cout << i << ' '; } cout << endl << endl; system("pause"); return 0; } ``` ![image-20200522223306345](https://gitee.com/Pineapple47/DataStructureAndAlgorithm/raw/master/排序_快速排序20200522223308.png) ## 總結 - 快排是不穩定的排序演算法 - `33 33'`排序後可能變成`33' 33` - 時間複雜度: - 平均:$O(Nlog_N)$ - 最差:$O(N^2)$,退化為氣泡排序 - 空間複雜度: - 遞迴呼叫消耗棧空間 - 最優:$O(log_N)$ - 最差:$O(N)$,退化為冒