外部排序&多路歸並排序


外部排序:

一、定義問題

      外部排序指的是大文件的排序,即待排序的記錄存儲在外存儲器上,待排序的文件無法一次裝入內存,需要在內存和外部存儲器之間進行多次數據交換,以達到排序 整個文件的目的。外部排序最常用的算法是多路歸並排序,即將原文件分解成多個能夠一次性裝入內存的部分,分別把每一部分調入內存完成排序。然后,對已經排 序的子文件進行多路歸並排序。

二、處理過程

  (1)按可用內存的大小,把外存上含有n個記錄的文件分成若干個長度為L的子文件,把這些子文件依次讀入內存,並利用有效的內部排序方法對它們進行排序,再將排序后得到的有序子文件重新寫入外存;

  (2)對這些有序子文件逐趟歸並,使其逐漸由小到大,直至得到整個有序文件為止。

   先從一個例子來看外排序中的歸並是如何進行的?
  假設有一個含10000 個記錄的文件,首先通過10 次內部排序得到10 個初始歸並段R1~R10 ,其中每一段都含1000 個記錄。然后對它們作如圖10.11 所示的兩兩歸並,直至得到一個有序文件為止 如下圖

三 、多路歸並排序算法以及敗者樹

    多路歸並排序算法在常見數據結構書中都有涉及。從2路到多路(k路),增大k可以減少外存信息讀寫時間,但k個歸並段中選取最小的記錄需要比較k-1次, 為得到u個記錄的一個有序段共需要(u-1)(k-1)次,若歸並趟數為s次,那么對n個記錄的文件進行外排時,內部歸並過程中進行的總的比較次數為 s(n-1)(k-1),也即(向上取整)(logkm)(k-1)(n-1)=(向上取整)(log2m/log2k)(k-1)(n-1),而(k- 1)/log2k隨k增而增因此內部歸並時間隨k增長而增長了,抵消了外存讀寫減少的時間,這樣做不行,由此引出了“敗者樹”tree of loser的使用。在內部歸並過程中利用敗者樹將k個歸並段中選取最小記錄比較的次數降為(向上取整)(log2k)次使總比較次數為(向上取整) (log2m)(n-1),與k無關。

    敗者樹是完全二叉樹, 因此數據結構可以采用一維數組。其元素個數為k個葉子結點、k-1個比較結點、1個冠軍結點共2k個。ls[0]為冠軍結點,ls[1]--ls[k- 1]為比較結點,ls[k]--ls[2k-1]為葉子結點(同時用另外一個指針索引b[0]--b[k-1]指向)。另外bk為一個附加的輔助空間,不 屬於敗者樹,初始化時存着MINKEY的值。

    多路歸並排序算法的過程大致為:

 

   1):首先將k個歸並段中的首元素關鍵字依次存入b[0]--b[k-1]的葉子結點空間里,然后調用CreateLoserTree創建敗者樹,創建完畢之后最小的關鍵字下標(即所在歸並段的序號)便被存入ls[0]中。然后不斷循環:

   2)把ls[0]所存最小關鍵字來自於哪個歸並段的序號得到為q,將該歸並段的首元素輸出到有序歸並段里,然后把下一個元素關鍵字放入上一個元素本來所 在的葉子結點b[q]中,調用Adjust順着b[q]這個葉子結點往上調整敗者樹直到新的最小的關鍵字被選出來,其下標同樣存在ls[0]中。循環這個 操作過程直至所有元素被寫到有序歸並段里。

 


 

 

外部排序之多路歸並

 

該外部排序上場了.
外部排序干嘛的?

 

  1. 內存極少的情況下,利用分治策略,利用外存保存中間結果,再用多路歸並來排序;

 

  1. map-reduce的嫡系.

 

這里寫圖片描述
這里寫圖片描述

 

1.分

 

內存中維護一個極小的核心緩沖區bigdata按行讀入,搜集到memBuffer中的數據調用內排進行排序,排序后將有序結果寫入磁盤文件 循環利用這里寫圖片描述

 

2.合

 

現在有了n個有序的小文件,怎么合並成1個有序的大文件?
把所有小文件讀入內存,然后內排?
(⊙o⊙)…
no!

 

利用如下原理進行歸並排序:
這里寫圖片描述
我們舉個簡單的例子:

 

文件1:3,6,9
文件2:2,4,8
文件3:1,5,7

第一回合:
文件1的最小值:3 , 排在文件1的第1行
文件2的最小值:2,排在文件2的第1行
文件3的最小值:1,排在文件3的第1行
那么,這3個文件中的最小值是:min(1,2,3) = 1
也就是說,最終大文件的當前最小值,是文件1、2、3的當前最小值的最小值,繞么?
上面拿出了最小值1,寫入大文件.

 

第二回合:
文件1的最小值:3 , 排在文件1的第1行
文件2的最小值:2,排在文件2的第1行
文件3的最小值:5,排在文件3的第2行
那么,這3個文件中的最小值是:min(5,2,3) = 2
將2寫入大文件.

