合并 k 个排序链表,返回合并后的排序链表。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
1.最简单的思路,每次从k个链表中选择最小,加入目标链表中。简单分析一下,每次的比较选择,需要k-1次,则是O(k)的时间复杂度,假设一共有n个节点,则总的时间复杂度为O(nk)。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize)
{
//进行k路比较
struct ListNode** p=lists;
int i=0,min=-1,j=0; //min此次比较中最小的结点链表序号
struct ListNode* head,*tail,*pmin;
head=(struct ListNode*)malloc(sizeof(struct ListNode));
head->next=NULL; //构造表头结点易于对第一个节点的操作
tail=head; //尾结点,采用尾插法
int num=0; //用于指示已经有多少个空列表
pmin=NULL;
min=-1;
while(num<listsSize) //num=listsSize则说明k个链表都寻找完了
{
for(j=0,num=0; j<listsSize && lists[j]==NULL ; j++)
num++; //用于找到k个链表目前第一个不为空的值
if(j<listsSize)
{
pmin=lists[j];
min=j;
}
//printf("%d %d \n",lists[min]->val,min);
for(i=j+1; i<listsSize; i++) //进行比较找出最小的值和最小值的指针
{
if(lists[i]!=NULL && lists[i]->val < pmin->val)
{
min=i;
pmin=lists[i];
}
else if(lists[i]==NULL)
{
num++;
//printf("%d \n",i);
}
else
;
}
if(num<listsSize ) //将找到的结点插入链表,并对取出的结点的链表更新
{
lists[min]=lists[min]->next;
pmin->next=tail->next;
tail->next=pmin;
tail=pmin;
}
else if(min==-1)
{
return head->next;
}
}
return head->next;
}
测试结果,很显然,很耗时,看到下面时间(抱住自己,怎么滴,也得再搞一种更优更优的算法)如图:
2.我们再分析一下,如果采用两两合并,并归的思路,对于k链表,我们可以通过递归来二分下去,最后通过合并两个有序链表完成排序,时间复杂度,代码如下:
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* MergeTwo(struct ListNode* list1,struct ListNode* list2)
{
//合并两个链表
if(list1==NULL) return list2;
if(list2==NULL) return list1;
if(list1->val<list2->val)
{
list1->next=MergeTwo(list1->next,list2);
return list1;
}
else
{
list2->next=MergeTwo(list1,list2->next);
return list2;
}
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize)
{
//k路并归思路
struct ListNode* list1,*list2,*l;
if( listsSize==0 )
return NULL;
else if
(listsSize==1) //退出递归的两种特殊情况
return *lists;
else
{
list1=mergeKLists(lists,listsSize/2);
list2=mergeKLists(&(lists[listsSize/2]),listsSize-listsSize/2); //注意取地址
l=MergeTwo(list1,list2);
return l;
}
}
显然优于第一个算法,再一次证明选择好的算法思想的重要性!!!
不说了继续搬砖了!
2019.8.19-晚