一、题目
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
二、思路
首先,合并k个升序链表或许有难度,我们可以先试着合并2个升序链表。
/**
* 对于两个按照升序排列的链表,将它们合并到一个升序链表中,返回合并后的链表
*/
public ListNode mergeTwoLists(ListNode left,ListNode right) {
//合并后的新链表,采用两个指针指示,我们用headPointer.next指向链表的头节点,tailPointer指向链表的尾节点
ListNode headPointer=new ListNode();//空节点
ListNode tailPointer=headPointer;
//当left和right都不为空的时候,将较小的节点放入新链表
while(left!=null && right!=null) {
if(left.val<right.val) {
tailPointer.next=left;
tailPointer=left;
//将left的指针后移
left=left.next;
}else {
tailPointer.next=right;
tailPointer=right;
//将right的指针后移
right=right.next;
}
}
//如果left和right其中有一个为null,那就直接将tailPointer.next指向另一个ListNode就可以了。
tailPointer.next=left==null?right:left;
return headPointer.next;
}
然后,我们再思考合并k个升序链表的事情,只要顺序合并即可,代码如下:
public ListNode mergeKLists(ListNode[] lists) {
ListNode ans = null;
for (int i = 0; i < lists.length; i++) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
但是,很明显这个算法的时间复杂度非常高。
假设有k个链表,每个链表的长度为n,第一次合并之后 ans 的长度为n,第二次长度为2n,第 i 次长度为 i*n,第 i 次的时间复杂度为O(n+(i-1)*n)=O(i*n),总的时间复杂度为O(k²n)。
另外,我们想到与这个问题比较类似的,就是归并排序,可以采用分治的思想:将原问题分解为几个规模较小但类似原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。
具体到这个问题,操作如下:
- 分解: 将长度为length的链表数组lists,分解为两个长度为length/2的链表数组list1和list2。
- 解决: 使用递归方法,将链表数组list1 都合并到 left链表中,list2 都合并到 right链表中。(当链表数组的长度为1时,递归开始回升,不需要做任何操作,因为这个链表本身已经按照升序排列)
- 合并: 将left链表和right链表合并。
/**
* 将链表数组lists的第p项到第r项链表合并到一个链表
*/
public ListNode merge(ListNode[] lists,int p,int r) {
if(p>r) return null;
//这个链表数组的长度为1的时候,直接返回
if(p==r) return lists[p];
//以q为分界线,将数组lists分为lists1和lists2
int q=(p+r)/2;
//将lists1合并到left链表
ListNode left=merge(lists, p, q);
//将lists2合并到right链表
ListNode right=merge(lists, q+1, r);
//将left和right两个链表合并
return mergeTwoLists(left, right);
}
public ListNode mergeKListNodes(ListNode[] lists) {
return merge(lists, 0,lists.length-1);
}