內(nèi)排序的歸并排序是采用二路歸并。
將已有序的子序列合并,得到完全有序的序列;即先使每個(gè)子序列有序,再使子序列段間有序
外排序我們可以將這個(gè)“二”擴(kuò)大到M。
將一個(gè)大文件分成M個(gè)小文件,每個(gè)小文件是有序的,然后對(duì)應(yīng)在內(nèi)存中我們開(kāi)M個(gè)優(yōu)先隊(duì)列,每個(gè)隊(duì)列從對(duì)應(yīng)編號(hào)的文件中讀取TopN條記錄,然后我們從M路隊(duì)列中各取一個(gè)數(shù)字進(jìn)入中轉(zhuǎn)站隊(duì)列,并將該數(shù)字打上隊(duì)列編號(hào)標(biāo)記,當(dāng)從中轉(zhuǎn)站出來(lái)的最小數(shù)字就是我們最后要排序的數(shù)字之一,因?yàn)樵摂?shù)字打上了隊(duì)列編號(hào),所以方便我們通知對(duì)應(yīng)的編號(hào)隊(duì)列繼續(xù)出數(shù)字進(jìn)入中轉(zhuǎn)站隊(duì)列,可以看出中轉(zhuǎn)站一直保存了M個(gè)記錄,當(dāng)中轉(zhuǎn)站中的所有數(shù)字都出隊(duì)完畢,則外排序結(jié)束。這考驗(yàn)的是我們的架構(gòu)能力。
問(wèn)題描述:
輸入:給定一個(gè)文件,里面最多含有n個(gè)不重復(fù)的正整數(shù)(也就是說(shuō)可能含有少于n個(gè)不重復(fù)正整數(shù)),且其中每個(gè)數(shù)都小于等于n,n=10^7。
輸出:得到按從小到大升序排列的包含所有輸入的整數(shù)的列表。
條件:最多有大約1MB的內(nèi)存空間可用,但磁盤空間足夠。且要求運(yùn)行時(shí)間在5分鐘以下,10秒為最佳結(jié)果。
采取方法:1、歸并排序,內(nèi)存不夠。2、位圖法。3、多路歸并
1、歸并排序,內(nèi)部排序。
2、位圖法
例如:用一個(gè)20位長(zhǎng)的字符串來(lái)表示一個(gè)所有元素都小于20的簡(jiǎn)單的非負(fù)整數(shù)集合,邊框用如下字符串來(lái)表示集合{1,2,3,5,8,13}:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0,上述集合中各數(shù)對(duì)應(yīng)的位置則置1,沒(méi)有對(duì)應(yīng)的數(shù)的位置則置0。
針對(duì)我們的10^7個(gè)數(shù)據(jù)量的磁盤文件排序問(wèn)題,我們可以這么考慮,由于每個(gè)7位十進(jìn)制整數(shù)表示一個(gè)小于1000萬(wàn)的整數(shù)。我們可以使用一個(gè)具有1000萬(wàn)個(gè)位的字符串來(lái)表示這個(gè)文件,其中,當(dāng)且僅當(dāng)整數(shù)i在文件中存在時(shí),第i位為1。采取這個(gè)位圖的方案是因?yàn)槲覀兠鎸?duì)的這個(gè)問(wèn)題的特殊性:1、輸入數(shù)據(jù)限制在相對(duì)較小的范圍內(nèi),2、數(shù)據(jù)沒(méi)有重復(fù),3、其中的每條記錄都是單一的整數(shù),沒(méi)有任何其它與之關(guān)聯(lián)的數(shù)據(jù)。
三步進(jìn)行解決:
第一步,將所有的位都置為0,從而將集合初始化為空。
第二步,通過(guò)讀入文件中的每個(gè)整數(shù)來(lái)建立集合,將每個(gè)對(duì)應(yīng)的位都置為1。
第三步,檢驗(yàn)每一位,如果該位為1,就輸出對(duì)應(yīng)的整數(shù)。
注意:
用此位圖方法,嚴(yán)格說(shuō)來(lái)還是不太行,空間消耗10^7/8還是大于1M(1M=1024*1024空間,小于10^7/8)。
既然如果用位圖方案的話,我們需要約1.25MB(若每條記錄是8位的正整數(shù)的話,則10000000/(1024*1024*8) ~= 1.2M)的空間,而現(xiàn)在只有1MB的可用存儲(chǔ)空間,那么究竟該作何處理呢?
3、多路歸并
假設(shè)文件中整數(shù)個(gè)數(shù)為N(N是億級(jí)的),整數(shù)之間用空格分開(kāi)。首先分多次從該文件中讀取M(十萬(wàn)級(jí))個(gè)整數(shù),每次將M個(gè)整數(shù)在內(nèi)存中使用快速排序之后存入臨時(shí)文件,然后使用多路歸并將各個(gè)臨時(shí)文件中的數(shù)據(jù)再次整體排好序后存入輸出文件。
排序過(guò)程兩部分構(gòu)成:
1)內(nèi)存排序
由于要求的可用內(nèi)存為1MB,那么每次可以在內(nèi)存中對(duì)250K的數(shù)據(jù)進(jìn)行排序,然后將有序的數(shù)寫入硬盤。
那么10M的數(shù)據(jù)需要循環(huán)40次,最終產(chǎn)生40個(gè)有序的文件。
2)歸并排序
將每個(gè)文件最開(kāi)始的數(shù)讀入(由于有序,所以為該文件最小數(shù)),存放在一個(gè)大小為40的first_data數(shù)組中;
選擇first_data數(shù)組中最小的數(shù)min_data,及其對(duì)應(yīng)的文件索引index;
將first_data數(shù)組中最小的數(shù)寫入文件result,然后更新數(shù)組first_data(根據(jù)index讀取該文件下一個(gè)數(shù)代替min_data);
判斷是否所有數(shù)據(jù)都讀取完畢,否則返回2。
所以,本程序按順序分兩步,第一步、Memory Sort,第二步、Merge Sort。程序的流程圖,如下圖所示(感謝F的繪制)。
小結(jié):
bit-map
適用范圍:可進(jìn)行數(shù)據(jù)的快速查找,判重,刪除,一般來(lái)說(shuō)數(shù)據(jù)范圍是int的10倍以下
基本原理及要點(diǎn):使用bit數(shù)組來(lái)表示某些元素是否存在,比如8位電話號(hào)碼
擴(kuò)展:bloom filter可以看做是對(duì)bit-map的擴(kuò)展
問(wèn)題實(shí)例:
1)已知某個(gè)文件內(nèi)包含一些電話號(hào)碼,每個(gè)號(hào)碼為8位數(shù)字,統(tǒng)計(jì)不同號(hào)碼的個(gè)數(shù)。
8位最多99 999 999,大概需要99m個(gè)bit,大概10幾m字節(jié)的內(nèi)存即可。
2)2.5億個(gè)整數(shù)中找出不重復(fù)的整數(shù)的個(gè)數(shù),內(nèi)存空間不足以容納這2.5億個(gè)整數(shù)。
將bit-map擴(kuò)展一下,用2bit表示一個(gè)數(shù)即可,0表示未出現(xiàn),1表示出現(xiàn)一次,2表示出現(xiàn)2次及以上。或者我們不用2bit來(lái)進(jìn)行表示,我們用兩個(gè)bit-m
[外排序適用范圍]
大數(shù)據(jù)的排序,去重基本原理及要點(diǎn):外排序的歸并方法,置換選擇敗者樹原理,最優(yōu)歸并樹擴(kuò)展。
問(wèn)題實(shí)例:
1).有一個(gè)1G大小的一個(gè)文件,里面每一行是一個(gè)詞,詞的大小不超過(guò)16個(gè)字節(jié),內(nèi)存限制大小是1M。返回頻數(shù)最高的100個(gè)詞。這個(gè)數(shù)據(jù)具有很明顯的特點(diǎn),詞的大小為16個(gè)字節(jié),但是內(nèi)存只有1m做hash有些不夠,所以可以用來(lái)排序。內(nèi)存可以當(dāng)輸入緩沖區(qū)使用。
具體實(shí)例詳解:
鏈接 :http://www.lxweimin.com/p/1683cf5cc0c9
勝者樹,敗者樹
勝者樹和敗者樹都是完全二叉樹,是樹形選擇排序的一種變型。每個(gè)葉子結(jié)點(diǎn)相當(dāng)于一個(gè)選手,每個(gè)中間結(jié)點(diǎn)相當(dāng)于一場(chǎng)比賽,每一層相當(dāng)于一輪比賽。
同的是,勝者樹的中間結(jié)點(diǎn)記錄的是勝者的標(biāo)號(hào);而敗者樹的中間結(jié)點(diǎn)記錄的敗者的標(biāo)號(hào)。
勝者樹與敗者樹可以在log(n)的時(shí)間內(nèi)找到最值。任何一個(gè)葉子結(jié)點(diǎn)的值改變后,利用中間結(jié)點(diǎn)的信息,還是能夠快速地找到最值。在k路歸并排序中經(jīng)常用到。
區(qū)別:
在用勝者樹的時(shí)候,每個(gè)新元素上升時(shí),首先需要獲得父節(jié)點(diǎn),然后再獲得兄弟節(jié)點(diǎn),然后再比較。
在使用敗者樹的時(shí)候,每個(gè)新元素上升時(shí),只需要獲得父節(jié)點(diǎn)并比較即可。
所以總的來(lái)說(shuō),減少了訪存的時(shí)間。
勝者樹和敗者樹在每輸出和補(bǔ)充一個(gè)值之后都要自底向上調(diào)整,每上升一層都需要一次比較,敗者樹是和父節(jié)點(diǎn)的一次比較,勝者樹是和兄弟的一次比較,在比較的內(nèi)存訪問(wèn)次數(shù)上二者沒(méi)有太大的差別(或者勝者樹可能好一點(diǎn))。不同的是勝者樹每次必然需要更新勝者(因?yàn)檫@條路徑就是以原來(lái)的最終勝者為外部節(jié)點(diǎn)的路徑,而原來(lái)的最終勝者已經(jīng)被輸出了),但敗者樹每次不一定需要更新,這就代表它在每次上升時(shí)可能會(huì)少一次向內(nèi)存的寫入,因此更優(yōu)
由于新加入的節(jié)點(diǎn)一定是替換了上一輪的勝者,那么對(duì)于勝者堆,從新節(jié)點(diǎn)到根之間路徑節(jié)點(diǎn)存的都是上一輪的勝者,這些數(shù)據(jù)事實(shí)上對(duì)于本輪比較來(lái)說(shuō)是無(wú)用的,但每次還要與兄弟節(jié)點(diǎn)比較去更新它。
而敗者堆中,對(duì)于新更新的節(jié)點(diǎn),它的父節(jié)點(diǎn)都是兄弟子堆的勝者,是最有價(jià)值、值得比較的數(shù)據(jù),每次更新也都可以直接用于下輪比較。
http://blog.csdn.net/v_JULY_v/article/details/6451990 ?(贊)
http://blog.csdn.net/v_JULY_v/article/details/6279498 ?(超贊)
http://www.cnblogs.com/charlesblc/p/6138908.html
http://blog.csdn.net/msdnwolaile/article/details/52084983
http://blog.csdn.net/u013322907/article/details/38125641
http://blog.csdn.net/msdnwolaile/article/details/52084983
http://blog.csdn.net/v_JULY_v
http://blog.csdn.net/whz_zb/article/details/7425152
https://www.zhihu.com/question/35144290