【數(shù)據(jù)結(jié)構(gòu)與算法】快速排序算法

前言

快速排序(Quicksort)是對冒泡排序的一種改進。
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分數(shù)據(jù)分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數(shù)據(jù)變成有序序列。

值得注意的是,快速排序算法,是一種不穩(wěn)定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結(jié)束時產(chǎn)生變動。
快速排序的時間復雜度最低為nlogn,最高為n的平方,它的主要原理是先確定一個基準數(shù),這個基準數(shù)可以為做左邊的數(shù)或者最右邊的數(shù)或者隨機數(shù)等等。然后將所有比它小的數(shù)都放到它前面,所有比它大的數(shù)都放到它后面。

流程說明

在此舉個例子:
1.首先拿最右邊的這個數(shù)為基準數(shù)。
2.然后用拿最左邊的數(shù)和這個基準數(shù)做比較,小于這個基準數(shù),左邊的序號+1,再和這個值進行比較,直到大于這個數(shù),然后基準數(shù)和這個值的位置調(diào)換。
3.之后對右邊的數(shù)重復上述的過程,直到找到一個右邊的數(shù)要小于這個基準數(shù),再和這個基準數(shù)進行位置調(diào)換,左邊的序號+1;。
4.上述過程循環(huán)下去,直到左邊的序號等于右邊的序號,兩個相等序號所在的位置就是基準值應(yīng)該存在的位置,將這個位置的值賦值為基準值。
5.這時會出現(xiàn)一個現(xiàn)象,基準數(shù)左邊的數(shù)都是比他小的,右邊的數(shù)都是比它大的。
6.然后就可以利用分治和遞歸的思想,分別對基準值左邊的區(qū)間進行排序,對右邊的區(qū)間進行排序。這個過程是一個遞歸的過程,而遞歸必然存在一個出口,遞歸的出口就是排序區(qū)間的長度為0。

代碼實現(xiàn)

這里用java來簡單實現(xiàn)一下。

public static void quickSort(int[] num,int left,int right){
      //遞歸的出口
    if(left>=right){
        return;
    }
    //獲取基準值所在的索引
    int res = particular(num,left,right);
    //對左區(qū)間進行排序
    quickSort(num,left,res-1);
    //對右區(qū)間進行排序
    quickSort(num,res+1,right);
}
public static int particular(int[] num,int left,int right){
    //確定一個基準值,這里的基準值確定為最右邊的數(shù)
    int base = num[right];
    while(left<right){
        while(num[left]<base&&left<right){
            left++;
        }
        if(left<right){
            num[right]=num[left];
            right--;
        }
        while(num[right]>base&&left<right){
            right--;
        }
        if(left<right){
            num[left]=num[right];
            left++;
        }
    }
    //循環(huán)結(jié)束
    //此時left==right,而這個位置就是基準值在數(shù)組中應(yīng)該在的位置
    num[left]=base;
    //最后返回基準值所在的位置
    return left;
}

算法優(yōu)化

  1. 三平均分區(qū)法
    關(guān)于這一改進的最簡單的描述大概是這樣的:與一般的快速排序方法不同,它并不是選擇待排數(shù)組的第一個數(shù)作為中軸,而是選用待排數(shù)組最左邊、最右邊和最中間的三個元素的中間值作為中軸。這一改進對于原來的快速排序算法來說,主要有兩點優(yōu)勢:

