快速排序作爲十大經典排序之一,大家應該有所耳聞,對它的優化網上也有不少,比如隨機數快排,中位數快排,甚至有的走火入魔去融合插入排序或多線程……今天我們不成魔,做個優化針對於重複的數字,普通快排一次partition歸位一個數,經過優化後直接歸位所有和它相同的數,即整個區間劃分爲<num,==num,>num的三區間,就像荷蘭國旗那樣所以就叫這個了。
具體思路:劃分爲3個區,小於區,等於區,大於區。i指針從左往右循環,如果i位置數比候選num小,就把小於區右邊一位與它交換,小於區右擴,i++;反之比它大,就把大於區左邊一個與它交換,但i停在原地(因爲交換後的數還不知道大小,需要再次比較);如果相等,則直接i++;當指針i撞上了大於區第一個數,就終止了。看下圖應該好理解:
不管是大於num還是小於num,都會讓小於區往右擴,或大於區往左擴,指針i與小於區之間則是等於區,當i遇到從右往左移動的大於區的時候,就完成了一次partition過程。一次partition把所有num相等的全排完序,並返回它們的下標。java版的代碼如下:
package class03;
import java.util.Arrays;
/**
* @Description
* @Package: class03
* @Author: Ray
* @CreateTime: 2020/7/5 15:07
* @E-mail: [email protected]
*/
public class Test11 {
/**
* 荷蘭國旗partition
*
* @param arr
* @param left
* @param right
* @return 返回數組裏有2個值:[等於區域的左邊界下標,等於區域右邊界下標]
*/
public static int[] netherlandsFlag(int[] arr, int left, int right) {
if (left == right) { //只有一個數的情況
return new int[]{left, right};
}
int i = left; //指針i
int small = left - 1; //小於區初始位置爲left左邊一位
int big = right + 1; //大於區初始位置爲right右邊一位
int num = arr[right]; //取最右的數作爲候選數
while (i < big) { //i遇到大於區才終止
if (num == arr[i]) { //等於的邏輯
i += 1;
} else if (arr[i] > num) { //大於的邏輯,注意交換後,i停在原地等候下一循環比較
swap(arr, big - 1, i);
big -= 1;
} else { //小於的邏輯
swap(arr, small + 1, i);
small += 1;
i += 1;
}
}
return new int[]{small + 1, big - 1}; //返回等於區的範圍下標
}
/**
* 遞歸過程
* @param arr
* @param l
* @param r
*/
public static void process(int[] arr, int l, int r) {
if (l < r) {
swap(arr, l + (int) (Math.random() * (r - l + 1)), r); //生成l~r範圍的隨機數,並與r位置做交換,避免最壞時間複雜度O(n^2)出現
int[] equalSpan = netherlandsFlag(arr, l, r); //等於區域
process(arr, l, equalSpan[0] - 1); //從l到等於區左一位遞歸
process(arr, equalSpan[1] + 1, r); //從等於區右一位到r遞歸
}
}
/**
* 快速排序
* @param arr
*/
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
/**
* 交換
* @param arr
* @param left
* @param right
*/
public static void swap(int[] arr, int left, int right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
public static void main(String[] args) {
int[] arr = {1, 5, 4, 3, 2, 7, 8, 9, 4, 7, 7, 8, 7};
quickSort(arr);
System.out.println(Arrays.toString(arr));
}
}