新的快速排序算法: 《Dual-Pivot QuickSort》閱讀筆記

相信大家在大學(xué)的《算法與數(shù)據(jù)結(jié)構(gòu)》里面都學(xué)過快速排序(QuickSort), 知道這種排序的性能很好罢维,JDK里面直到JDK6用的都是這種經(jīng)典快排的算法锉试。但是到了JDK7的時候JDK內(nèi)置的排序算法已經(jīng)由經(jīng)典快排變成了Dual-Pivot排序算法。那么Dual-Pivot到底是何方圣神熟妓,能比我們學(xué)過的經(jīng)典快排還要快呢遏片?我們一起來看看。

經(jīng)典快排

在學(xué)習(xí)新的快排之前豪墅,我們首先來復(fù)習(xí)一下經(jīng)典快排,它的核心思想是:

接受一個數(shù)組黔寇,挑一個數(shù)(pivot)偶器,然后把比它小的那一攤數(shù)放在它的左邊,把比它大的那一攤數(shù)放在它的右邊,然后再對這個數(shù)左右兩攤數(shù)遞歸的執(zhí)行快排過程屏轰,直到子數(shù)組只剩一個數(shù)為止颊郎。[2]

偽代碼大概是這樣的:

void quicksort(int array[], int left, int right)
{
    //Do nothing if left <= right
    //p <- Get a number from array
    //Put elements <= p to the left side
    //Put elements >= p to the right side
    //Put p in the middle slot which index is pivot
    //Recursive quicksort the left parts and right parts
}

元素比較的次數(shù)

經(jīng)典快排為什么快? 所謂快其實專業(yè)的說法是“時間復(fù)雜度”霎苗。對于排序算法來說主要看的是排序所需要的元素比較的次數(shù)姆吭。

我們在《算法與數(shù)據(jù)結(jié)構(gòu)》里面都是這么學(xué)的。

對于快排來說唁盏,假設(shè)輸入數(shù)組里面數(shù)字的個數(shù)為n猾编,那么平均要進(jìn)行的元素之間比較的次數(shù)是: O(nlogn)。相比冒泡排序的O(n2)來說比較的次數(shù)要少升敲,那么當(dāng)然就快了答倡。

Dual-Pivot快排是個什么鬼?

它是俄羅斯人Vladimir Yaroslavskiy在2009年開發(fā)出來的驴党。要知道經(jīng)典快排在1990年代穩(wěn)定下來就再也沒有大改過了瘪撇,幾乎所有的語言里面的排序用的都是同樣的經(jīng)典快排的算法。20年之后當(dāng)這個俄羅斯人提出新的Dual-Pivot快排的時候港庄,很多人的第一想法是不信的倔既,因為經(jīng)典快排都被研究爛了,大家不相信在這個算法上面還會有什么可以改進(jìn)的地方鹏氧。

那么Dual-Pivot快排到底是怎么樣的一個排序算法呢渤涌?其實從它的名字里面可以看出一些端倪:在經(jīng)典快排里面有一個pivot的概念,它是用來分隔大數(shù)和小數(shù)的 -- 這個pivot把一個數(shù)組分成兩份把还。那么所謂的Dual-Pivot其實就是用兩個Pivot, 把整個數(shù)組分成三份实蓬。偽代碼大概是這樣的:

dual_pivot_quicksort(A,left,right) // sort A[left..right]
    if right?left ≥ 1
        // Take outermost elements as pivots (replace by sampling)
        p := min {A[left],A[right]}
        q := max{A[left],A[right]}
        ? := left +1; g := right ?1; k := ?
        while k ≤ g
            if A[k] < p
                 Swap A[k] and A[?]; ? := ?+1
            else if A[k] ≥ q
                while A[g] > q and k < g
                    g := g ?1
                end while
                Swap A[k] and A[g]; g := g ?1
                if A[k] < p
                    Swap A[k] and A[?]; ? := ?+1
                end if
            end if
            k := k +1
        end while
        ? := ??1; g := g +1
        A[left] := A[?]; A[?] := p // p to final position
        A[right] := A[g]; A[g] := q // q to final position
        dual_pivot_quicksort(A, left , ??1)
        dual_pivot_quicksort(A, ?+1,g ?1)
        dual_pivot_quicksort(A,g +1,right)
    end if

看起來主要的區(qū)別就是經(jīng)典快排遞歸的時候把輸入數(shù)組分兩段,而Dual-Pivot則分三段吊履,就這么簡單安皱,那為什么這就快了呢?
其實如果按照元素比較次數(shù)來比較的話艇炎,Dual-Pivot快排元素比較次數(shù)其實比經(jīng)典快排要多:

1.5697nlnn VS 1.7043nlnn

理論跟實際情況不符合的時候酌伊,如果實際情況沒有錯,那么就是理論錯了缀踪。

CPU與內(nèi)存

要理解上面的問題居砖,先介紹點背景知識。我們平常很少考慮過CPU的速度驴娃,內(nèi)存的速度奏候,CPU和內(nèi)存速度是否匹配的問題。

其實它們是不匹配的托慨。

距統(tǒng)計在過去的25年里面鼻由,CPU的速度平均每年增長46%, 而內(nèi)存的帶寬每年只增長37%暇榴,那么經(jīng)過25年的這種不均衡發(fā)展厚棵,它們之間的差距已經(jīng)蠻大了蕉世。

假如這種不均衡持續(xù)持續(xù)發(fā)展,有一天CPU速度再增長也不會讓程序變得更快婆硬,因為CPU始終在等待內(nèi)存?zhèn)鬏敂?shù)據(jù)狠轻,這就是傳說中內(nèi)存墻(Memory Wall)。

