一、快速排序基礎
快速排序也是一種較爲基礎的排序算法,其效率比上篇文章介紹的冒泡排序算法有大幅提升。因爲使用冒泡排序時,一趟只能選出一個最值,有n個元素最多就要執行n - 1趟比較。而使用快速排序時,一次可以將所有元素按大小分成兩堆,也就是平均情況下需要logn輪就可以完成排序。
快速排序的思想是:每趟排序時選出一個基準值,然後將所有元素與該基準值比較,並按大小分成左右兩堆,然後遞歸執行該過程,直到所有元素都完成排序。
快速排序的步驟如下:
1>先從數列中取出一個數作爲基準數。
2>分區過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。
3>再對左右區間重複第二步,直到各區間只有一個數。
1.1 排序過程圖示
假如有[4,7,2,8,1]五個元素,下面就以常見的“挖坑法”來演示一次快速排序的過程:
首先選定基準元素key,並記住這個位置index,這個位置相當於一個“坑”。並且設置兩個指針left和right,指向數列的最左和最右兩個元素:
接下來,從right指針開始,把指針所指向的元素和基準元素做比較。如果比key大,則right指針向左移動;如果比key小,則把right所指向的元素填入坑中。在當前數組元素中,1<4,所以把1填入基準元素所在位置,也就是坑的位置。這時候,元素1本來所在的位置成爲了新的坑。同時,left向右移動一位:
接下來,我們切換到left指針進行比較。如果left指向的元素小於key,則left指針向右移動;如果元素大於key,則把left指向的元素填入坑中。在當前數列中,7>4,所以把7填入index的位置。這時候元素7本來的位置成爲了新的坑。同時,right向左移動一位。
繼續上面的過程,8>4,元素位置不變,right左移:
2<4,用2來填坑,left右移,切換到left:
當left和right指針重合時,把之前的key元素,也就是4放到index的位置。此時數列左邊的元素都小於4,數列右邊的元素都大於4,這一輪交換終告結束:
1.2 快速排序實現
用代碼來實現上述過程,示例如下:
static void quickSort(int[] arr, int startIndex, int endIndex) {
/*當startIndex大於等於endIndex時,不再遞歸*/
if (startIndex >= endIndex) {
return;
}
/*得到基準元素位置*/
int keyIndex = divide(arr, startIndex, endIndex);
/*遞歸處理基準的左右兩部分*/
quickSort(arr, startIndex, keyIndex - 1);
quickSort(arr, keyIndex + 1, endIndex);
}
static int divide(int[] arr, int startIndex, int endIndex) {
/*取第一個位置的元素作爲基準元素*/
int key = arr[startIndex];
int left = startIndex;
int right = endIndex;
/*坑的位置,初始等於key的位置*/
int index = startIndex;
/*當right大於等於left時,執行循環*/
while (right >= left){
/*right指針從右向左進行比較*/
while (right >= left) {
if (arr[right] < key) {
/*最右邊的元素覆蓋原來坑中的值*/
arr[left] = arr[right];
/*坑的位置改變,變成最右邊元素對應的索引*/
index = right;
/*left索引右移,因爲原來的值已被right索引的值所覆蓋*/
left++;
break;
}
/*最右邊的元素參與比較,無論是否參與與坑中元素的交換,都左移,不再參與下一輪的比較*/
right--;
}
/*left指針從左向右進行比較*/
while (right >= left) {
if (arr[left] > key) {
arr[right] = arr[left];
index = left;
right--;
break;
}
left++;
}
}
/*將基準元素放在index位置,也就是大小元素放在其前後的中間位置*/
arr[index] = key;
return index;
}
二、快速排序優化
2.1 三數取中
該方法指的是選取基準值時,不再取固定位置(如第一個元素、最後一個元素)的值,因爲這種固定取值的方式在面對隨機輸入的數組時,效率是非常高的。但是一旦輸入數據是有序的,使用固定位置取值,效率就會非常低。因此此時引入了三數取中,即在數組中隨機選出三個元素,然後取三者的中間值做爲基準值。
2.2 插入排序
當待排序序列的長度分割到一定大小(如 < 10)後,使用插入排序。
2.3 相等元素聚集
在一次分割結束後,可以把與Key相等的元素聚在一起,繼續下次分割時,不用再對與key相等元素分割。