問題:請給出一個時間為O(nlgk),用來將k個已排序鏈表合并為一個排序鏈表的演算法。此處的n為所有輸入鏈表中元素的總數。(提示:用一個最小堆來做k路合并)
編程思路:
假設k個鏈表都是非降序排列的。
(1)取k個元素建立最小堆,這k個元素分別是k個鏈表的第一個元素。建堆的時間複雜度O(k)。
(2)堆頂元素就是k個鏈表中最小的那個元素,取出它。時間複雜度O(1)。
(3)若堆頂元素所在鏈表不為空白,則取下一個元素放到堆頂位置,這可能破壞了最小堆性質,所以進行堆調整。堆調整時間複雜度O(lgk)。若為空白,則此子鏈表已經被合并完畢,則刪除最小堆的堆頂元素,此時最小堆的heapSize減小了1 。刪除指定元素時間複雜度O(lgk)。
(4)重複步驟(2)~(3)n-k次。總的時間複雜度是O(k)+O(nlgk)即O(nlgk)。
要先定義一個資料結構Node,主要是為了找鏈表所在索引方便,這個程式我先用隨機函數產生4個子鏈表(一個二維數組,為了省事就讓每個子鏈表的元素個數相同)然後用冒泡排序分別對每個鏈表排序,然後合并它們。
#include <stdio.h>#include <string.h>#include <time.h>#define BUFFER_SIZE 10typedef struct{int data;int index;}Node;void Output(Node (*a)[BUFFER_SIZE],int k,int len){int i=0;int j=0;for(i=0;i<k;i++){printf("第%d個鏈表:",i); for(j=0;j<len;j++){printf("%d ",a[i][j].data);}printf("\n");}} void BubbleSort(Node (*a)[BUFFER_SIZE],int k,int len){int i=0;int j=0;int h=0;int tmp=0;for(h=0;h<k;h++){for(i=1;i<len;i++){for(j=0;j<len-i;j++){if(a[h][j].data>a[h][j+1].data){//因為每一次都是在每個子鏈表內部排序,所以就沒有必要交換index了,因為同一個子鏈表中的index都是一樣的 tmp=a[h][j].data;a[h][j].data=a[h][j+1].data;a[h][j+1].data=tmp;}}}}}//堆調整,保持堆的性質 void MinHeapIfy(Node *a,int i,int heapSize){int smallest=0;int left=0;int right=0;Node tmp;while(i<heapSize){left=i<<1;right=(i<<1)+1;if(left<=heapSize&&a[i].data>a[left].data){smallest=left;}else{smallest=i;}if(right<=heapSize&&a[smallest].data>a[right].data){smallest=right;}if(i!=smallest){//在C++中針對Node可以定義一個賦值建構函式就不用像下面這樣子交換了 tmp.data=a[i].data;tmp.index=a[i].index;a[i].data=a[smallest].data;a[i].index=a[smallest].index;a[smallest].data=tmp.data;a[smallest].index=tmp.index;i=smallest;}else{break;}}} //建堆 void BuildMinHeap(Node *a,int heapSize){int i=0;for(i=heapSize/2;i>1;i--){MinHeapIfy(a,i,heapSize);}} //刪除堆中指定元素void MinHeapDelete(Node *a,int i,int *heapSize) {Node tmp;tmp.data=a[i].data;tmp.index=a[i].index;a[i].data=a[*heapSize].data;a[i].index=a[*heapSize].index;if(a[i].data==tmp.data){(*heapSize)--;}else if(a[i].data>tmp.data){(*heapSize)--;MinHeapIfy(a,i,*heapSize);}else if(a[i].data<tmp.data){(*heapSize)--;while(i>1&&a[i>>1].data>a[i].data){tmp.data=a[i].data;tmp.index=a[i].index;a[i].data=a[i>>1].data;a[i].index=a[i>>1].index;a[i>>1].data=tmp.data;a[i>>1].index=tmp.index;}}}//合并k個子鏈表 void Merge(Node (*a)[BUFFER_SIZE],int k,Node *result){int heapSize=0;int i=0;Node tmp[BUFFER_SIZE+1];int j=0;int index=0;int n=k*BUFFER_SIZE;//k個鏈表中的元素總數 int b[k];//用來記錄每個子鏈表該哪個元素添加進堆中 memset(b,0,sizeof(b)); //用每個鏈表的第一個元素,共k個,建立最小堆 for(i=0;i<k;i++){tmp[i+1].data=a[i][0].data;tmp[i+1].index=a[i][0].index;b[i]++; }heapSize=k;BuildMinHeap(tmp,heapSize);while(n>0){//將堆頂元素放入輸出鏈表 result[j].data=tmp[1].data;result[j].index=tmp[1].index;index=result[j].index;j++;n--;//子鏈表少了一個元素if(b[index]<BUFFER_SIZE){//該子鏈表還未空,則繼續從該子鏈表選取元素入堆 //將index子鏈表的下一個元素放到堆頂,然後進行堆調整 tmp[1].data=a[index][b[index]].data;tmp[1].index=a[index][b[index]].index;b[index]++;//index子鏈表待加入堆中的下一個元素 MinHeapIfy(tmp,1,heapSize);}else{MinHeapDelete(tmp,1,&heapSize);}}BuildMinHeap(tmp,k);}int main(){int i=0;int j=0;Node a[4][BUFFER_SIZE];Node result[4*BUFFER_SIZE];//隨機產生k個鏈表,k=4 srand((unsigned)time(NULL));for(i=0;i<4;i++){for(j=0;j<BUFFER_SIZE;j++){a[i][j].data=rand()%1000;a[i][j].index=i;}} printf("隨機產生的k個鏈表:\n");Output(a,4,BUFFER_SIZE); BubbleSort(a,4,BUFFER_SIZE);printf("對k個鏈表進行冒泡排序:\n"); Output(a,4,BUFFER_SIZE);Merge(a,4,result);printf("合并K個鏈表:\n"); for(i=0;i<4*BUFFER_SIZE;i++){printf("%d ",result[i].data);}system("pause");return 0;}