合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
详解:
将K个排序链表合并成一个,假设合并之后的总长度是n。
我们以前经常说讲两个排序列表合并成一个排序列表(他的长度为n),时间复杂度为o(n)
那这个题目变成了k个,如果还是按照原来的方法,可以看到,每一次的计算都要遍历k个数组拿到最小值,复杂度就是o(n*k),显然慢了。那怎么优化呢?
深入一点理解刚才的思路,就是先从k个列表的第一个数,找出最小数,加入新列表,然后从这个最小数所在列表的第二个数和其他k-1个列表,在找出最小的数,加入新列表,以此类推。
是不是听着听着,感觉似曾相识?找最小,更新他的值,再找最小,以此类推。
对的,没错,不用怕,哪怕是链表,依然甩出宝刀,线段树。
我擦,这是链表阿,怎么建树?
对k建树,进行n次查询和更新即可。
然后看看时间复杂度:o(n*logk)
这样去理解这就是典型的线段树了,只是插入和更新都是操作的链表节点,编码不能马虎,不然容易出bug。
至于线段树的原理,这里就不详说了,下次有机会再开一贴详解,或者自行去搜。
再详细的思路也不及代码片行,那直接贴代码:
private Node[] nodeList;
public ListNode mergeKLists(ListNode[] lists) {
int length = lists.length;
if(length < 1){
return null;
}
nodeList = new Node[length << 2];
build(1, 0, length - 1, lists);
ListNode result = null, tmp = null;
while(lists[nodeList[1].k] != null){
if(result == null){
result = lists[nodeList[1].k];
lists[nodeList[1].k] = lists[nodeList[1].k].next;
tmp = result;
result.next = null;
}else{
tmp.next = lists[nodeList[1].k];
lists[nodeList[1].k] = lists[nodeList[1].k].next;
tmp = tmp.next;
tmp.next = null;
}
change(1, nodeList[1].k, (lists[nodeList[1].k] == null) ? Integer.MAX_VALUE : lists[nodeList[1].k].val);
}
return result;
}
public class Node{
int left;
int right;
int minValue;
int k;
}
public void update(int n){
if(nodeList[n << 1].minValue < nodeList[(n << 1) + 1].minValue){
nodeList[n].minValue = nodeList[n << 1].minValue;
nodeList[n].k = nodeList[n << 1].k;
}else{
nodeList[n].minValue = nodeList[(n << 1) + 1].minValue;
nodeList[n].k = nodeList[(n << 1) + 1].k;
}
}
public void build(int n, int left, int right, ListNode[] lists){
Node node = new Node();
nodeList[n] = node;
node.left = left;
node.right = right;
if(left == right){
if(lists[left] != null){
node.minValue = lists[left].val;
}else{
node.minValue = Integer.MAX_VALUE;
}
node.k = left;
return;
}
int mid = (left + right) >> 1;
build(n << 1, left, mid, lists);
build((n << 1) + 1, mid + 1, right, lists);
update(n);
}
public void change(int n, int k, int value){
if(nodeList[n].left == nodeList[n].right){
nodeList[n].minValue = value;
nodeList[n].k = k;
return;
}
int mid = (nodeList[n].left + nodeList[n].right) >> 1;
if(k <= mid){
change(n << 1, k, value);
}else{
change((n << 1) + 1, k, value);
}
update(n);
}
最后贴效率:
写在后面:
这个是我的思路,有其他的思路也欢迎留言学习,毕竟算法无界,可能还有更优的算法也未尝不可能。