外部排序

1、應用場景

(1)問題:

          給你一個包含20億個int類型整數的文件,計算機的內存只有2GB,怎麼給它們排序?一個int數佔4個字節,20個億需要80億字節,大概佔用8GB的內存,而計算機只有2GB的內存,數據都裝不下!

(2)解決方案:

          可以把8GB分割成4個2GB的數據來排,然後在把他們拼湊回去。如下圖:

         排序的時候可以選擇快速排序或歸併排序等算法。爲了方便,我們把排序好的2G有序數據稱爲有序子串。接着把兩個小的有序子串合併成一個大的有序子串。

         注意:讀取的時候是每次讀取一個int數,通過比較之後再輸出。

         按照這個方法來回合併,總共經過三次合併之後就可以得到8G的有序子串。

 2、外排序 二路外排序

         我們假設需要排序的int數有12個,內存一次只能裝下3個int數。

         接下來把12個數據分成4份,然後排序成有序子串:

         然後把子串進行兩兩合併:

輸出哪個元素就在那個元素所在的有序子串再次讀入一個元素:

         繼續

         重複直到合併成一個包含6個int有序子串:

         再把兩個包含6個int的有序子串合併成一個包含12個int數據的最終有序子串:

3、優化策略

(1)分析

         因爲硬盤的讀寫速度比內存要慢的多,按照以上這種方法,每個數據都從硬盤讀了三次,寫了三次,要花很多時間。

         解釋下:例如對於數據2,我們把無序的12個數據分成有序的4個子串需要讀寫各一次,把2份3個有序子串合併成6個有序子串讀寫各一次;把2份6個有序子串合併從12個有序子串讀寫各一次,一共需要讀寫各3次。

         在進行有序子串合併的時候,不採取兩兩合併的方法,而是可以3個子串,或4個子串一起來合併。

(2)多路歸併

         爲了方便講解,我們假設內存一共可以裝4個int型數據。

         剛纔我們是採取兩兩合併的方式,現在我們可以採取4個有序子串一起合併的方式,這樣的話,每個數據從硬盤讀寫的次數各需要2次就可以了。如圖:

         4個有序子串的合併,叫4路歸併。如果是n個有序子串的合併,就把它稱爲n路歸併。n並非越大越好。

(3)置換選擇算法

         n不是越大越好,那麼我們可以想辦法減少有序子串的總個數。這樣,也能減少數據從硬盤讀寫的次數。

         以前面的12個無序數據爲例:

         例如我們可以從12個數據讀取3個存到內存中,然後從內存中選出最小的那個數放進子串p1裏;之後再從剩餘的9個數據讀取一個放到內存中,然後再從內存中選出一個數放進子串p1裏,這個數必須滿足比p1中的其他數大,且在內存中儘量小。這樣一直重複,直到內存中的數都比p1中的數小,這時p1子串存放結束,繼續來p2子串的存放,例如(這時假設內存只能存放3個int型數據):

         讀入3個到內存中,且選出一個最小的到子串p1:

         從內存中再次讀取一個元素86:

         從內存中再次讀取一個元素3:

         從內存中再次讀取一個元素24:

         從內存中再次讀取一個元素8:

         這個時候已經沒有符合要求的數了,且內存已滿,進而用p2子串來存放,以此類推。

         通過這種方法,p1子串存放了4個數據,而原來的那種方法p1子串只能存放3個數據。

         我們要如何從內存中選出這個目的數呢?難道每次都把內存中的數據進行排序,然後再逐個比較選擇嗎?其實我們可以構建一個最小堆來幫助我們選擇目的數。具體如下:

         從12個數據中讀取3個數據,構建一個最小堆,然後從堆頂選擇一個數寫入到p1中。之後再從剩餘的9個數中讀取一個數,如果這個數比剛纔那個寫入到p1中的數大,則把這個數插入到最小堆中,重新調整最小堆結構,然後在堆頂選一個數寫入到p1中。否則,把這個數暫放在一邊,暫時不處理。之後一樣需要調整堆結構,從堆頂選擇一個數寫入到p1中。

         這裏說明一下,那個被放在一邊的數是不能在放入p1中的了,因爲它一定比p1中的數都要小,所以它會放在下一個子串中。如下圖所示:

         從12個數據中讀取3個數據:

         構建最小堆,且選出目標數:

         讀入下一個數86:

         讀入下一個數3,比70小,暫放一邊,不加入堆結構中:

         讀入下一個數據24,比81小,不加入堆結構:

         讀入下一個數據8,比86小,不加入堆結構。此時p1已經完成了,把那些剛纔暫放一邊的數重新構成一個堆,繼續p2的存放:

         以此類推…最後生成的p2如下:

         這樣子的話,最後只生成了2個有序子串,我們把這種方法稱之爲置換選擇。按照這種方法,最好的情況下,所有數據只生成一個有序子串;最壞的情況下,和原來沒采取置換選擇算法一樣,還是4個子串。

如果內存可以容納n個元素的話,那麼平均每個子串的長度爲2m,也就是說,使用置換選擇算法我們可以減少一半的子串數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章