首先,它使得最壞情況發(fā)生的幾率減小了。
其次,未改進的快速排序算法為了防止比較時數(shù)組越界,在最后要設(shè)置一個哨點。

  1. 根據(jù)分區(qū)大小調(diào)整算法
    這一方面的改進是針對快速排序算法的弱點進行的。快速排序?qū)τ谛∫?guī)模的數(shù)據(jù)集性能不是很好。可能有人認為可以忽略這個缺點不計,因為大多數(shù)排序都只要考慮大規(guī)模的適應(yīng)性就行了。但是快速排序算法使用了分治技術(shù),最終來說大的數(shù)據(jù)集都要分為小的數(shù)據(jù)集來進行處理。由此可以得到的改進就是,當數(shù)據(jù)集較小時,不必繼續(xù)遞歸調(diào)用快速排序算法,而改為調(diào)用其他的對于小規(guī)模數(shù)據(jù)集處理能力較強的排序算法來完成。Introsort就是這樣的一種算法,它開始采用快速排序算法進行排序,當遞歸達到一定深度時就改為堆排序來處理。這樣就克服了快速排序在小規(guī)模數(shù)據(jù)集處理中復雜的中軸選擇,也確保了堆排序在最壞情況下O(n log n)的復雜度。
    另一種優(yōu)化改進是當分區(qū)的規(guī)模達到一定小時,便停止快速排序算法。也即快速排序算法的最終產(chǎn)物是一個“幾乎”排序完成的有序數(shù)列。數(shù)列中有部分元素并沒有排到最終的有序序列的位置上,但是這種元素并不多。可以對這種“幾乎”完成排序的數(shù)列使用插入排序算法進行排序以最終完成整個排序過程。因為插入排序?qū)τ谶@種“幾乎”完成的排序數(shù)列有著接近線性的復雜度。這一改進被證明比持續(xù)使用快速排序算法要有效的多。
    另一種快速排序的改進策略是在遞歸排序子分區(qū)的時候,總是選擇優(yōu)先排序那個最小的分區(qū)。這個選擇能夠更加有效的利用存儲空間從而從整體上加速算法的執(zhí)行。

  2. 不同的分區(qū)方案考慮
    對于快速排序算法來說,實際上大量的時間都消耗在了分區(qū)上面,因此一個好的分區(qū)實現(xiàn)是非常重要的。尤其是當要分區(qū)的所有的元素值都相等時,一般的快速排序算法就陷入了最壞的一種情況,也即反復的交換相同的元素并返回最差的中軸值。無論是任何數(shù)據(jù)集,只要它們中包含了很多相同的元素的話,這都是一個嚴重的問題,因為許多“底層”的分區(qū)都會變得完全一樣。
    對于這種情況的一種改進辦法就是將分區(qū)分為三塊而不是原來的兩塊:一塊是小于中軸值的所有元素,一塊是等于中軸值的所有元素,另一塊是大于中軸值的所有元素。另一種簡單的改進方法是,當分區(qū)完成后,如果發(fā)現(xiàn)最左和最右兩個元素值相等的話就避免遞歸調(diào)用而采用其他的排序算法來完成。

  3. 并行的快速排序
    由于快速排序算法是采用分治技術(shù)來進行實現(xiàn)的,這就使得它很容易能夠在多臺處理機上并行處理。
    在大多數(shù)情況下,創(chuàng)建一個線程所需要的時間要遠遠大于兩個元素比較和交換的時間,因此,快速排序的并行算法不可能為每個分區(qū)都創(chuàng)建一個新的線程。一般來說,會在實現(xiàn)代碼中設(shè)定一個閥值,如果分區(qū)的元素數(shù)目多于該閥值的話,就創(chuàng)建一個新的線程來處理這個分區(qū)的排序,否則的話就進行遞歸調(diào)用來排序。
    對于這一并行快速排序算法也有其改進。該算法的主要問題在于,分區(qū)的這一步驟總是要在子序列并行處理之前完成,這就限制了整個算法的并行程度。解決方法就是將分區(qū)這一步驟也并行處理。改進后的并行快速排序算法使用2n個指針來并行處理分區(qū)這一步驟,從而增加算法的并行程度。

