输入:k个有序链表lists
输出:一个有序链表
规则:将这个k个有序链表合并成一个有序链表
分析:在链表中合并两个有序链表为一个有序链表是基本功。最开始的直觉是我们可以将lists[0]和lists[1]合并得到 result,result再和lists[2]合并,一直 到最后合并完成。这个每个节点 都要和其他链表中的一个节点比较,所以时间复杂度是O(k*n)。n是所有节点的个数总和。
public ListNode mergeKLists(ListNode[] lists) {
ListNode mergeHead = new ListNode(0);
ListNode node = mergeHead;
while(true){
ListNode minHead = null;
int idx = -1;
for (int i=0;i<lists.length;i++) {
if (lists[i] != null) {
if(minHead == null || minHead.val> lists[i].val){
minHead = lists[i];
idx = i;
}
}
}
if(minHead==null){
break;
}
lists[idx] = lists[idx].next;
node.next = new ListNode(minHead.val);
node = node.next;
}
return mergeHead.next;
}
分析2:可以使用堆排序解决上面的比较问题。在堆中维护的是每个链表 表头元素。移除最小的节点,插入链表中的下一个节点。时间复杂度O(nlogk)。
public ListNode mergeKLists(ListNode[] lists) {
ListNode dummy = new ListNode(-1);
ListNode current = dummy;
PriorityQueue<ListNode> heap = new PriorityQueue<ListNode>(new Comparator<ListNode>(){
public int compare(ListNode i1,ListNode i2){
return i1.val-i2.val;
}
});
//把头节点放进去
for(ListNode node: lists){
if(node!=null){
heap.offer(node);
}
}
while(!heap.isEmpty()){
ListNode node = heap.poll();
if(node.next != null){
heap.offer(node.next);
}
current.next = node;
current = current.next;
}
return dummy.next;
}
分析3:使用分治法。先解决lists[0]和lists[1],lists[2]和lists[3]…,合并完成只剩下 k 2 \dfrac{k}{2} 2k。下一轮解决lists[0]和lists[2],lists[4]和lists[6]…,合并完成剩下 k 4 \dfrac{k}{4} 4k…一直到最后只剩下lists[0],就是结果。
public ListNode mergeKLists(ListNode[] lists) {
for(int interval =1 ;interval<lists.length; interval=interval*2){
for(int j = 0;j+interval<lists.length;j = j + 2*interval){
lists[j] = mergeTwoLists(lists[j],lists[j+interval]);
}
}
return lists.length>0?lists[0]:null;
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode currentNode = dummy;
while(l1!=null && l2!=null){
if(l1.val > l2.val){
currentNode.next = l2;
l2 = l2.next;
}else{
currentNode.next = l1;
l1 = l1.next;
}
currentNode = currentNode.next;
}
while(l1!=null){
currentNode.next = l1;
l1 = l1.next;
currentNode = currentNode.next;
}
while(l2!=null){
currentNode.next = l2;
l2 = l2.next;
currentNode = currentNode.next;
}
return dummy.next;
}
复杂度分析。最外层循环有 l o g 2 k log_2^k log2k次。mergeTwoLists函数的时间复杂度是n=两个列表节点个数的和。最后合起来是O(nlogk)。这里的n是所有链表节点个数和。