12-排序(下):如何用快排思想在O(n)內(nèi)查找第K大元素趋箩?

排序操作相關(guān)代碼請(qǐng)移步 Leooel 的博客

今天寫的是歸并排序快速排序,時(shí)間復(fù)雜度均為 O(nlogn)加派,適合大規(guī)模數(shù)據(jù)的排序叫确。

歸并排序

歸并排序使用的就是分治思想。分治芍锦,顧名思義竹勉,就是分而治之,將一個(gè)大問(wèn)題分解成小的子問(wèn)題來(lái)解決娄琉。小的子問(wèn)題解決了次乓,大問(wèn)題也就解決了。

分治算法一般都是用遞歸來(lái)實(shí)現(xiàn)的孽水。分治是一種解決問(wèn)題的處理思想票腰,遞歸是一種編程技巧。

先寫出歸并排序的遞推公式:

遞推公式: merge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r))
終止條件: p >= r 不用再繼續(xù)分解

歸并排序的性能分析

  • 第一女气,歸并排序是穩(wěn)定的排序算法嗎杏慰?

歸并排序是一個(gè)穩(wěn)定的排序算法。

  • 歸并排序的時(shí)間復(fù)雜度是多少炼鞠?

不僅遞歸求解的問(wèn)題可以寫成遞推公式缘滥,遞歸代碼的時(shí)間復(fù)雜度也可以寫成遞推公式。
T(a) = T(b) + T(c) + K
T(a)簇搅、T(b)完域、T(c) 分別是求解問(wèn)題 a、b瘩将、c 的時(shí)間
K 等于將兩個(gè)子問(wèn)題 b吟税、c 的結(jié)果合并成問(wèn)題 a 的結(jié)果所消耗的時(shí)間

我們假設(shè)對(duì) n 個(gè)元素進(jìn)行歸并排序需要的時(shí)間是 T(n),那分解成兩個(gè)子數(shù)組排序的時(shí)間都是 T(n/2)姿现。我們知道肠仪,merge() 函數(shù)合并兩個(gè)有序子數(shù)組的時(shí)間復(fù)雜度是 O(n)。所以备典,套用前面的公式异旧,歸并排序的時(shí)間復(fù)雜度的計(jì)算公式就是:

T(1) = C; n=1 時(shí)提佣,只需要常量級(jí)的執(zhí)行時(shí)間吮蛹,所以表示為 C荤崇。
T(n) = 2*T(n/2) + n; n>1

一步一步分解推導(dǎo)潮针,得到 k=log2n 术荤。我們將 k 值代入上面的公式,得到 T(n)=Cn+nlog2n每篷。

如果我們用大 O 標(biāo)記法來(lái)表示的話瓣戚,T(n) 就等于 O(nlogn)。所以歸并排序的時(shí)間復(fù)雜度是 O(nlogn)焦读。

從我們的原理分析和偽代碼可以看出子库,歸并排序的執(zhí)行效率與要排序的原始數(shù)組的有序程度無(wú)關(guān),所以其時(shí)間復(fù)雜度是非常穩(wěn)定的矗晃,不管是最好情況仑嗅、最壞情況,還是平均情況喧兄,時(shí)間復(fù)雜度都是 O(nlogn)无畔。

  • 第三,歸并排序的空間復(fù)雜度是多少吠冤?

歸并排序的 merge 合并函數(shù)浑彰,在合并兩個(gè)有序數(shù)組為一個(gè)有序數(shù)組時(shí),需要借助額外的存儲(chǔ)空間拯辙。但是遞歸代碼的空間復(fù)雜度并不能像時(shí)間復(fù)雜度那樣累加郭变。在任意時(shí)刻,CPU 只會(huì)有一個(gè)函數(shù)在執(zhí)行涯保,也就只會(huì)有一個(gè)臨時(shí)的內(nèi)存空間在使用诉濒。臨時(shí)內(nèi)存空間最大也不會(huì)超過(guò) n 個(gè)數(shù)據(jù)的大小,所以空間復(fù)雜度是 O(n)夕春。

快速排序的原理

快排利用的也是分治思想未荒,乍看起來(lái),它有點(diǎn)像歸并排序及志,但是思路完全不一樣片排。

快排的思想是這樣的:如果要排序數(shù)組中下標(biāo)從 p 到 r 之間的一組數(shù)據(jù),我們選擇 p 到 r 之間的任意一個(gè)數(shù)據(jù)作為 pivot(分區(qū)點(diǎn))速侈。我們遍歷 p 到 r 之間的數(shù)據(jù)率寡,將小于pivot 的放到左邊,將大于 pivot 的放到右邊倚搬,將pivot 放到中間冶共。經(jīng)過(guò)這一步驟之后,數(shù)組 p 到 r 之間的數(shù)據(jù)就被分成了三個(gè)部分,前面 p 到 q-1 之間都是小于 pivot 的捅僵,中間是 pivot家卖,后面的 q+1 到 r 之間是大于 pivot 的。

