快速排序
1.定義:
(1)快速排序是一種分治的排序算法,它將一個數組分成兩個子數組,將兩部分獨立地排序。
(2)快速排序和歸併排序時互補的:歸併排序將數組分成兩個子數組分別排序,並將有序的子數組歸併以將整個數組排序;而快速排序將數組排序的方式則是當兩個子數組都有序時整個數組也就自然有序了。
(3)歸併排序時,遞歸調用發生在處理整個數組之前;快速排序時,遞歸調用發生在處理整個數組之後。
(4)歸併排序中,一個數組被等分爲兩半;在快速排序中,切分的位置取決於數組的內容。
2.代碼剖析
假設:數組a,最小下標low,最大下標high
(1)當我們將數組切分後,假設得到了切分的下標j,即對於某個j,a[j]已經排定;a[low]到a[j-1]中的所有元素都不大於a[j];a[j+1]到a[high]中的所有元素都不小於a[j]。
(2)因此,爲了完成上述切分,一般策略是先隨意地取a[low]作爲切分元素;即那個將會被排定的元素;然後我們從數組的左端開始向右掃描直到找到一個大於等於它的元素,再從數組的右端開始向左掃描直到找得到一個小於等於它的元素。然後交換他們的位置。如此繼續,我們就可以保證左指針i的左側元素都不大於切分元素,右指針j的右側元素都不小於切分元素。
(3)當兩個指針相遇時,我們只需要將切分元素a[low]和左子數組最右側的元素(a[j])交換然後返回j即可。如下圖所示:
3.圖解
4.算法代碼
/**
* 快速排序
*
* @param a
*/
public static void quickSort(int[] a) {
quickSort(a, 0, a.length - 1);
}
private static void quickSort(int[] a, int low, int high) {
//當只有一個元素時,直接返回,無需排序
if (high <= low) {
return;
}
//獲得切分的下標j
int j = partition(a, low, high);
//通過切分的下標j,分別對下標j左邊數組以及右邊數組進行快排,遞歸下去
quickSort(a, low, j - 1);
quickSort(a, j + 1, high);
}
private static int partition(int[] a, int low, int high) {
//將數組切分爲a[low..i-1],a[i],a[i+1...high]
int i = low; //左右掃描指針3
int j = high + 1;
int v = a[low]; //切分元素
int e;
while (true) {
//掃描左右,檢查是否結束並交換元素
while (a[++i] < v) {
if (i == high) break;
}
while (a[--j] > v) {
if (j == low) {
break;
}
}
//當兩個指針相遇時,跳出循環
if (i >= j) {
break;
}
e = a[i];
a[i] = a[j];
a[j] = e;
}
//當指針相遇後,將切分元素和左子數組最右側的元素交換然後返回j即可
e = a[low];
a[low] = a[j];
a[j] = e;
return j;
}
5.算法優劣分析
(1)優點:簡潔性,比較次數少。
(2)排序效率最終還是依賴切分數組的效果,而這依賴於切分元素的值
(3)快速排序最好情況是每次都正好能將數組對半分
(4)存在缺點:在切分不平衡時這個算法可能會較爲低效。例如:如果第一次從最小的元素切分,第二次從第二小的元素切分,如此這般,每次調用只會移除一個元素。這會導致一個大子數組需要切分很多次。因此,在我們對一個數組進行排序之前,我們可以先打亂這個數組的順序來避免這種情況發生。
6.資料引用:
’《算法Algorithms(第4版)》 p182~p186
上一節:Java算法(4):歸併排序