快速排序-三種實現方式

概念

??????快速排序的基本思想是分治法,選取一個“基準數”作為比較參照,經過排序運算,將數據分為兩個規模更小的數據源,其中一個所有的數據都比另一部分的小,然后遞歸進行操作從而使數據達到有序的狀態。

分治法的基本思想是:將原問題分解為若干個規模更小但結構與原問題相似的子問題。遞歸地解這些子問題,然后將這些子問題的解組合為原問題的解。

快速排序的時間復雜度在最壞情況下是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, ] 

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容