也就是說,最小值屬於哪個文件,那么就從哪個文件當中取下一行數據.(因為小文件內部有序,下一行數據代表了它當前的最小值)

 

 


 

敗者樹可以改善時間復雜度:

 

勝者樹與敗者樹  

 


 

       勝者樹和敗者樹都是完全二叉樹,是樹形選擇排序的一種變型。每個葉子結點相當於一個選手,每個中間結點相當於一場比賽,每一層相當於一輪比賽。

 

 

 

      不同的是,勝者樹的中間結點記錄的是勝者的標號;而敗者樹的中間結點記錄的敗者的標號。

 

 

 

       勝者樹與敗者樹可以在log(n)的時間內找到最值。任何一個葉子結點的值改變后,利用中間結點的信息,還是能夠快速地找到最值。在k路歸並排序中經常用到。

 

 

 

一、勝者樹

 

      

 

       勝者樹的一個優點是,如果一個選手的值改變了,可以很容易地修改這棵勝者樹。只需要沿着從該結點到根結點的路徑修改這棵二叉樹,而不必改變其他比賽的結果。

 


 

 

Fig. 1

 

Fig.1是一個勝者樹的示例。規定數值小者勝。

 

1.         b3 PK b4,b3勝b4負,內部結點ls[4]的值為3;

 

2.         b3 PK b0,b3勝b0負,內部結點ls[2]的值為3;

 

3.         b1 PK b2,b1勝b2負,內部結點ls[3]的值為1;

 

4.         b3 PK b1,b3勝b1負,內部結點ls[1]的值為3。.

 

當Fig. 1中葉子結點b3的值變為11時,重構的勝者樹如Fig. 2所示。

 

1.         b3 PK b4,b3勝b4負,內部結點ls[4]的值為3;

 

2.         b3 PK b0,b0勝b3負,內部結點ls[2]的值為0;

 

3.         b1 PK b2,b1勝b2負,內部結點ls[3]的值為1;

 

4.         b0 PK b1,b1勝b0負,內部結點ls[1]的值為1。.

 

 

Fig. 2

 

 

 

 

 

二、敗者樹

 

 

 

       敗者樹是勝者樹的一種變體。在敗者樹中,用父結點記錄其左右子結點進行比賽的敗者,而讓勝者參加下一輪的比賽。敗者樹的根結點記錄的是敗者,需要加一個結點來記錄整個比賽的勝利者。采用敗者樹可以簡化重構的過程。

 

 

 

 

Fig. 3

 

Fig. 3是一棵敗者樹。規定數大者敗。

 

1.         b3 PK b4,b3勝b4負,內部結點ls[4]的值為4;

 

2.         b3 PK b0,b3勝b0負,內部結點ls[2]的值為0;

 

3.         b1 PK b2,b1勝b2負,內部結點ls[3]的值為2;

 

4.         b3 PK b1,b3勝b1負,內部結點ls[1]的值為1;

 

5.         在根結點ls[1]上又加了一個結點ls[0]=3,記錄的最后的勝者。

 

敗者樹重構過程如下:

 

·            將新進入選擇樹的結點與其父結點進行比賽:將敗者存放在父結點中;而勝者再與上一級的父結點比較。

 

·            比賽沿着到根結點的路徑不斷進行,直到ls[1]處。把敗者存放在結點ls[1]中,勝者存放在ls[0]中。

 

 

Fig. 4

 

       Fig. 4是當b3變為13時,敗者樹的重構圖。

 

 

 

       注意,敗者樹的重構跟勝者樹是不一樣的,敗者樹的重構只需要與其父結點比較。對照Fig. 3來看,b3與結點ls[4]的原值比較,ls[4]中存放的原值是結點4,即b3與b4比較,b3負b4勝,則修改ls[4]的值為結點3。同理,以此 類推,沿着根結點不斷比賽,直至結束。

 

 

 

        由上可知,敗者樹簡化了重構。敗者樹的重構只是與該結點的父結點的記錄有關,而勝者樹的重構還與該結點的兄弟結點有關。

 

 

 

二 使用敗者樹加快合並排序

 

外部排序最耗時間的操作時磁盤讀寫,對於有m個初始歸並段,k路平衡的歸並排序,磁盤讀寫次數為

 

|logkm|,可見增大k的值可以減少磁盤讀寫的次數,但增大k的值也會帶來負面效應,即進行k路合並

 

的時候會增加算法復雜度,來看一個例子。

 

把n個整數分成k組,每組整數都已排序好,現在要把k組數據合並成1組排好序的整數,求算法復雜度

 

u1: xxxxxxxx

 

u2: xxxxxxxx

 

u3: xxxxxxxx

 

.......

 

uk: xxxxxxxx

 

算法的步驟是:每次從k個組中的首元素中選一個最小的數,加入到新組,這樣每次都要比較k-1次,故

 

算法復雜度為O((n-1)*(k-1)),而如果使用敗者樹,可以在O(logk)的復雜度下得到最小的數,算法復雜

 

度將為O((n-1)*logk), 對於外部排序這種數據量超大的排序來說,這是一個不小的提高。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM