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

2021-09-07 09:25:25 字數 2585 閱讀 3644

比如檔案內有1億資料排序。

程式設計珠璣第乙個case是有關乙個技巧性解決外部排序問題的。問題很巧妙的解決了,但一開始提到的利用歸併排序進行外部排序的演算法仍值得仔細**一下,畢竟本科時學的不是很深入。

先來看內部排序中最簡單的2路歸併排序演算法。

演算法核心操作是將一維陣列中前後相鄰的兩個有序序列歸併為乙個有序序列,給定陣列中序列界限i、m、n,用2個下標變數分別從i和j=m+1開始逐個往後處理,先比較,小的寫到結果序列的當前遍歷下標k中,相應下標自增繼續比較直到某個序列的下標走到邊界,再將另外乙個序列的剩餘元素拷貝到結果序列中。

演算法可用遞迴或遞推實現,從相鄰的兩兩元素開始不斷呼叫上面的核心操作組成較長有序序列直到完成整個序列。

演算法進行一趟歸併就得到乙個區域性有序的完整新序列,n個元素共需要log2n趟歸併,每趟完成比較操作n次(1次得到序列的1個值),得到的新序列寫到結果序列空間中,下一趟之前要先將結果序列複製乙份到臨時空間,下一趟歸併在臨時空間上進行。因此時間複雜度nlog2n,空間上除了原始序列空間n、結果序列空間n,還需要輔助臨時空間n。

接下來看外部排序。外部排序指的是大檔案的排序,即待排序的記錄儲存在外儲存器上,待排序的檔案無法一次裝入記憶體,需要在記憶體和外部儲存器之間進行多次資料交換,以達到排序整個檔案的目的。外部排序最常用的演算法是多路歸併排序,即將原檔案分解成多個能夠一次性裝入記憶體的部分,分別把每一部分調入記憶體完成排序。然後,對已經排序的子檔案進行多路歸併排序

多路歸併排序演算法在常見資料結構書中都有涉及。從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增長而增長了,抵消了外存讀寫減少的時間,這樣做不行,由此引出了「敗者樹」treeof 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的值。

多路歸併排序演算法的過程大致為:首先將k個歸併段中的首元素關鍵字依次存入b[0]--b[k-1]的葉子結點空間裡,然後呼叫createlosertree建立敗者樹,建立完畢之後最小的關鍵字下標(即所在歸併段的序號)便被存入ls[0]中。然後不斷迴圈:把ls[0]所存最小關鍵字來自於哪個歸併段的序號得到為q,將該歸併段的首元素輸出到有序歸併段裡,然後把下乙個元素關鍵字放入上乙個元素本來所在的葉子結點b[q]中,呼叫adjust順著b[q]這個葉子結點往上調整敗者樹直到新的最小的關鍵字被選出來,其下標同樣存在ls[0]中。迴圈這個操作過程直至所有元素被寫到有序歸併段裡。

偽**如下:

void adjust(losertree &ls,int s)/*從葉子結點b[s]到根結點的父結點ls[0]調整敗者樹*/          t=t/2;                    /*向樹根退一層,找到父結點*/   }  ls[0]=s;                           /*ls[0]記錄本趟最小關鍵字所在的段號*/}

void k_merge( int ls[k])/*ls[0]~ls[k-1]是敗者樹的內部比較結點。b[0]~b[k-1]分別儲存k個初始歸併段的當前記錄*//*函式get_next(i)用於從第i個歸併段讀取並返回當前記錄*/} 

最後,對使用多路歸併排序來進行外部排序的過程大致描述一下:根據有限的記憶體資源將大檔案分為l個段,然後依次將這l個段讀入記憶體並利用高效的內部排序演算法對每個段進行排序,排序後的結果即為初始有序歸併段直接寫入外存檔案。內部排序時要選擇合適的排序演算法,並且要考慮到內部排序需要的輔助空間以及有限的記憶體空間來決定究竟要把大檔案分為幾個段。接下來選擇合適的路數k對這l個歸併段進行多路歸併排序,每一趟歸併使k個歸併段變為1個較大歸併段寫入檔案,反覆幾趟歸併後得到整個有序的檔案。在多路歸併過程中,記憶體空間只需要維護乙個大小為2k的敗者樹,資料取、放都是對應外存的讀寫,這樣的話一次把一大塊資料讀入記憶體、把記憶體中排好的一大塊資料寫入檔案比較省時,不知這個需要程式設計師程式設計安排還是os能通過虛擬頁面檔案直接幫忙做到。找出計算機組成原理的課本回顧下發現,自認為用虛擬頁面檔案管理解決這個問題完全是風馬牛不相及的。段頁式虛擬儲存是將程式的邏輯空間以段頁式來管理,而要排序的檔案不屬於程式本身的邏輯空間。實際上,這個問題應該從磁碟本身提供的快取記憶體方面來考慮。現在磁碟一般都有幾m到十幾m的快取記憶體,利用資料訪問的空間區域性性和時間區域性性規則,使用預讀策略,一次性將一塊資料讀入快取記憶體,再次讀寫時則先檢查cache中是否能夠命中,如能命中則不需去碟片上讀。若cache空間不足以提高讀寫速率,則需要程式設計師編寫程式將大塊資料讀入寫出。

多路歸併排序

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

多路歸併排序

下面的問題描述及相關文字都參考於csdn中july的部落格,在此對july表示感謝。july的部落格位址如下 在對海量資料排序時,有時不能把資料全部匯入到記憶體中,這時需要用到多路歸併排序。比如輸入 乙個最多含有n個不重複的正整數 也就是說可能含有少於n個不重複正整數 的檔案,其中每個數都 小於等於...

多路歸併排序

雪壓枝頭低,雖低不著泥 今天準備放假,無聊看到乙個場景題,問題如下 有乙個檔案裡面儲存著很多很多很多的無序的數,然後要求進行乙個排序,記憶體限定,磁碟足夠 然後搗鼓了一下,學到了一種新技能 多路歸併排序 學習的過程是這樣的 第一步 把儲存著很多很多很多數的檔案進行切割,切割成n個小檔案,每個小檔案都...