快速排序算法的變種

  1. 隨機化快排
    快速排序的最壞情況基于每次劃分對主元的選擇。基本的快速排序選取第一個元素作為主元。這樣在數(shù)組已經(jīng)有序的情況下,每次劃分將得到最壞的結(jié)果。一種比較常見的優(yōu)化方法是隨機化算法,即隨機選取一個元素作為主元。這種情況下雖然最壞情況仍然是O(n^2),
    但最壞情況不再依賴于輸入數(shù)據(jù),而是由于隨機函數(shù)取值不佳。實際上,隨機化快速排序得到理論最壞情況的可能性僅為1/(2^n)。所以隨機化快速排序可以對于絕大多數(shù)輸入數(shù)據(jù)達到O(nlogn)的期望時間復雜度。一位前輩做出了一個精辟的總結(jié):“隨機化快速排序可以滿足一個人一輩子的人品需求。
    隨機化快速排序的唯一缺點在于,一旦輸入數(shù)據(jù)中有很多的相同數(shù)據(jù),隨機化的效果將直接減弱。對于極限情況,即對于n個相同的數(shù)排序,隨機化快速排序的時間復雜度將毫無疑問的降低到O(n^2)。解決方法是用一種方法進行掃描,使沒有交換的情況下主元保留在原位置。

  2. 平衡快排
    每次盡可能地選擇一個能夠代表中值的元素作為關(guān)鍵數(shù)據(jù),然后遵循普通快排的原則進行比較、替換和遞歸。通常來說,選擇這個數(shù)據(jù)的方法是取開頭、結(jié)尾、中間3個數(shù)據(jù),通過比較選出其中的中值。取這3個值的好處是在實際問題中,出現(xiàn)近似順序數(shù)據(jù)或逆序數(shù)據(jù)的概率較大,此時中間數(shù)據(jù)必然成為中值,而也是事實上的近似中值。萬一遇到正好中間大兩邊小(或反之)的數(shù)據(jù),取的值都接近最值,那么由于至少能將兩部分分開,實際效率也會有2倍左右的增加,而且利于將數(shù)據(jù)略微打亂,破壞退化的結(jié)構(gòu)。

  3. 外部快排
    與普通快排不同的是,關(guān)鍵數(shù)據(jù)是一段buffer,首先將之前和之后的M/2個元素讀入buffer并對該buffer中的這些元素進行排序,然后從被排序數(shù)組的開頭(或者結(jié)尾)讀入下一個元素,假如這個元素小于buffer中最小的元素,把它寫到最開頭的空位上;假如這個元素大于buffer中最大的元素,則寫到最后的空位上;否則把buffer中最大或者最小的元素寫入數(shù)組,并把這個元素放在buffer里。保持最大值低于這些關(guān)鍵數(shù)據(jù),最小值高于這些關(guān)鍵數(shù)據(jù),從而避免對已經(jīng)有序的中間的數(shù)據(jù)進行重排。完成后,數(shù)組的中間空位必然空出,把這個buffer寫入數(shù)組中間空位。然后遞歸地對外部更小的部分,循環(huán)地對其他部分進行排序。

  4. 三路基數(shù)快排
    (Three-way Radix Quicksort,也稱作Multikey Quicksort、Multi-key Quicksort):結(jié)合了基數(shù)排序(radix sort,如一般的字符串比較排序就是基數(shù)排序)和快排的特點,是字符串排序中比較高效的算法。該算法被排序數(shù)組的元素具有一個特點,即multikey,如一個字符串,每個字母可以看作是一個key。算法每次在被排序數(shù)組中任意選擇一個元素作為關(guān)鍵數(shù)據(jù),首先僅考慮這個元素的第一個key(字母),然后把其他元素通過key的比較分成小于、等于、大于關(guān)鍵數(shù)據(jù)的三個部分。然后遞歸地基于這一個key位置對“小于”和“大于”部分進行排序,基于下一個key對“等于”部分進行排序。

時間復雜度分析

最壞情況:O(n ^ 2)

最好情況:θ(nlogn)

平均情況:θ(nlogn)

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