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