快速排序(簡稱快排):在待排序數組中確定一個基準值(pivot),一次排序后將所有小于基準值的數移動至基準值左側,大于基準值的數據移動至基準值右側,這樣基準值所在的位置就是最終排序后其應在位置。根據分治、遞歸的思想,對左右兩側數據遞歸上面的操作,直至區間縮小為1,所有的數據就都有序了。
基準值的選取方法是很關鍵的(比如三元選取,隨機選取等),但是并屬于該篇博客的講解范圍,所以下面為了解釋方便,基準值暫定的比較隨意,大家諒解 ~
單路快排
下圖中說明了一次排序的詳細流程:核心思想:基準值定為最右邊,i和j從最左邊開始,如果j小于基準值,則i和j交換位置,并且i++,j++。否則i保持不動,j++。最終當j移動到基準值所在位置后,基準值與i交換位置。
單路快排圖示(*注:該圖來自極客時間)
單路排序數據交換流程
猜想一:基準值從最左邊開始是否可以?名詞解釋 -- 穩定性:經過某種排序算法之后,如果相同值的數據,前后順序沒有發生改變,我們就把這種算法叫做穩定的排序算法。
猜想一
得出結論:基準值必需是j移動的結尾,因為最終需要一次基準值和i的交換位置
代碼實現:
public static void quickSortSingle(int[] arr, int left, int right) {
if (left > right)
return;
int pivot = arr[right];
int i = 0, j = 0;
while (j < right) {
while (j < right && arr[j] <= pivot) {//如果"哨兵"j小于基準值,則"哨兵"i與"哨兵"j交換位置
swap(arr, i, j);
i++;
j++;
}
j++;
}
//此時"哨兵"j移動到最右側,基準值與哨兵"i"所在位置的值進行交換
swap(arr, i, right);
quickSortSingle(arr, left, i - 1);
quickSortSingle(arr, i + 1, right);
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
雙路快排
下面按照上述的規則列出一組數據整個交換流程:核心思想:基準值在最左邊,“哨兵”i在最左邊,“哨兵”j在最右邊,從右邊(注意要從右邊開始,下面會有說明)先開始(j--),如果“哨兵”j所在的數據小于基準值則停止;“哨兵”i開始(i++),如果“哨兵”i所在的數據大于基準值則停止,i與j交換位置;如果i和j相遇,則基準值與i或j(因為兩者現在一致)交換位置。
雙路排序數據交換流程
猜想二
得出結論:因為如果先從右邊開始會停留在比基準值大的數上,這時交換基準值的位置就會出現問題,所以開始執行的方向必需是基準值的反方向。
代碼實現:
public static void quickSortDual(int[] arr, int left, int right) {
if (left > right)
return;
int pivot = arr[left];//基準值
int i = left;//左側"哨兵"
int j = right;//右側"哨兵"
while (i != j) {//注意:要從基準值所在側的另外一側開始
while (arr[j] >= pivot && i < j)//如果右側出現了比基準值小的元素,則"哨兵"j停留
j--;
while (arr[i] <= pivot && i < j)//如果左側出現了比基準值小的元素,則"哨兵"i停留
i++;
if (i < j) {//如果"哨兵"i與j沒有相遇則交換其所在位置的數據
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
//此時"哨兵"i與j相遇,交換基準值與該相遇點的數據
arr[left] = arr[i];
arr[i] = pivot;
quickSortDual(arr, left, i - 1);//遞歸的處理左側數據
quickSortDual(arr, i + 1, right);//遞歸的處理右側數據
}
三路快排
下面按照上述的規則列出一組數據整個交換流程(雖然該數據的排序過程并沒有丟失穩定性,但是大家不要認為三路快排是個穩定排序算法):核心思想:基準值在最左邊,“哨兵”i在基準值右側加1的位置,“哨兵”j在最右邊,從基準值右側加1的位置開始往后遍歷,如果遍歷到的當前值小于基準值,則當前值與左側"哨兵"交換位置,左側"哨兵"進一,反之,則當前值與右側"哨兵"交換位置,左側"哨兵"退一。
三路排序數據交換流程
代碼實現:
public static void quickSortThreeWay(int[] arr, int left, int right) {
if (left > right)
return;
int pivot = arr[left];//基準值
int i = left;//左側"哨兵"
int j = right + 1;//右側"哨兵"
int index = left + 1;
while (index < j) {
if (arr[index] < pivot) {//如果當前值小于基準值,則當前值與左側"哨兵"交換位置,左側"哨兵"進一
swap(arr, index, i + 1);
i++;
index++;
} else if (arr[index] > pivot) {//如果當前值大于基準值,則當前值與右側"哨兵"交換位置,左側"哨兵"退一
swap(arr, index, j - 1);
j--;
} else {
index++;
}
}
swap(arr, left, i);
quickSortSingle(arr, left, i - 1);
quickSortSingle(arr, j, right);
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}