敗者樹 K-路歸併排序 - 天堂人間 - 部落格頻道 - CSDN.NET
敗者樹 K-路歸併排序 分類: 演算法學習 排序 2011-09-30 22:15 628人閱讀 評論(0) 收藏 舉報資料結構merge演算法儲存structtree
外部排序指的是大檔案的排序,即待排序的記錄儲存在外儲存空間上,待排序的檔案無法一次裝入記憶體,需要在記憶體和外部儲存空間之間進行多次資料交換,以達到排序整個檔案的目的。外部排序最常用的演算法是多路歸併排序,即將原檔案分解成多個能夠一次性裝入記憶體的部分,分別把每一部分調入記憶體完成排序。然後,對已經排序的子檔案進行多路歸併排序。
多路歸併排序演算法在常見資料結構書中都有涉及。從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的值。
多路歸併排序演算法的過程大致為:首先將k個歸併段中的首元素關鍵字依次存入b[0]--b[k-1]的葉子結點空間裡,然後調用CreateLoserTree建立敗者樹,建立完畢之後最小的關鍵字下標(即所在歸併段的序號)便被存入ls[0]中。然後不斷迴圈:把ls[0]所存最小關鍵字來自於哪個歸併段的序號得到為q,將該歸併段的首元素輸出到有序歸併段裡,然後把下一個元素關鍵字放入上一個元素本來所在的葉子結點b[q]中,調用Adjust順著b[q]這個葉子結點往上調整敗者樹直到新的最小的關鍵字被選出來,其下標同樣存在ls[0]中。迴圈這個操作過程直至所有元素被寫到有序歸併段裡。
以上資料借鑒 http://chenkegarfield.blog.163.com/blog/static/62330008200910249526638/
參考資料結構(C語言版)嚴蔚敏教材P298頁虛擬碼,用C++實現
代碼如下:
[cpp] view plaincopyprint?
- #include <iostream>
- using namespace std;
-
- #define LEN 10 //最大歸併段長
- #define MINKEY -1 //預設全為正數
- #define MAXKEY 100 //最大值,當一個段全部輸出後的賦值
-
- struct Array
- {
- int arr[LEN];
- int num;
- int pos;
- }*A;
-
- int k,count;
- int *LoserTree,*External;
-
- void Adjust(int s)
- {
- int t=(s+k)/2;
- int temp;
- while(t>0)
- {
- if(External[s] > External[LoserTree[t]])
- {
- temp = s;
- s = LoserTree[t];
- LoserTree[t]=temp;
- }
- t=t/2;
- }
- LoserTree[0]=s;
- }
-
- void CreateLoserTree()
- {
- External[k]=MINKEY;
- int i;
- for(i=0;i<k;i++)LoserTree[i]=k;
- for(i=k-1;i>=0;i--)Adjust(i);
- }
-
- void K_Merge()
- {
- int i,p;
- for(i=0;i<k;i++)
- {
- p = A[i].pos;
- External[i]=A[i].arr[p];
- //cout<<External[i]<<",";
- A[i].pos++;
- }
- CreateLoserTree();
- int NO = 0;
- while(NO<count)
- {
- p=LoserTree[0];
- cout<<External[p]<<",";
- NO++;
- if(A[p].pos>=A[p].num)External[p]=MAXKEY;
- else
- {
- External[p]=A[p].arr[A[p].pos];
- A[p].pos++;
- }
- Adjust(p);
- }
- cout<<endl;
- }
-
- int main()
- {
- freopen("in.txt","r",stdin);
-
- int i,j;
- count=0;
- cin>>k;
- A=(Array *)malloc(sizeof(Array)*k);
- for(i=0;i<k;i++)
- {
- cin>>A[i].num;
- count=count+A[i].num;
- for(j=0;j<A[i].num;j++)
- {
- cin>>A[i].arr[j];
- }
- A[i].pos=0;
- }
- LoserTree=(int *)malloc(sizeof(int)*k);
- External=(int *)malloc(sizeof(int)*(k+1));
-
- K_Merge();
-
- return 0;
- }
#include <iostream>using namespace std;#define LEN 10//最大歸併段長#define MINKEY -1 //預設全為正數#define MAXKEY 100 //最大值,當一個段全部輸出後的賦值struct Array{int arr[LEN];int num;int pos;}*A;int k,count;int *LoserTree,*External;void Adjust(int s){int t=(s+k)/2;int temp;while(t>0){if(External[s] > External[LoserTree[t]]){temp = s;s = LoserTree[t];LoserTree[t]=temp;}t=t/2;}LoserTree[0]=s;}void CreateLoserTree(){External[k]=MINKEY;int i;for(i=0;i<k;i++)LoserTree[i]=k;for(i=k-1;i>=0;i--)Adjust(i);}void K_Merge(){int i,p;for(i=0;i<k;i++){p = A[i].pos;External[i]=A[i].arr[p];//cout<<External[i]<<",";A[i].pos++;}CreateLoserTree();int NO = 0;while(NO<count){p=LoserTree[0];cout<<External[p]<<",";NO++;if(A[p].pos>=A[p].num)External[p]=MAXKEY;else {External[p]=A[p].arr[A[p].pos];A[p].pos++;}Adjust(p);}cout<<endl;}int main(){freopen("in.txt","r",stdin);int i,j;count=0;cin>>k;A=(Array *)malloc(sizeof(Array)*k);for(i=0;i<k;i++){cin>>A[i].num;count=count+A[i].num;for(j=0;j<A[i].num;j++){cin>>A[i].arr[j];}A[i].pos=0;}LoserTree=(int *)malloc(sizeof(int)*k);External=(int *)malloc(sizeof(int)*(k+1));K_Merge();return 0;}
輸入檔案 如下:
5
5
1 5 6 8 25
6
2 6 9 25 30 32
3
5 9 16
6
6 9 15 24 30 36
2
8 34