根據(jù)分治庙楚、遞歸的處理思想篡九,我們可以用遞歸排序下標(biāo)從 p 到 q-1 之間的數(shù)據(jù)和下標(biāo)從 q+1 到 r 之間的數(shù)據(jù),直到區(qū)間縮小為 1醋奠,就說(shuō)明所有的數(shù)據(jù)都有序了。

遞推公式如下:

遞推公式: quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1, r)
終止條件: p >= r

歸并排序中有一個(gè) merge() 合并函數(shù)伊佃,我們這里有一個(gè) partition() 分區(qū)函數(shù)窜司。怎么低空間、時(shí)間復(fù)雜度的實(shí)現(xiàn)這個(gè)分區(qū)函數(shù)呢航揉?你可以想一下~

快速排序的性能分析

快排是一種原地塞祈、不穩(wěn)定的排序算法。那時(shí)間復(fù)雜度呢帅涂?

快排也是用遞歸來(lái)實(shí)現(xiàn)的议薪。對(duì)于遞歸代碼的時(shí)間復(fù)雜度,前面總結(jié)的公式媳友,這里也還是適用的斯议。如果每次分區(qū)操作,都能正好把數(shù)組分成大小接近相等的兩個(gè)小區(qū)間醇锚,那快排的時(shí)間復(fù)雜度遞推求解公式跟歸并是相同的哼御。所以,快排的時(shí)間復(fù)雜度也是 O(nlogn)焊唬。

但是恋昼,公式成立的前提是每次分區(qū)操作,我們選擇的 pivot 都很合適赶促,正好能將大區(qū)間對(duì)等地一分為二液肌。但實(shí)際上這種情況是很難實(shí)現(xiàn)的。

我舉一個(gè)比較極端的例子鸥滨。如果數(shù)組中的數(shù)據(jù)原來(lái)已經(jīng)是有序的了嗦哆,比如 1,3爵赵,5吝秕,6,8空幻。如果我們每次選擇最后一個(gè)元素作為 pivot烁峭,那每次分區(qū)得到的兩個(gè)區(qū)間都是不均等的。我們需要進(jìn)行大約 n 次分區(qū)操作,才能完成快排的整個(gè)過(guò)程约郁。每次分區(qū)我們平均要掃描大約 n/2 個(gè)元素缩挑,這種情況下,快排的時(shí)間復(fù)雜度就從 O(nlogn) 退化成了 O(n^2)鬓梅。

那快排的平均情況時(shí)間復(fù)雜度是多少呢供置?等到后面了解了遞歸樹再來(lái)方便的計(jì)算。

先給出結(jié)論:T(n) 在大部分情況下的時(shí)間復(fù)雜度都可以做到 O(nlogn)绽快,只有在極端情況下芥丧,才會(huì)退化到 O(n^2)。而且坊罢,我們也有很多方法將這個(gè)概率降到很低胸蛛,后面會(huì)講到狗准。

歸并和快排的區(qū)別

快排和歸并用的都是分治思想,遞推公式和遞歸代碼也非常相似,那它們的區(qū)別在哪里呢陋桂?

可以發(fā)現(xiàn)廉油,歸并排序的處理過(guò)程是由下到上的碰声,先處理子問(wèn)題跟啤,然后再合并。而快排正好相反起趾,它的處理過(guò)程是由上到下的诗舰,先分區(qū),然后再處理子問(wèn)題训裆。

歸并排序雖然是穩(wěn)定的始衅、時(shí)間復(fù)雜度為 O(nlogn) 的排序算法,但是它是非原地排序算法缭保。歸并之所以是非原地排序算法汛闸,主要原因是合并函數(shù)無(wú)法在原地執(zhí)行∫章睿快速排序通過(guò)設(shè)計(jì)巧妙的原地分區(qū)函數(shù)诸老,可以實(shí)現(xiàn)原地排序,解決了歸并排序占用太多內(nèi)存的問(wèn)題钳恕。

開(kāi)篇解答

如何在 O(n) 時(shí)間復(fù)雜度內(nèi)求無(wú)序數(shù)組中的第 K 大元素别伏?


快排核心思想就是分治和分區(qū),我們可以利用分區(qū)的思想忧额,來(lái)解答厘肮。

我們選擇數(shù)組區(qū)間 A[0…n-1] 的最后一個(gè)元素 A[n-1] 作為 pivot,對(duì)數(shù)組 A[0…n-1] 原地分區(qū)睦番,這樣數(shù)組就分成了三部分类茂,A[0…p-1]耍属、A[p]、A[p+1…n-1]巩检。

