题干描述

合并K个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:


输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6


解法一

让链表两两合并,合并之后的链表再和第三个链表合并。

时间复杂度:k 为链表个数,n 为总的结点数,两两归并,每个结点会被归并 log(k) 次,所以总的时间复杂度为 O(nlog(k)) 。空间复杂度为 O(1)。

public class ListNode {
public int val;
public ListNode next;

public ListNode(int val) {
this.val = val;
}

public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}

public int getData() {
return val;
}
}
class Solution {
public static ListNode mergeKLists(ListNode[] lists) {

ListNode res = new ListNode(0); //设置结果
if (lists == null || lists.length < 0) {
return null;
} else if (lists.length == 1) {
return lists[0];
} else if (lists.length == 2) {
mergeTwoLists(lists[0], lists[1]);
} else {
res = mergeTwoLists(lists[0], lists[1]);
for (int i = 2; i < lists.length; i++) {
mergeTwoLists(res, lists[i]);
}
}
return res;
}

public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode tmp = res;

while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tmp.next = l1;
l1 = l1.next;
} else {
tmp.next = l2;
l2 = l2.next;
}
tmp = tmp.next;
}
//后面是为了补全的,因为链表的长度可能不一样
if (l1 != null) {
tmp.next = l1;
} else {
tmp.next = l2;
}
return res.next;
}

public static void main(String[] args) {

// 前一个节点,指向后一个节点
ListNode node5 = new ListNode(5, null);
ListNode node4 = new ListNode(4, node5);
ListNode node3 = new ListNode(3, node4);
ListNode node2 = new ListNode(2, node3);
ListNode node1 = new ListNode(1, node2);

ListNode node8 = new ListNode(7, null);
ListNode node7 = new ListNode(5, node8);
ListNode node6 = new ListNode(3, node7);

ListNode node11 = new ListNode(6, null);
ListNode node10 = new ListNode(4, node11);
ListNode node9 = new ListNode(2, node10);

ListNode[] lists = new ListNode[3];

lists[0] = node1;
lists[1] = node6;
lists[2] = node9;
System.out.println(lists);

ListNode listNode = mergeKLists(lists);

while (listNode != null) {
System.out.println(listNode.getData());
listNode = listNode.next;
}

}
}

解法二

用容量为k 的最小堆优先队列,把链表的头结点都放进去,然后出队当前优先队列中最小的,挂上链表,然后让出队的那个节点的下一个入队,再出队当前优先队列中最小的,直到优先队列为空。

借助优先队列(小顶堆)的解法的时间复杂度为O(nlog(k)),k 为链表个数,n 为总的结点数,空间复杂度为O(k),小顶堆需要维护一个长度为k 的数组。

【数据结构与算法】合并K个排序链表(Java版)_优先队列

class Solution {
public ListNode mergeKLists(ListNode[] lists) {

if (lists.length == 0) {
return null;
}

ListNode dummyHead = new ListNode(0);
ListNode curr = dummyHead;
PriorityQueue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});

for (ListNode list : lists) {
if (list == null) {
continue;
}
pq.add(list);
}

while (!pq.isEmpty()) {
ListNode nextNode = pq.poll();
curr.next = nextNode;
curr = curr.next;
if (nextNode.next != null) {
pq.add(nextNode.next);
}
}
return dummyHead.next;
}
}