1. 程式人生 > >快速排序的實現與分析

快速排序的實現與分析

快速排序應用極廣,效率極高且實現簡單。快排和歸併排序一樣用到了分治的思想。

快排中,最關鍵的操作叫做“切分”,切分使得切分元素v左邊的元素都不大於v,v右邊的元素都不小於v。

設有陣列a[lo......hi], lo、hi分別為陣列的下界和上界,可以假設每次切分出來的元素下標為j,那麼切分操作就返回j的值。那麼以j為界,又可以將[lo........(j-1)],[(j+1)......hi]兩個子陣列再次切分,直到整個a有序。

我們可以指定a[lo]為切分元素v,然後通過陣列元素的比較和移動把v放在合適的位置以保證“切分元素v左邊的元素都不大於v,v右邊的元素都不小於v”這一基本條件。在比較和移動的過程中,我們設定了兩個指標i和j,初始化i指向lo,j指向hi。然後開始第一次掃描,i從lo開始向hi走,當遇見大於v的元素時停下並指向該元素a[i],與此同時,j從hi向lo走,當遇見小於v的元素時停下並指向該元素a[j]。此時a[i]>v,a[j]<v,為了保證切分的基本條件,所以將a[i]和a[j]交換位置。接下來繼續上述操作,直到i>=j時(指標相遇)停止掃描,此時交換a[lo]和a[j],便完成了一次切分操作。

遞迴地完成上述切分操作便能實現快速排序。

看一個例子:

int []a={15, 7, 6, 29, 18, 24, -6, -20, 53, 47};

第一次切分(i紅,j藍):

v=a[lo]=15

15 7 6 29 18 24 -6 -20 53 47,swap(a, i, j): 15 7 6 -20 18 24 -6 29 53 47,此時i=3,j=7

15 7 6 -20 18 24 -6 29 53 47,swap(a, i, j): 15 7 6 -20 -6 24 18 29 53 47,此時i=4,j=6

15 7 6 -20

-6 24 18 29 53 47,swap(a, lo, j): -6 7 6 -20 15 24 18 29 53 47,此時i=5, j=4

完成切分: -6 7 6 -20 15 24 18 29 53 47,可以發現,15左邊的元素都不大於15,右邊的元素都不小於15

切分操作的實現程式碼:

private static int partition(int[] a, int lo, int hi){
		int i=lo, j=hi+1;
		int v=a[lo];
		while (true){
			while (a[++i]<v) 
				if (i==hi)
					break;
			while (a[--j]>v)
				if (j==lo)
					break;
			if (i>=j)
				break;
			swap(a, i, j);
		}
		swap(a, lo, j);
		return j;
	}

遞迴切分排序:

public static void sort(int[] a, int lo, int hi){
		if (hi<=lo)
			return;
		int j=partition(a, lo, hi);
		sort(a, lo, j-1);
		sort(a, j+1, hi);
	}

public static void sort(int[] a){
		sort(a, 0, a.length-1);
	}

切分軌跡:

lo=0, hi=9, i=5, j=4, v=15
-6 7 6 -20 15 24 18 29 53 47 

lo=0, hi=3, i=2, j=1, v=-6
-20 -6 6 7 15 24 18 29 53 47 

lo=2, hi=3, i=3, j=2, v=6
-20 -6 6 7 15 24 18 29 53 47 

lo=5, hi=9, i=7, j=6, v=24
-20 -6 6 7 15 18 24 29 53 47 

lo=7, hi=9, i=8, j=7, v=29
-20 -6 6 7 15 18 24 29 53 47 

lo=8, hi=9, i=9, j=9, v=53
-20 -6 6 7 15 18 24 29 47 53 

給出兩個結論:

1.長度為N的無重複陣列排序,快排平均需要~2NlnN次比較。

2.快排最多需要約(N^2)/2次比較,隨機打亂陣列能夠預防這種情況。

BY DXH924

2018.11.1