快速排序--荷蘭國旗三色優化

快速排序作爲十大經典排序之一,大家應該有所耳聞,對它的優化網上也有不少,比如隨機數快排,中位數快排,甚至有的走火入魔去融合插入排序或多線程……今天我們不成魔,做個優化針對於重複的數字,普通快排一次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));
		
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章