敗者樹與外部多路歸併排序

2022-08-31 09:15:07 字數 2541 閱讀 7398

在處理大資料量的排序時,由於資料無法全部載入到記憶體,內部排序無法對整個資料集進行排序,需要到外部排序。

外部排序有一些特點,跟記憶體容量和讀寫檔案有關:

1. 讀寫檔案,需要考慮 io 時間

2. 從無序到逐步有序的過程中,需要多個中間檔案

外部排序有多種,常見的歸併排序的如下:

輸入為大檔案 f ,排序過程分為分割和歸併

分割:1. 從 f 中讀入記憶體能容納的 k 個數

2. 對 k 個數排序

3. 排序結果輸出的到檔案 gi

4. 重複 1-3 直到 f 結束

分割後得到 ceil(f/k) 個已排序的小檔案,合併過程將多個小檔案合併

歸併:1. 開啟 m 個小檔案(或後續中間結果檔案)

2. 從 m 小檔案中讀當前數字,選最小的數輸出到中間結果檔案 ri (假設為按從小到大排序)

3. 重複 2 直到 m 個小檔案結束

4. 重複 1-3 直到所有檔案(包間中間檔案)已合併到最終檔案

歸併的過程中,以什麼樣的演算法選數呢?

假如 m=2 (二路外部歸併排序) ,那麼沒有什麼好說的,直接比較兩個數的大小就行了。但從整體上來說,由於每次只操作兩個小檔案,需要的中間結果檔案數量多,io 占用時間長。

如果提公升 m ,進行多路排序,那麼可減少中間檔案的數量,但是對數的比較運算就複雜,每次取走乙個數後,都要對 m 個檔案流第乙個數進行一次排序。

敗者樹是處理 m 路歸併時,優化排序的資料結構,它是對排序樹的一種變型,可減少因取走數調整的工作量。

敗者樹的基本思想是:左右孩子結點進行比較時,敗者儲存到父節點,而勝者則繼續向上比較;根節點儲存冠軍。

l[0]:冠軍b2

|l[1]:敗者b1

/ \

l[2]:敗者b0 l[3]:敗者b3

/ \ / \

l[4]:b[0]8 l[5]:b[1]7 l[6]:b[2]5 l[7]:b[3]9

對於 m 路歸併排序,需要使用 m 個元素記錄勝敗情況,m 個葉子儲存 m 個檔案流第乙個數。敗者樹的節點可用 union 表示為:

typedef union _node
infotype 中需要記錄檔案流和對應的第乙個數,需要支援的操作:

top() 取當前檔案偏移的數,偏移位置不變

de() 取當前檔案偏移的數,偏移位置 + 1

它使用陣列儲存,l[0]為冠軍,l[m]...l[2m-1]為葉子節點,每個節點 i 的分節點為 i/2 (有些寫法會把敗者樹陣列 l 和資料陣列 b 分開,也是可以的) 。每次取走冠軍l[0],需要從葉子結點向上更新。

調整的演算法為:

// 調整敗者樹 l ,冠軍為位置 s ,取出冠軍後調整

adjust(losertree l, int s):

p = s / 2 // 從葉子節點 l[s] 向根節點 l[0] 調整

while p>0:

// l[s] 失敗,調整父節點l[p],l[p]向上比較

if l[s].info->top() > l[l[p]].info->top():

tmp = l[p] // 暫存 l[p]

l[p] = s // 父節點為敗者

s = tmp // 存入 s 繼續向上

p = p / 2 // 繼續向上調整

l[0] = s // 冠軍

那麼敗者樹的構建過程為:

create_lt(losertree l, int m, int data):

for i in rang(m):

l[i] = m+i; // 勝者敗者初始化

for i in range(m, 2m-1):

l[i] = infotype(data[i-m]) // 資料初始化

for i in range(2m-1, m, step=-1):

adjust(l, i); // 初始化調整,從 data[m-1]...data[0]

合併的演算法為:

m_merge(losertree l, int m, int data):

create_lt(l, m, data);

// 一直取冠軍直到檔案結束

while l[l[0]]->top() != eof:

output(l[l[0]]->de()); // 輸出冠軍,同時該檔案流到下一位置

adjust(l, l[0])

敗者樹將調整的過程的比較數量減少到 log(n) ,有效地加速外部排序的過程。

除了在外部歸併排序中使用,還可以在置換選擇排序中使用敗者樹,以在記憶體中獲取當前小於已輸出的最大的數的最小數,不過比較的時有雙關鍵字:當前已輸出的最大數、和同時比較數字。

關於多路歸併排序 外部排序

比如檔案內有1億資料排序。程式設計珠璣第乙個case是有關乙個技巧性解決外部排序問題的。問題很巧妙的解決了,但一開始提到的利用歸併排序進行外部排序的演算法仍值得仔細 一下,畢竟本科時學的不是很深入。先來看內部排序中最簡單的2路歸併排序演算法。演算法核心操作是將一維陣列中前後相鄰的兩個有序序列歸併為乙...

敗者樹多路排序

定義 敗者樹是乙個完全二叉樹,非葉子節點記錄失敗者。那麼相對於勝者樹,具有訪存小的優勢。勝者樹勝者拿走後,整條通路 從葉子到根 所有記錄的勝者資訊失效,那麼新加入節點需要從葉子一直遍歷到根,有可能每次都要寫入 更新新的勝者 而敗者樹,新加入的節點可以利用整條路徑的敗者資訊。敗者樹構建 構建過程 初始...

多路歸併排序

我們有如下乙個問題 對於若干個長度相同的序列,將其合併成乙個有序的序列。暴力的方法顯然是不可取的,這裡可以利用優先佇列來處理這個問題。首先從簡單的開始,對於2路歸併排序,設兩個序列為,將,排序,有 a1 a2 a3 an b1 b2 b3 bn 建立乙個優先佇列,佇列中首先存入元素 a1,0 b1,...