實現多路平衡歸併演算法

2021-09-11 02:42:55 字數 4304 閱讀 1413

對於外部排序演算法來說,直接影響演算法效率的因素為讀寫外存的次數,即次數越多,演算法效率越低。若想提高演算法的效率,即減少演算法執行過程中讀寫外存的次數,可以增加 k–路平衡歸併中的 k 值。

經過計算得知,如果毫無限度地增加 k 值,雖然會減少讀寫外存資料的次數,但會增加內部歸併的時間,得不償失。

對於 10 個臨時檔案,當採用 2-路平衡歸併時,若每次從 2 個檔案中想得到乙個最小值時只需比較 1 次;而採用 5-路平衡歸併時,若每次從 5 個檔案中想得到乙個最小值就需要比較 4 次。以上僅僅是得到乙個最小值記錄,如要得到整個臨時檔案,其耗費的時間就會相差很大。

為避免在增加 k 值的過程中影響內部歸併的效率,在進行 k-路歸併時可使用敗者樹來實現,該方法在增加 k 值時不會影響其內部歸併的效率。

敗者樹實現內部歸併

敗者樹是樹形選擇排序的一種變形,本身是一棵完全二叉樹。

對於無序表建立的完全二叉樹如圖1所示,構建此樹的目的是選出無序表中的最小值。

圖 1 勝者樹

這棵樹與敗者樹正好相反,是一棵勝者樹。因為樹中每個非終端結點(除葉子結點之外的其它結點)中的值都表示的是左右孩子相比較後的較小值(誰最小即為勝者)。例如葉子結點 49 和 38 相對比,由於 38 更小,所以其雙親結點中的值保留的是勝者 38。然後用 38 去繼續同上層去比較,一直比較到樹的根結點。

而敗者樹恰好相反,其雙親結點儲存的是左右孩子比較之後的失敗者,而勝利者則繼續同其它的勝者去比較。

例如圖 1 中,葉子結點 49 和 38 比較,38 更小,所以 38 是勝利者,49 為失敗者,但由於是敗者樹,所以其雙親結點儲存的應該是 49;同樣,葉子結點 65 和 97 比較,其雙親結點中儲存的是 97 ,而 65 則用來同 38 進行比較,65 會儲存到 97 和 49 的雙親結點的位置,38 繼續做後續的勝者比較,依次類推。

勝者樹和敗者樹的區別就是:勝者樹中的非終端結點中儲存的是勝利的一方;而敗者樹中的非終端結點儲存的是失敗的一方。而在比較過程中,都是拿勝者去比較。

圖 2 敗者樹

如圖 2 所示為一棵 5-路歸併的敗者樹,其中 b0—b4 為樹的葉子結點,分別為 5 個歸併段中儲存的記錄的關鍵字。 ls 為一維陣列,表示的是非終端結點,其中儲存的數值表示第幾歸併段(例如 b0 為第 0 個歸併段)。ls[0] 中儲存的為最終的勝者,表示當前第 3 歸併段中的關鍵字最小。

當最終勝者判斷完成後,只需要更新葉子結點 b3 的值,即匯入關鍵字 15,然後讓該結點不斷同其雙親結點所表示的關鍵字進行比較,敗者留在雙親結點中,勝者繼續向上比較。

例如,葉子結點 15 先同其雙親結點 ls[4] 中表示的 b4 中的 12 進行比較,12 為勝利者,則 ls[4] 改為 15,然後 12 繼續同 ls[2] 中表示的 10 做比較,10 為勝者,然後 10 繼續同其雙親結點 ls[1] 表示的 b1(關鍵字 9)作比較,最終 9 為勝者。整個過程如下圖所示:

注意:為了防止在歸併過程中某個歸併段變為空,處理的辦法為:可以在每個歸併段最後附加乙個關鍵字為最大值的記錄。這樣當某一時刻選出的冠軍為最大值時,表明 5 個歸併段已全部歸併完成。(因為只要還有記錄,最終的勝者就不可能是附加的最大值)。

敗者樹:

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

/***    實驗題目:

*        實現多路平衡歸併演算法

*    實驗目的:

*        領會外排序中多路平衡歸併的執行過程和演算法設計

*    實驗內容:

*        編寫程式,模擬利用敗者樹實現5路歸併演算法的過程以求解以下問題:

*    設有5個檔案中記錄關鍵字如下:

*    f0: f1:   f2:  f3   f4

*    要求將其歸併為乙個有序段並輸出。假設這些輸入檔案資料存放在記憶體中,輸出

*    結果直接在螢幕上顯示。

*/#include

#define max_size    20                          //  每個檔案中的最多記錄

#define k           5                           //  5路平衡歸併

#define max_key     32767                       //  最大關鍵字值∞

#define min_key     -32768                      //  最小關鍵字值-∞

typedef int info_type;

typedef int key_type;

typedef struct

rec_type;                                      //  檔案中記錄的型別

typedef struct

file_type;                                     //  模擬的檔案型別

typedef int loser_tree;                         //  敗者樹為loser_tree[k]

/*------------------------以下為全域性變數------------------------*/

rec_type b[k];                                  //  b中存放各段中取出的當前記錄

file_type f[k];                                 //  存放檔案記錄的陣列

/*-----------------初始化存放檔案記錄的陣列f---------------------*/

static void initial(void)

/*--------------從f[i]檔案中讀乙個記錄到b[i]中---------------*/

static void input(int i, int &key)

static int cnt = 0;

/*--------------沿從葉子結點b[s]到根結點ls[0]=5的路徑調整敗者樹--------------*/

// ls為一維陣列,表示的是非終端結點,其儲存的數值表示第幾歸併段,例如b[0]為第0個歸併段

static void adjust(loser_tree ls[k], int s)     //  s=4,3,2,1,0

t = t / 2;

}ls[0] = s;                                  //  根結點存放第幾個歸併段

printf("\tls[0] = %d\n", ls[0]);

}/*--------------建立敗者樹ls--------------*/

static void create_loser_tree(loser_tree ls[k])

/*--------------顯示敗者樹----------------*/

static void display(loser_tree ls[k])

else if(b[ls[i]].key == min_key)

else

}printf("\n");

}/*--------------輸出f[q]中的當前記錄--------------*/

static void output(int q)

/*--------------利用敗者樹ls進行k路歸併到輸出-----------------*/

static void k_merge(loser_tree ls[k])

create_loser_tree(ls);                  //  建立敗者樹ls,選得最小關鍵字為b[ls[0]].key

display(ls);

while(b[ls[0]].key != max_key)

}int main(void)

f1: f2: f3 f4\n");

initial();

k_merge(ls);

return 0;

}

7 7 3 多路平衡歸併與敗者樹

歸併趟數s logm r 向下取整 從而增加歸併路數m可以減少歸併趟數s,進而減少訪問外存的次數 i o次數 然而,當增加歸併路數m時,內部歸併時間將增加。做內部歸併時,在m個元素中選擇關鍵字最小的記錄需要比較m 1次。每趟歸併n個元素最多需要作 n 1 m 1 次比較,s趟歸併總共需要的比較次數為...

多路歸併排序的實現

為了更好地測試程式,首先利用以下方法生成了1000000個整數,每個整數均不相同 生成隨機排列的1000000個數字,每個數字都不相同 const int size 1000000 int num size void produce srand unsigned time null 隨機交換隨機的兩...

多路歸併排序

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