出處:http://blog.csdn.net/MichealTX/article/details/7172910
問題:請給出一個時間爲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 10
- typedef 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;
- }