问题描述
问题分析
一看到这种合并K个数组/链表的题目,立即就要想到分治法:
合并K个链表,可理解为先将其划分为两组 K 2 \frac{K}{2} 2K个链表分别进行合并,两个 K 2 \frac{K}{2} 2K个链表又可以划分为四个 K 4 \frac{K}{4} 4K个链表……直到划分为每组中只有一个链表,然后在将各组合并结果依次层层合并,即可得到最终合并结果
合并图示如下(转载于官方题解):
解法:分治法
- 时间复杂度:O( N log k N\log{k} Nlogk ),其中 N N N表示所有链表的节点总数, k k k表示链表数。因为分治的特点,划分的次数为 log 2 k \log_2{k} log2k次。另外,合并两个链表的时间主要花费在比较上,若两个链表的总节点数为 n n n,那么一次合并所需的时间为O( n n n )。综合来看,总的花费时间为O( N log k N\log{k} Nlogk )。
- 空间复杂度:O( 1 1 1 )。空间消耗主要在于合并两个链表时,由于是只申请了常量级的空间,并且链表间就地合并,所以只花费了O( 1 1 1 )的空间。
Java代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 0){
return null;
}
//定义长度与步长
int len = lists.length;
int step = 1;
//按照不同步长扫描一遍
while (step < len){
//依次合并
for (int i = 0; i + step < len; i = i + step * 2) {
lists[i] = merge(lists[i], lists[i+step]);
}
//步长更新
step *= 2;
}
return lists[0];
}
static ListNode merge(ListNode a, ListNode b){
//指针
ListNode curA = a;
ListNode curB = b;
ListNode result = new ListNode(0);
ListNode cur = result;
while (curA != null && curB != null){
cur.next = curA.val < curB.val ? curA : curB;
cur = cur.next;
if (curA.val < curB.val){
curA = curA.next;
}else {
curB = curB.next;
}
}
if (curA != null){
cur.next = curA;
}else {
cur.next = curB;
}
return result.next;
}
}
结果分析
以上代码的执行结果:
执行时间 | 内存消耗 |
---|---|
7 ms | 42.8MB |