问题:请给出一个时间为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;
- }