概念
??????快速排序的基本思想是分治法,選取一個“基準數”作為比較參照,經過排序運算,將數據分為兩個規模更小的數據源,其中一個所有的數據都比另一部分的小,然后遞歸進行操作從而使數據達到有序的狀態。
分治法的基本思想是:將原問題分解為若干個規模更小但結構與原問題相似的子問題。遞歸地解這些子問題,然后將這些子問題的解組合為原問題的解。
快速排序的時間復雜度在最壞情況下是O(N2),平均的時間復雜度是O(N*logN)。快速排序是不穩定的算法。
算法穩定性:假設在數列中存在 a[i] = a[j],若在排序之前,a[i] 在a[j] 前面;并且排序之后,a[i] 仍然在a[j] 前面。則這個排序算法是穩定的!
實現方式
??????記錄一下三種簡單的實現方式,總結以代碼+日志的方式展示,通過日志可以清晰的展示出每輪每次的數據操作。前提假設最終的目標排序方式都是升序,可以為了清晰展示出每次具體操作了哪些數據,日志[]中括號內展示的為數組下標,()小括號內展示的為本輪的數據源,""雙引號內展示的為本次需要交換的兩個值,''單引號內展示的為pre(前后指針)的值。
左右指針法
- 選取基準值,開始本輪循環;
- 從數組右側開始遞減比較,發現有值小于基準值,停下記錄該下標;
- 從數組左側開始遞增比較,發現有值大于基準值,停下記錄該下標;
- 將兩個下標的值進行交換;
- 接著循環重復第2~4步,直到左右兩指針相遇,表示這一輪的內循環比較結束;
- 此時左、右指針指向相同的值,將左、右指針指向的值與基準值交換(基準值歸位),就完成了本輪的排序;
- 每輪排序完成后,基準值左邊的都是小于它的,而右邊都是大于等于它的,接著以基準值為分割點,將兩個子集數據源從第1步進行遞歸操作;
??????通俗點說,就是一個指針從右側依次遞減,一個指針從左側依次遞增,當發現符合條件的情況時,交換兩個指針指向的值,然后以基準值為分割點,將本次數據源分為兩個小的數組,依次遞歸循環該操作。
??????這里需要注意的就是內部循環初始先從右側開始還是左側開始,這里先分析下本例當中,要求升序排列,從左側(最終肯定是小于基準值的值)取默認基準值,當內部循環優先從右側開始時,每次循環完,右側指針指向的永遠是小于基準值的值(排除已經是升序的情況),這時假如本次沒有交換,在循環外右側指針(等于左側指針)就可以與基準值的進行交換,最終基準值歸位,小于基準值的數都在一側。
??????相應的,我們也可以在內循環時優先從左側判斷,這時在選基準值時就選擇右側的;或者嘗試下降序時的初始方式,這里就不贅述了……
public static void quickSort(int left, int right) {
if (left >= right) {
return;
}
int l = left;
int r = right;
int target = arrs[l];
while (l < r) {
while (arrs[r] >= target && l < r) {
r--;
}
while (arrs[l] <= target && l < r) {
l++;
}
if (l < r) {
swap(arrs[l], arrs[r]);
}
}
if (l != left) {
swap(arrs[l], arrs[left]):
}
quickSort(left, l - 1);
quickSort(l + 1, right);
}
隨機初始數組: [29, 13, 86, 2, 55, 7, 22, 33, ]
################
第1輪,基準值是29:
交換[2]和[6]位置的值
交換前的數據: [(29, 13, "86", 2, 55, 7, "22", 33), ]
交換后的數據: [(29, 13, "22", 2, 55, 7, "86", 33), ]
交換[4]和[5]位置的值
交換前的數據: [(29, 13, 22, 2, "55", "7", 86, 33), ]
交換后的數據: [(29, 13, 22, 2, "7", "55", 86, 33), ]
內循環結束,當前l=r=[4],與基準值(下標[0])值交換
交換前的數據: [("29", 13, 22, 2, "7", 55, 86, 33), ]
交換后的數據: [("7", 13, 22, 2, "29", 55, 86, 33), ]
第1輪結束
第2輪,基準值是7:
交換[1]和[3]位置的值
交換前的數據: [(7, "13", 22, "2"), 29, 55, 86, 33, ]
交換后的數據: [(7, "2", 22, "13"), 29, 55, 86, 33, ]
內循環結束,當前l=r=[1],與基準值(下標[0])值交換
交換前的數據: [("7", "2", 22, 13), 29, 55, 86, 33, ]
交換后的數據: [("2", "7", 22, 13), 29, 55, 86, 33, ]
第2輪結束
第3輪,基準值是22:
循環結束,當前l=r=[3],與基準值(下標[2])值交換
交換前的數據: [2, 7, ("22", "13"), 29, 55, 86, 33, ]
交換后的數據: [2, 7, ("13", "22"), 29, 55, 86, 33, ]
第3輪結束
第4輪,基準值是55:
交換[6]和[7]位置的值
交換前的數據: [2, 7, 13, 22, 29, (55, "86", "33"), ]
交換后的數據: [2, 7, 13, 22, 29, (55, "33", "86"), ]
內循環結束,當前l=r=[6],與基準值(下標[5])值交換
交換前的數據: [2, 7, 13, 22, 29, ("55", "33", 86), ]
交換后的數據: [2, 7, 13, 22, 29, ("33", "55", 86), ]
第4輪結束
################
排序最終數組: [2, 7, 13, 22, 29, 33, 55, 86, ]
填坑法
- 選取基準值,該下標即為第一個坑位A,開始本輪循環;
- 從數組右側開始遞減比較,當發現有值小于基準值時,將當前值填到坑位A中,同時當前下標替換為坑位B(注意:初始基準值被覆蓋掉了,本輪操作最后,需要將該值填到坑位中);
- 從數組左側開始遞增比較,當發現有值大于基準值,將值填到坑位B中,同時當前下標替換為坑位A;
- 不停的重復第2~3步的操作,直到左右兩指針相遇,表示這一輪的內循環比較結束;
- 將基準值填到左右指針指向的坑位中,就完成了本輪的排序;
- 以基準值為分割點,將本次數據源分為兩個小的數組,依次從第1步遞歸循環操作;
public static void quickSort(int left, int right) {
if (left >= right) {
return;
}
int l = left;
int r = right;
int target = arrs[l];
while (l < r) {
while (arrs[r] >= target && l < r) {
r--;
}
if (l < r) {
arrs[l] = arrs[r];
}
while (arrs[l] <= target && l < r) {
l++;
}
if (l < r) {
arrs[r] = arrs[l];
}
}
arrs[l] = target;
quickSort(left, l - 1);
quickSort(l + 1, right);
}
隨機初始數組: [14, 18, 1, 28, 30, 11, 78, 55, ]
################
第1輪,基準值是14,當前坑位[0]:
交換前的數據: [("14", 18, 1, 28, 30, "11", 78, 55), ]
交換后的數據: [("11", 18, 1, 28, 30, "11", 78, 55), ] 當前坑位[5],值是11
交換前的數據: [(11, "18", 1, 28, 30, "11", 78, 55), ]
交換后的數據: [(11, "18", 1, 28, 30, "18", 78, 55), ] 當前坑位[1],值是18
交換前的數據: [(11, "18", "1", 28, 30, 18, 78, 55), ]
交換后的數據: [(11, "1", "1", 28, 30, 18, 78, 55), ] 當前坑位[2],值是1
內循環結束,準備將基準值補上最后一個坑位 [(11, 1, "1", 28, 30, 18, 78, 55), ]
交換后的數據: [(11, 1, "14", 28, 30, 18, 78, 55), ]
第1輪結束
第2輪,基準值是11,當前坑位[0]:
交換前的數據: [("11", "1"), 14, 28, 30, 18, 78, 55, ]
交換后的數據: [("1", "1"), 14, 28, 30, 18, 78, 55, ] 當前坑位[1],值是1
內循環結束,準備將基準值補上最后一個坑位 [(1, "1"), 14, 28, 30, 18, 78, 55, ]
交換后的數據: [(1, "11"), 14, 28, 30, 18, 78, 55, ]
第2輪結束
第3輪,基準值是28,當前坑位[3]:
交換前的數據: [1, 11, 14, ("28", 30, "18", 78, 55), ]
交換后的數據: [1, 11, 14, ("18", 30, "18", 78, 55), ] 當前坑位[5],值是18
交換前的數據: [1, 11, 14, (18, "30", "18", 78, 55), ]
交換后的數據: [1, 11, 14, (18, "30", "30", 78, 55), ] 當前坑位[4],值是30
內循環結束,準備將基準值補上最后一個坑位 [1, 11, 14, (18, "30", 30, 78, 55), ]
交換后的數據: [1, 11, 14, (18, "28", 30, 78, 55), ]
第3輪結束
第4輪,基準值是30,當前坑位[5]:
內循環結束,準備將基準值補上最后一個坑位 [1, 11, 14, 18, 28, ("30", 78, 55), ]
交換后的數據: [1, 11, 14, 18, 28, ("30", 78, 55), ]
第4輪結束
第5輪,基準值是78,當前坑位[6]:
交換前的數據: [1, 11, 14, 18, 28, 30, ("78", "55"), ]
交換后的數據: [1, 11, 14, 18, 28, 30, ("55", "55"), ] 當前坑位[7],值是55
內循環結束,準備將基準值補上最后一個坑位 [1, 11, 14, 18, 28, 30, (55, "55"), ]
交換后的數據: [1, 11, 14, 18, 28, 30, (55, "78"), ]
第5輪結束
################
排序最終數組: [1, 11, 14, 18, 28, 30, 55, 78, ]
前后指針法
- 選取基準值,定義兩個指針,cur 表示當前指針指向,pre 默認表示cur 指針的前一位;
- cur 和 pre 指針依次++ 進行遞增比較,當cur 發現大于基準值時,pre 暫停遞增(即[pre+1] 指向了一個大于基準值的值);
- cur 接著++ 進行遞增比較,當發現比基準值小時,對數組[pre+1] 和 數組[cur] 的值進行交換;
- 不停的重復第2~3步操作,直到一輪循環結束(即cur 從當前數據源從左移到了右);
- 內循環結束后,將數組[pre+1] 的值與基準值進行交換;
- 以基準值為分割點,將本次數據源分為兩個小的數組,依次從第1步遞歸循環操作;
public static void quickSort(int left, int right) {
if (left >= right) {
return;
}
int cur = left;
int pre = cur - 1;
int target = arrs[right];
while (cur < right) {
while (arrs[cur] < target && ++pre != cur) {
swap(arrs[cur], arrs[pre];
}
cur++;
}
swap(arrs[++pre], arrs[right]);
quickSort(left, pre -1);
quickSort(pre + 1, right);
}
隨機初始數組: [86, 59, 63, 53, 68, 99, 58, 1, ]
################
第1輪,基準值是1,'pre':[-1],"cur":[0]:
"cur"指針++了: [(86, "59", 63, 53, 68, 99, 58, 1), ] ,'pre':[-1], "cur":[1]
"cur"指針++了: [(86, 59, "63", 53, 68, 99, 58, 1), ] ,'pre':[-1], "cur":[2]
"cur"指針++了: [(86, 59, 63, "53", 68, 99, 58, 1), ] ,'pre':[-1], "cur":[3]
"cur"指針++了: [(86, 59, 63, 53, "68", 99, 58, 1), ] ,'pre':[-1], "cur":[4]
"cur"指針++了: [(86, 59, 63, 53, 68, "99", 58, 1), ] ,'pre':[-1], "cur":[5]
"cur"指針++了: [(86, 59, 63, 53, 68, 99, "58", 1), ] ,'pre':[-1], "cur":[6]
"cur"指針++了: [(86, 59, 63, 53, 68, 99, 58, "1"), ] ,'pre':[-1], "cur":[7]
內循環結束,準備將'++pre'(大于基準值的值)與基準值交換: [("86", 59, 63, 53, 68, 99, 58, "1"), ]
交換后的數據: [("1", 59, 63, 53, 68, 99, 58, "86"), ]
第1輪結束
第2輪,基準值是86,'pre':[0],"cur":[1]:
"cur"指針++了: [1, ('59', "63", 53, 68, 99, 58, 86), ] ,'pre':[1], "cur":[2]
"cur"指針++了: [1, (59, '63', "53", 68, 99, 58, 86), ] ,'pre':[2], "cur":[3]
"cur"指針++了: [1, (59, 63, '53', "68", 99, 58, 86), ] ,'pre':[3], "cur":[4]
"cur"指針++了: [1, (59, 63, 53, '68', "99", 58, 86), ] ,'pre':[4], "cur":[5]
"cur"指針++了: [1, (59, 63, 53, '68', 99, "58", 86), ] ,'pre':[4], "cur":[6]
交換前的數據: [1, (59, 63, 53, 68, "99", "58", 86), ]
交換后的數據: [1, (59, 63, 53, 68, "58", "99", 86), ]
"cur"指針++了: [1, (59, 63, 53, 68, '58', 99, "86"), ] ,'pre':[5], "cur":[7]
內循環結束,準備將'++pre'(大于基準值的值)與基準值交換: [1, (59, 63, 53, 68, 58, "99", "86"), ]
交換后的數據: [1, (59, 63, 53, 68, 58, "86", "99"), ]
第2輪結束
第3輪,基準值是58,'pre':[0],"cur":[1]:
"cur"指針++了: ['1', (59, "63", 53, 68, 58), 86, 99, ] ,'pre':[0], "cur":[2]
"cur"指針++了: ['1', (59, 63, "53", 68, 58), 86, 99, ] ,'pre':[0], "cur":[3]
交換前的數據: [1, ("59", 63, "53", 68, 58), 86, 99, ]
交換后的數據: [1, ("53", 63, "59", 68, 58), 86, 99, ]
"cur"指針++了: [1, ('53', 63, 59, "68", 58), 86, 99, ] ,'pre':[1], "cur":[4]
"cur"指針++了: [1, ('53', 63, 59, 68, "58"), 86, 99, ] ,'pre':[1], "cur":[5]
內循環結束,準備將'++pre'(大于基準值的值)與基準值交換: [1, (53, "63", 59, 68, "58"), 86, 99, ]
交換后的數據: [1, (53, "58", 59, 68, "63"), 86, 99, ]
第3輪結束
第4輪,基準值是63,'pre':[2],"cur":[3]:
"cur"指針++了: [1, 53, 58, ('59', "68", 63), 86, 99, ] ,'pre':[3], "cur":[4]
"cur"指針++了: [1, 53, 58, ('59', 68, "63"), 86, 99, ] ,'pre':[3], "cur":[5]
內循環結束,準備將'++pre'(大于基準值的值)與基準值交換: [1, 53, 58, (59, "68", "63"), 86, 99, ]
交換后的數據: [1, 53, 58, (59, "63", "68"), 86, 99, ]
第4輪結束
################
排序最終數組: [1, 53, 58, 59, 63, 68, 86, 99, ]