有點像木桶理論:木桶的容量是由最短的那塊板決定的彬犯,所以你CPU再快向楼,如果內(nèi)存帶寬不夠,那也沒用谐区。

25年前Dual-Pivot快排可能真的比經(jīng)典快排要慢湖蜕,但是25年之后雖然算法還是以前的那個算法,但是計算機(jī)已經(jīng)不是以前的計算機(jī)了宋列。在現(xiàn)在的計算機(jī)里面Dual-Pivot算法更快昭抒!

掃描元素個數(shù)

那么既然光比較元素比較次數(shù)這種計算排序算法復(fù)雜度的方法已經(jīng)無法客觀的反映算法優(yōu)劣了,那么應(yīng)該如何來評價一個算法呢炼杖?作者提出了一個叫做掃描元素個數(shù)的算法灭返。

在這種新的算法里面,我們把對于數(shù)組里面一個元素的訪問: array[i] 稱為一次掃描坤邪。但是對于同一個下標(biāo)熙含,并且對應(yīng)的值也不變得話,即使訪問多次我們也只算一次艇纺。而且我們不管這個訪問到底是讀還是寫怎静。

其實這個所謂的掃描元素個數(shù)反應(yīng)的是CPU與內(nèi)存之間的數(shù)據(jù)流量的大小。

因為內(nèi)存比較慢黔衡,統(tǒng)計CPU與內(nèi)存之間的數(shù)據(jù)流量的大小也就把這個比較慢的內(nèi)存的因素考慮進(jìn)去了消约,因此也就比元素比較次數(shù)更能體現(xiàn)算法在當(dāng)下計算機(jī)里面的性能指標(biāo)。

在這種新的算法下面經(jīng)典快排和Dual-Pivot快排的掃描元素個數(shù)分別為:

1.5697nlnn VS 1.4035nlnn

也就是說經(jīng)典快排確實進(jìn)行了更多的元素掃描動作员帮,因此也就比較慢或粮。在這種新的算法下面,Dual-Pivot快排比經(jīng)典快排t節(jié)省了12%的元素掃描捞高,從實驗來看節(jié)省了10%的時間氯材。

結(jié)論

  • 由于CPU與內(nèi)存的發(fā)展失衡,我們在分析算法復(fù)雜性的時候已經(jīng)不能簡單地用元素比較次數(shù)來比較了硝岗,因為這種比較的方法只考慮了CPU的因素氢哮,沒有考慮內(nèi)存的因素。
  • 對于那種對輸入數(shù)據(jù)進(jìn)行順序掃描的排序算法型檀,掃描元素的個數(shù)這種新的算法把內(nèi)存的流量的因素考慮進(jìn)去冗尤,比較適應(yīng)新時代。
  • 世界在不斷的變化,你當(dāng)年在學(xué)堂里面學(xué)的東西可能已經(jīng)過時了裂七。

參考資料

  1. Why Is Dual-Pivot Quicksort Fast?
  2. 一個Quicksort究竟可以寫到多么短
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皆看,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子背零,更是在濱河造成了極大的恐慌腰吟,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徙瓶,死亡現(xiàn)場離奇詭異毛雇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)侦镇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門灵疮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人壳繁,你說我怎么就攤上這事始藕。” “怎么了氮趋?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵伍派,是天一觀的道長。 經(jīng)常有香客問我剩胁,道長诉植,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任昵观,我火速辦了婚禮晾腔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啊犬。我一直安慰自己灼擂,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布觉至。 她就那樣靜靜地躺著剔应,像睡著了一般。 火紅的嫁衣襯著肌膚如雪语御。 梳的紋絲不亂的頭發(fā)上峻贮,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音应闯,去河邊找鬼纤控。 笑死,一個胖子當(dāng)著我的面吹牛碉纺,可吹牛的內(nèi)容都是我干的船万。 我是一名探鬼主播刻撒,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耿导!你這毒婦竟也來了声怔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碎节,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后抵卫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狮荔,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年介粘,在試婚紗的時候發(fā)現(xiàn)自己被綠了殖氏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡姻采,死狀恐怖雅采,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情慨亲,我是刑警寧澤婚瓜,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站刑棵,受9級特大地震影響巴刻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛉签,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一胡陪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碍舍,春花似錦柠座、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捧书,卻和暖如春狂塘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鳄厌。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工荞胡, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人了嚎。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓泪漂,卻偏偏與公主長得像廊营,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萝勤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文有七千字敌卓,閱讀大約需要占用你10分鐘時間慎式。 好吧。趟径。隨便寫的瘪吏,我也不知道會花多久看完。因為寫的比較爛蜗巧,而且只是...
    鍋與盆閱讀 8,098評論 5 36
  • 概述 排序有內(nèi)部排序和外部排序掌眠,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大幕屹,一次不能容納全部...
    蟻前閱讀 5,186評論 0 52
  • 概述:排序有內(nèi)部排序和外部排序蓝丙,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序,而外部排序是因排序的數(shù)據(jù)很大望拖,一次不能容納全部...
    每天刷兩次牙閱讀 3,732評論 0 15
  • 01 大三那年實習(xí)遇見的老板说敏,是一個極具人格魅力的人沧烈。 聰明、睿智像云,而且永遠(yuǎn)那么的精力充沛锌雀,舉手投足之間都流露出一...
    秦啨閱讀 1,260評論 2 7
  • 如同很多打工仔一樣(我也是打工仔一個),很多人選擇購買了蘋果手機(jī)迅诬。 我曾經(jīng)想過為什么腋逆。 對于那些真正高收入的人,我...
    菇?jīng)錾倌晡医鋹哿薸閱讀 333評論 0 2