二路和多路歸併排序

2021-07-05 08:27:08 字數 1972 閱讀 6912

先通俗地說一下歸併排序。打個比方,桌子上有兩堆撲克牌,每堆撲克牌都是已經排好序的(不妨假設是公升序)。我們從兩堆撲克牌中各取最上面的一張(取的兩張牌顯然分別是兩堆牌中最小的 ),比較大小,把最小的一張拿在手裡,然後從「勝出」的牌所在的牌堆中再取一張。重複上面的過程。。。如果有乙個牌堆被取完了,那麼就把另一堆一股腦搬過來。這時候,你手中的牌就是公升序的。換句話說,通過上面的演算法,你成功把兩個各為公升序的牌堆合成了乙個公升序的牌堆——二合一。可以證明,該演算法的複雜度是o(nlgn)。

上面所說的其實就是乙個二路歸併排序。顯然,如果把k堆牌合成一堆,那就是k路歸併排序。

考慮這樣乙個問題,我這裡有k個檔案,裡面各自儲存著已經排好序(不妨假設是公升序)的,並且序相同的(要麼都是公升序要麼都是降序)的整數。檔案裡的資料量很大,全部一股腦的裝到記憶體裡然後排序在輸出是不現實的。這時候,就需要進行外排序,這時資料不會一次性全部進入記憶體。

在看接下來的內容之前,強烈建議讀者在紙上演示一下,更能加深理解。

我們可以先建立乙個敗者樹。所謂敗者,就是在兩兩比較中不被選中的數,比方說我想要排成公升序,那麼兩數之間較大的數就不會被選中,那它就是敗者。b[i]表示從第i個檔案中按順序取出的數(模擬一下牌堆。。),這作為敗者樹的葉節點。然後,兩子節點的父節點儲存的是敗者的所在檔案編號,勝者就晉級去更高一層比賽,那麼冠軍就應該是我們要的數(通過b[losertree[0]]得到)。接下來,每個節點都記錄了每場比賽的敗者,這些資訊將成為優化的關鍵。我們從勝者所在的檔案裡再取乙個數,儲存在b[s]中,然後我們只需要更新從葉節點s一路更新到根節點,不需要理會其他節點。因為其他節點所代表的比賽結果和決定接下來的勝者沒有關係。

這樣一來,我們在o(k)的時間內決定第乙個勝者,然後不斷地用o(lgk)的時間決定下乙個勝者。

/*以下程式執行k路歸併排序,最終結果為公升序

*/#include#include#includeusing namespace std;

#define k 3 //歸併排序的路數

#define flag 100

/*表示檔案結束的「哨兵值」,每個輸入檔案的末尾都有它,對於公升序排序,他應該是比

所有待排的數都大,這樣只要還有數沒排,它總是個敗者。那如果他晉級成了冠軍呢?

*/#define key -1

/*key用於初始化敗者樹中的敗者,這應該是乙個比待排序檔案中任何數都

小的數,保證第一輪調整能順利進行(使每個與他比較的數都成為敗者)。

由於不將其輸出,這個冠軍被「忽略」。

*/file *fp[k+1];//fp[0]到fp[k-1]為輸入檔案(小牌堆),fp[k]為輸出檔案(最終的總牌堆)。

int losertree[k]; //敗者樹,在這裡用順序儲存結構。儲存的是每個敗者所在的檔案編號

int b[k+1]; //我們會將key放在b[k]中,b陣列的含義同前文

int input(int i) //從第i個檔案中取數

void output(int val) //將val輸出到輸出檔案中,fp[k]是指向輸出檔案的指標

void adjust(int s) //從葉節點s向根節點一路更新,t為父節點,losertree[t]儲存的是敗者

t=t/2; //這樣可以得到父節點

}losertree[0]=s; //這一趟比賽最後的冠軍!!

}void createlosertree() //建立敗者樹

{ b[k]=key;//不懂這一步可以翻看前文預編譯key的那一句

for(int i=0;i為什麼我們最後要把flag對應的哨兵值也輸出呢?考慮這一種情況:待排的檔案數目也相當多,也許有上百萬,那麼我們不能這些檔案一次排完,要分多次執行程式。那麼這次的輸出可能成為下次的輸入,輸入檔案最後當然要有flag啦。。

如果是降序呢?首先flag的值要調整為乙個足夠小的數,然後key(第一輪比賽中注定的勝者)要調整為乙個足夠大的數。比賽規則要改:b[s]>b[lowertree[0]]改為b[s]

歸併排序(二路歸併)

歸併排序是一種遞迴思想的體現,通過多次合併較小的幾個 二路歸併為兩個 有序陣列形成新的有序表。思路 將陣列分為n nn個一元組,兩兩合併得到二元組,以此類推共合併log 2n log 2n log2 n 次後,陣列變得有序。時間複雜度為o n logn o nlogn o nlog n 編譯環境de...

二路歸併排序

不是困難的演算法,不過也是練習了下遞迴。include include include using namespace std const int maxn 100 5 int a maxn int b maxn void mergesort int a,int b,int begin,int en...

二路歸併排序

二路歸併排序是將兩個有序表合併成乙個有序表的排序方法。其基本思想為 序列中有 n個記錄,可以看作為 n個有序子串行,每個序列長度為 1。首先將每相鄰的兩個記錄合併,得到 n 2 個較大的有序子串行,每個序列長度為 2。再將上述子串行兩兩合併,得到 n 2 2 個有序子串行,直至得到乙個長度為 n的有...