快速排序的實現與分析
快速排序應用極廣,效率極高且實現簡單。快排和歸併排序一樣用到了分治的思想。
快排中,最關鍵的操作叫做“切分”,切分使得切分元素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 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