题目:k个已排序链表,元素总数是n,要求在nlgk时间复杂度内把k个链表合并为一个有序链表。
思路:取k个链表中每一个链表的第一个元素,共k个,将这k个元素建立一个最大堆(或最小堆),取出堆中的第一个元素放到数组str中,将堆中第一个元素所对应的链表中的该元素去掉,链表中的第二个元素上升为第一个元素。并把该元素插入到最大堆中的根节点,调整最大堆,然后仍然取出堆中根元素放入到str中,将该元素对应的链表下一个元素放入堆中,调整最大堆。当某一个链表中的元素取光时,将堆中的最后一个元素放到根节点,调整堆。直到所有链表中元素取光为止。
编写代码时,我遇到的一个小问题是,如何根据根元素的值确定该根元素属于哪一个链表,如果进行匹配搜索则时间复杂度就会变成nkLg(k).此处建立一个二维数组temp[100][2],其中temp[i][0]放链表的元素大小,temp[i][1]该元素属于第几个链表。以空间换时间的思路解决这个小问题。本代码写的有点麻烦,存放于链表中的元素完全可以直接放到数组中,但是为了倾向于面向对象的设计,本代码采用了建立链表对象的方法。具体代码如下:
LinkNode.h
#include<iostream>
using namespace std;
class Link;
class LinkNode
{
private:
int linkID;
int key;
LinkNode * next;
public:
friend Link;
LinkNode():next(NULL)
{}
LinkNode(int ID,int num,LinkNode *node=NULL):linkID(ID),key(num),next(node)
{
}
~LinkNode()
{
next=NULL;
}
int Getkey()
{
return this->key;
}
int GetLinkID()
{
return linkID;
}
};
Linki.h
//链表,实现了链表元素的排列,链表元素个数计算,元素删除
//元素添加链表元素查找。
#include<iostream>
#include"LinkNode.h"
using namespace std;
class Link
{
private:
LinkNode* head;
int length;
public:
Link():head(new LinkNode())
{
length=1;
}
~Link()
{
MakeEmpty();
delete head;
}
void MakeEmpty()
{
if(head->next==NULL)
{
return ;
}
LinkNode * p=head;
while(head->next!=NULL)
{
p=head->next;
head->next=p->next;
delete p;
}
}
//使用数组初始化链表
void Init(int num,int a[],int len)
{
int i;
this->length=len;
LinkNode * p=head;
for(i=0;i<len;i++)
{
p->key=a[i];
p->linkID=num;
LinkNode *q=new LinkNode();
p->next=q;
p=p->next;
}
}
int GetLength()
{
return length;
}
bool DeleteHead()
{
if(head==NULL)
{
cout<<"the link is empty!"<<endl;
return 0;
}
else
{
LinkNode* p;
p=head;
head=head->next;
delete p;
length-=1;
return 1;
}
}
LinkNode* GetHead()
{
if(head==NULL)
{
cout<<"the link is empty!"<<endl;
return NULL;
}
return this->head;
}
};
算法实现:
#include<iostream>
#include"Link.h"
using namespace std;
#define Left(i) i*2+1
#define Right(i) i*2+2
#define Parent(i) (i-1)/2
//此处添加利用循环方式代替递归Max_Heapify的函数,该函数可以替代Max_Heapify
//在某些情况下能取得更好 的效果
void Max_Heapify1(int (*a)[2],int length,int i)
{
int left,right,num=i,largest=i;
left=Left(i);
right=Right(i);
while(1)
{
if(a[left][0]>a[num][0]&&left<length)
{
largest=left;
}
//此处逻辑判断出错,开始为else if,发现不能正确排序,改为else之后正确
if(a[right][0]>a[largest][0]&&right<length)
{
largest=right;
}
if(num!=largest)
{
int temp;
temp=a[num][0];
a[num][0]=a[largest][0];
a[largest][0]=temp;
temp=a[num][1];
a[num][1]=a[largest][1];
a[largest][1]=temp;
num=largest;
left=Left(num);
right=Right(num);
}
else
{
break;
}
}
}
void Build_Max_Heap(int (*a)[2],int length )
{
int i;
for(i=(length-1)/2;i>=0;i--)
{
Max_Heapify1(a,length,i);
}
}
void InitLink(int (*b)[5],int k,int m,Link Node[])
{
int i;
for(i=0;i<k;i++)
{
Node[i].Init(i,b[i],m);
}
}
void SortLink(Link Node[],int k,int m,int str[])
{
// int *str=new int[100];
int temp[100][2]={0};
int i,j,t=0,count,s=k;
for(i=0;i<k;i++)
{
temp[i][0]=Node[i].GetHead()->Getkey();
temp[i][1]=Node[i].GetHead()->GetLinkID();
}
for(j=1;j<=k*m;j++)
{
Build_Max_Heap(temp,s);
str[t++]=temp[0][0];
count=temp[0][1];
Node[count].DeleteHead();
if(Node[count].GetLength()==0)
{
temp[0][1]=temp[s-1][1];
temp[0][0]=temp[s-1][0];
s--;
}
else
{
temp[0][1]=Node[count].GetHead()->GetLinkID();
temp[0][0]=Node[count].GetHead()->Getkey();
}
}
// return str;
}
int main()
{
int n,k,m,i;
int a[4][5]={{9,8,7,6,5},{5,4,3,2,1},{15,14,13,12,11},{29,28,27,26,25}};
n=20;k=4;m=5;
Link* Node=new Link[4];
InitLink(a,k,m,Node);
int st[100];
SortLink(Node,k,m,st);
for(i=0;i<20;i++)
{
cout<<st[i]<<" ";
}
// delete []st;
delete []Node;
cout<<endl;
cout<<"hHA"<<endl;
return 0;
}
代码中中需要注意的问题是:
二维数组作为参数传递的问题:
void InitLink(int (*b)[5],int k,int m,Link Node[]),形式如代码中,int(*b)[5],注意中括号中的数字必须写明,不能是int(*b)[]的格式。
第二个问题是:
在一个函数中new的指针,放到main函数中去delete,就会导致进程无法正常终止,按照C++语法应该是没问题,可能是编译器或机器有关。具体研究见后文。