如果 p+1=K厚骗,那 A[p] 就是要求解的元素;如果 K>p+1, 說(shuō)明第 K 大元素出現(xiàn)在 A[p+1…n-1] 區(qū)間兢哭,我們?cè)侔凑丈厦娴乃悸愤f歸地在 A[p+1…n-1] 這個(gè)區(qū)間內(nèi)查找领舰。同理,如果 K<p+1迟螺,那我們就在 A[0…p-1] 區(qū)間查找冲秽。直到區(qū)間縮小為 1。

為什么時(shí)間復(fù)雜度是 O(n)呢矩父?

第一次分區(qū)查找劳跃,我們需要對(duì)大小為 n 的數(shù)組執(zhí)行分區(qū)操作,需要遍歷 n 個(gè)元素浙垫。第二次分區(qū)查找,我們只需要對(duì)大小為 n/2 的數(shù)組執(zhí)行分區(qū)操作郑诺,需要遍歷 n/2 個(gè)元素夹姥。依次類推,分區(qū)遍歷元素的個(gè)數(shù)分別為辙诞、n/2辙售、n/4、n/8飞涂、n/16....直到區(qū)間縮小為 1旦部。

如果我們把每次分區(qū)遍歷的元素個(gè)數(shù)加起來(lái),就是:n+n/2+n/4+n/8+…+1较店。這是一個(gè)等比數(shù)列求和士八,最后的和等于 2n-1。所以梁呈,上述解決思路的時(shí)間復(fù)雜度就為 O(n)婚度。

課后思考

現(xiàn)在你有 10 個(gè)接口訪問(wèn)日志文件,每個(gè)日志文件大小約 300MB官卡,每個(gè)文件里的日志都是按照時(shí)間戳從小到大排序的蝗茁。你希望將這 10 個(gè)較小的日志文件,合并為 1 個(gè)日志文件寻咒,合并之后的日志仍然按照時(shí)間戳從小到大排列哮翘。如果處理上述排序任務(wù)的機(jī)器內(nèi)存只有 1GB,你有什么好的解決思路毛秘,能“快速”地將這 10 個(gè)日志文件合并嗎饭寺?


每次從各個(gè)文件中取一條數(shù)據(jù),在內(nèi)存中根據(jù)數(shù)據(jù)時(shí)間戳構(gòu)建一個(gè)最小堆,然后每次把最小值給寫入新文件佩研,同時(shí)將最小值來(lái)自的那個(gè)文件再出來(lái)一個(gè)數(shù)據(jù)柑肴,加入到最小堆中。

這個(gè)空間復(fù)雜度為常數(shù)旬薯,但沒(méi)能很好利用 1g 內(nèi)存晰骑,而且磁盤單個(gè)讀取比較慢,所以考慮每次讀取一批數(shù)據(jù)绊序,沒(méi)了再?gòu)拇疟P中取硕舆,時(shí)間復(fù)雜度還是一樣 O(n)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末骤公,一起剝皮案震驚了整個(gè)濱河市抚官,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阶捆,老刑警劉巖凌节,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異洒试,居然都是意外死亡倍奢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門垒棋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卒煞,“玉大人,你說(shuō)我怎么就攤上這事叼架∨显#” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵乖订,是天一觀的道長(zhǎng)扮饶。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乍构,這世上最難降的妖魔是什么贴届? 我笑而不...
    開(kāi)封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蜡吧,結(jié)果婚禮上毫蚓,老公的妹妹穿的比我還像新娘。我一直安慰自己昔善,他們只是感情好元潘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著君仆,像睡著了一般翩概。 火紅的嫁衣襯著肌膚如雪牲距。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天钥庇,我揣著相機(jī)與錄音牍鞠,去河邊找鬼。 笑死评姨,一個(gè)胖子當(dāng)著我的面吹牛难述,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吐句,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胁后,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嗦枢?” 一聲冷哼從身側(cè)響起攀芯,我...
    開(kāi)封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎文虏,沒(méi)想到半個(gè)月后侣诺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氧秘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年年鸳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敏储。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖朋鞍,靈堂內(nèi)的尸體忽然破棺而出已添,到底是詐尸還是另有隱情,我是刑警寧澤滥酥,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布更舞,位于F島的核電站,受9級(jí)特大地震影響坎吻,放射性物質(zhì)發(fā)生泄漏缆蝉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一瘦真、第九天 我趴在偏房一處隱蔽的房頂上張望刊头。 院中可真熱鬧,春花似錦诸尽、人聲如沸原杂。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)穿肄。三九已至年局,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咸产,已是汗流浹背矢否。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脑溢,地道東北人僵朗。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像焚志,于是被迫代替她去往敵國(guó)和親衣迷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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