数据结构与算法(一):排序算法之 - 快速排序(详细步骤图解,附代码)
快速排序
引子:Partition 過程
給定一個數組arr,和一個整數num。請把小于等于num的數放在數組的左邊,大于num的數放在數組的右邊。
要求額外空間復雜度O(1),時間復雜度O(N)
思路
維護一個 小于區,i 從第一個數開始:
- 只要 i 小于 num,就把 i 與 *小于區右側的第一個數 *交換,并將小于區向右擴大一個數
- 如果 i 大于等于 num,將 i++
這樣做的原理依據是:
在 i 的左側,都是看過的數,并且每一次比較,都把比 num 小的數發送到小于區。而 i 右側是沒看過的數,隨著 i++,將來一定會被看。
Partition 過程升級版:荷蘭旗問題
荷蘭器問題的描述,可查看:https://www.jianshu.com/p/356604b8903f
我們將數組劃分為三個區域,即:小于區,等于區,大于區
首先,隨機選擇一個基準數字 num
下標 i 從第一個數字開始,與基準數字 num 比較,分為三種情況:
-
如果 [i] == num,則 i++
-
如果 [i] < num,則讓 [i] 與小于區右側的第一個元素交換,小于區右移一個元素,i++
-
如果 [i] > num,則讓 [i] 與大于區左側的第一個元素交換,大于區右移一個元素,i 留在原地
直到 i 與大于區相撞,即可停止,此時已經完成第一輪比較
此時可以認為,等于區已經在它們正確的位置上。用同樣的方法,遞歸對小于區、大于區分別進行再一次劃分
第一輪排序過程 詳細圖解
快速排序 3.0 (隨機快排 + 荷蘭國旗技巧優化)
在arr[L…R]范圍上,進行快速排序的過程:
1)在這個范圍上,隨機選一個數記為num,
1)用num對該范圍做partition,< num的數在左部分,== num的數中間,>num的數在右部分。假設== num的數所在范圍是[a,b]
2)對arr[L…a-1]進行快速排序(遞歸)
3)對arr[b+1…R]進行快速排序(遞歸)
因為每一次partition都會搞定一批數的位置且不會再變動,所以排序能完成
隨機快排:時間復雜度分析
在arr[L…R]范圍上,進行快速排序的過程:
1)在這個范圍上,隨機選一個數記為num,
1)用num對該范圍做partition,< num的數在左部分,== num的數中間,>num的數在右部分。假設== num的數所在范圍是[a,b]
2)對arr[L…a-1]進行快速排序(遞歸)
3)對arr[b+1…R]進行快速排序(遞歸)
因為每一次partition都會搞定一批數的位置且不會再變動,所以排序能完成
代碼
public static void quickSort(int[] arr) {if (arr == null || arr.length < 2) {return;}process(arr, 0, arr.length - 1); }public static void process(int[] arr, int L, int R) {if (L >= R) return;swap(arr, L + (int) (Math.random() * (R - L + 1)), R); // 隨機選一個數作為基準,放在最右側int[] equalArea = netherlandsFlag(arr, L, R);process(arr, L, equalArea[0] - 1); // 小于區遞歸process(arr, equalArea[1] + 1, R); // 大于區遞歸 }// arr[L...R] 玩荷蘭國旗問題的劃分,以arr[R]做劃分值,分為:小于區,等于區,大于區 public static int[] netherlandsFlag(int[] arr, int L, int R) {if (L > R) {return new int[] { -1, -1 };}if (L == R) {return new int[] { L, R };}int less = L - 1; // 小于區的右邊界,L-1保持基準始終在最右側,或者你可以用獨立的變量記錄它int more = R; // 大于區的左邊界int index = L;while (index < more) {if (arr[index] == arr[R]) {index++;} else if (arr[index] < arr[R]) {swap(arr, index++, ++less);} else {swap(arr, index, --more);}}swap(arr, more, R);return new int[] { less + 1, more }; }public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp; }完整代碼(帶對數器)
package class03;public class Code03_PartitionAndQuickSort {public static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}public static int partition(int[] arr, int L, int R) {if (L > R) {return -1;}if (L == R) {return L;}int lessEqual = L - 1;int index = L;while (index < R) {if (arr[index] <= arr[R]) {swap(arr, index, ++lessEqual);}index++;}swap(arr, ++lessEqual, R);return lessEqual;}// arr[L...R] 玩荷蘭國旗問題的劃分,以arr[R]做劃分值// <arr[R] ==arr[R] > arr[R]public static int[] netherlandsFlag(int[] arr, int L, int R) {if (L > R) {return new int[]{-1, -1};}if (L == R) {return new int[]{L, R};}int less = L - 1; // < 區 右邊界int more = R; // > 區 左邊界int index = L;while (index < more) {if (arr[index] == arr[R]) {index++;} else if (arr[index] < arr[R]) {swap(arr, index++, ++less);} else { // >swap(arr, index, --more);}}swap(arr, more, R);return new int[]{less + 1, more};}public static void quickSort1(int[] arr) {if (arr == null || arr.length < 2) {return;}process1(arr, 0, arr.length - 1);}public static void process1(int[] arr, int L, int R) {if (L >= R) {return;}// L..R partition arr[R] [ <=arr[R] arr[R] >arr[R] ]int M = partition(arr, L, R);process1(arr, L, M - 1);process1(arr, M + 1, R);}public static void quickSort2(int[] arr) {if (arr == null || arr.length < 2) {return;}process2(arr, 0, arr.length - 1);}public static void process2(int[] arr, int L, int R) {if (L >= R) {return;}int[] equalArea = netherlandsFlag(arr, L, R);process2(arr, L, equalArea[0] - 1);process2(arr, equalArea[1] + 1, R);}public static void quickSort3(int[] arr) {if (arr == null || arr.length < 2) {return;}process3(arr, 0, arr.length - 1);}public static void process3(int[] arr, int L, int R) {if (L >= R) {return;}swap(arr, L + (int) (Math.random() * (R - L + 1)), R);int[] equalArea = netherlandsFlag(arr, L, R);process3(arr, L, equalArea[0] - 1);process3(arr, equalArea[1] + 1, R);}// for testpublic static int[] generateRandomArray(int maxSize, int maxValue) {int[] arr = new int[(int) ((maxSize + 1) * Math.random())];for (int i = 0; i < arr.length; i++) {arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());}return arr;}// for testpublic static int[] copyArray(int[] arr) {if (arr == null) {return null;}int[] res = new int[arr.length];for (int i = 0; i < arr.length; i++) {res[i] = arr[i];}return res;}// for testpublic static boolean isEqual(int[] arr1, int[] arr2) {if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {return false;}if (arr1 == null && arr2 == null) {return true;}if (arr1.length != arr2.length) {return false;}for (int i = 0; i < arr1.length; i++) {if (arr1[i] != arr2[i]) {return false;}}return true;}// for testpublic static void printArray(int[] arr) {if (arr == null) {return;}for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}// for testpublic static void main(String[] args) {int testTime = 500000;int maxSize = 100;int maxValue = 100;boolean succeed = true;for (int i = 0; i < testTime; i++) {int[] arr1 = generateRandomArray(maxSize, maxValue);int[] arr2 = copyArray(arr1);int[] arr3 = copyArray(arr1);quickSort1(arr1);quickSort2(arr2);quickSort3(arr3);if (!isEqual(arr1, arr2) || !isEqual(arr2, arr3)) {succeed = false;break;}}System.out.println(succeed ? "Nice!" : "Oops!");} }總結
以上是生活随笔為你收集整理的数据结构与算法(一):排序算法之 - 快速排序(详细步骤图解,附代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 值得收藏的时间复杂度速查表:数据结构操作
- 下一篇: Windows 配置 Github 的