题目描述:合并k个有序链表。
题目链接:Leetcode 23. Merge k Sorted List
思路就是调用合并两个有序链表的程序,不断调用直到最后但是这个算法复杂度貌似比较大。
事实上一看这样有序的东西一般都是二分的思路,要么二分查找,要么二分定位合并,这里就是二分归并。但是我最喜欢的还是优先级队列的做法,维护一个各链表顶点的优先级队列,每次取出最小并压入取出的下一个节点。(美妙)
该算法维护一个大小为k的堆,每次都会取堆顶的最小元素放到结果中,然后读取该元素的下一个元素(若为空,跳过)放入堆中,重新维护好。因为
每个链表是有序的,每次又是取当前k个元素中最小的,所以当所有链表都读完时结束,这个时候所有元素按从小到大放在结果链表中。这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要logk的复杂度,所以总时间复杂度是O(nklogk)。空间复杂度是堆的大小,即为
O(k)。
代码如下
// 堆的做法 简直不要太美
class Solution {
static class NodeComparator implements Comparator<ListNode> {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val; //小到大
}
}
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 1) {
return lists[0];
}
if (lists.length == 0) {
return null;
}
PriorityQueue<ListNode> mn = new PriorityQueue(new NodeComparator());
for (ListNode node : lists) {
if (node != null){
mn.offer(node);
}
}
ListNode ans = new ListNode(0);
ListNode p = ans;
while (mn.size() != 0) {
ListNode mnNode = mn.poll(); //移除堆顶的元素
p.next = mnNode;
mnNode = mnNode.next;
if (mnNode != null) {
mn.offer(mnNode);
}
p = p.next;
}
return ans.next;
}
}
public ListNode mergeKLists(ListNode[] lists) {
if (lists.length == 1) {
return lists[0];
}
if (lists.length == 0) {
return null;
}
ListNode l1 = lists[0];
ListNode l2 = lists[1];
ListNode ans = mergeTwoLists(l1,l2);
int idx = 2;
while(idx < lists.length){
ans = mergeTwoLists(lists[idx],ans);
idx++;
}
return ans;
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode ans = new ListNode(0);
ListNode p = ans;
while(l1 != null && l2 != null) {
if (l1.val < l2.val) {
p.next = l1;
l1 = l1.next;
p = p.next;
}else{
p.next = l2;
l2 = l2.next;
p = p.next;
}
}
ListNode res = (l1 == null) ? l2 : l1;
while (res != null){
p.next = res;
res = res.next;
p = p.next;
}
return ans.next;
}