1. 程式人生 > >排序演算法六 快速排序

排序演算法六 快速排序

1、快速排序基本思想

快速排序時C.R.A.Hoare在1962年提出的一種劃分交換排序。採用分治策略(Divide-and-ConquerMethod) 該方法的基本思想是: (1)先從數列中取出一個數作為基準數 (2)分割槽過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊,獲得一個index位置,將陣列劃分為左右兩部分(挖坑填數+分治法) (3)再對左右區間重複第二步,直到各區間只有一個數。(遞迴實現) 較難理解的地方在於第二步的區間劃分,在此採用挖坑填數的方法。 舉例分析:
int[] arr = {3,7,5,1,6,2,4};
0 1 2 3 4 5 6
3 7 5 1 6 2 4
(1)取第一個數作為基準數。初始化為low = 0;high = 6;priv = arr[low] = 3;    解釋:由於已經將arr[0]中 的資料儲存到priv中,可以看作是在陣列索引為0的位置挖坑了 (2)從high開始向左找到第一個比priv小或等於priv的數。當high=5時,小於priv,將arr[5]挖出賦值給arr[low],即arr[low]=arr[high] = 2,此時索引5的位置是坑,需要找元素來進行填充 (3)從low開始向右找到第一個比priv大或等於priv的數。當low=1時,arr[low] = 7>priv,符合條件則挖出arr[1]賦值給arr[high],即arr[high] = arr[low] = 7;此時索引low=1的位置是坑。 此時陣列變成了
0 1 2 3 4 5 6
2 3 5 1 6 7 4
low = 1,high = 5  priv = 3 後面再重複上面的步驟:先從後往前找第一個小於等於基準元素的,再從前向後找第一個大於等於基準元素的。 從high開始向前找,當high=3,符合條件,將arr[3]挖出,填到上一個坑,即arr[low] =arr[1] = arr[high] = 1,此時arr[3]處為坑 從low開始向後找,當low = 2,符合條件,將arr[2]挖出,填到上一個坑即arr[3] = arr[2] = 5 此時high=3.low=2.再開始找會發現low==high,所以退出。 最後將最後一個坑arr[2]填充,用基準元素。arr[2] = priv = 3; 所得陣列為:
0
1 2 3 4 5 6
2 1 3 5 6 7 4
第一輪用arr[low]將整個陣列分為兩部分,以arr[2]分界,左邊的數字都小於arr[2],右邊數字都大於arr[2]。下一步即是對左右兩個區間重複上述步驟。遞迴操作。

2、快速排序實現

挖坑填數法具體實現: (1)low陣列左邊界,high陣列右邊界,priv = arr[low]將基準數挖出形成一個坑arr[low] (2)high由後向前找到第一個比它小的,找到後挖出此數填到前一個坑arr[low]中 (3)low由前向後找到第一個比它大的,找到後挖出此數,填到前一個坑arr[high]中 (4)再重複執行(2)、(3)步,知道low==high,將基準數priv填入到arr[low]中,返回low,low即為左右兩部分的分界處。 java程式碼實現:                               public int partition(int[] arr,int low,int high)是挖坑填數的具體實現,返回分界點的位置                               public void qSort(int[] arr,int low,int high)是分治方法的具體實現,採用遞迴
public class quickSort {
	/**
	 * @param arr  待排序陣列
	 * @param low  剛開始表示排序範圍的第一個元素。逐漸向右移動,找到第一個比樞紐值val大的
	 * @param high 剛開始表示排序範圍的最後一個元素。逐漸向左移動,找到第一個比樞紐值val小的
	 * @return     返回分割點值所在的位置
	 */
	public int partition(int[] arr,int low,int high){
		int val = arr[low];//設定樞紐元素為每個序列的第一個元素
		while(low<high){  //要迴圈比較
			while(low<high&&arr[high]>=val){
				high--;
			}
			arr[low] = arr[high];
			while(low<high&&arr[low]<=val){
				low++;
			}
			arr[high] = arr[low];
		}
		//low = high,返回樞紐的位置
		arr[low] = val;
		return low;
	}
	
	public void qSort(int[] arr,int low,int high){
		//遞迴呼叫
		int pos;
		if(low<high){
			pos= partition(arr,low, high);
			qSort(arr, low, pos-1);
			qSort(arr, pos+1, high);
		}
	}
	
	public static void main(String[] args){
		int[] arr = {3,7,5,1,6,2,4};
		new quickSort().qSort(arr,0,arr.length-1);
		for(int i = 0;i<arr.length;i++){
			System.out.print(arr[i]+" ");
		}
	}
	
   /**
    *快速排序 被認為在所有同數量級(平均複雜度為O(n*logn))的排序方法中,平均效能最好。
    *選取樞紐元素:但是若初始記錄已經基本有序,這樣每次如果還選擇第一個元素作為樞軸元素,則再通過樞軸劃分子序列時,便會出現“一邊倒”的情況,
    *		   此時快速排序就完全成了氣泡排序,這便是最壞的情況,時間複雜度為O(n*n)。
    *        所以通常樞軸元素的選擇一般基於“三者取中”的原則,即比較首元素、末元素、中間元素的值,取三者中中間大小的那個。
    *快速排序的空間複雜度為O(logn)。
    */
}

3、快速排序特性

時間複雜度 O(n*logn),空間複雜度:O(logn) ,穩定性:不穩定

       快速排序 被認為在所有同數量級(平均複雜度為O(n*logn))的排序方法中,平均效能最好。
選取樞紐元素:但是若初始記錄已經基本有序,這樣每次如果還選擇第一個元素作為樞軸元素,則再通過樞軸劃分子序列時,便會出現“一邊倒”的情況,此時快速排序就完全成了氣泡排序,這便是最壞的情況,時間複雜度為O(n*n)。所以通常樞軸元素的選擇一般基於“三者取中”的原則,即比較首元素、末元素、中間元素的值,取三者中中間大小的那個。