一 题目
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
Input: [ 1->4->5, 1->3->4, 2->6 ] Output: 1->1->2->3->4->4->5->6
二 分析
就K个链表,合并成一个有序链表。本题是hard级别。这个题目的解法很多,是经典的面试题目。
前面做过2个的链表合并的,LeetCode 21. Merge Two Sorted Lists
一下子扩展到K个,还是有点懵,一想到各种为空的判断就头大。
2.1 全部list排序
所以先用list做整体排序,再输出。测试case的数据造起来麻烦,所以直接提交测试了。
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
List<Integer> alist = new ArrayList();
for(ListNode node:lists){
while(node!= null){
alist.add(node.val);
node= node.next;
}
}
Collections.sort(alist);
ListNode res = new ListNode(-1);
ListNode pre = res;
for(Integer val:alist){
pre.next= new ListNode(val);
pre = pre.next;
}
return res.next;
}
}
Runtime: 7 ms, faster than 47.49% of Java online submissions for Merge k Sorted Lists.
Memory Usage: 43.2 MB, less than 27.33% of Java online submissions forMerge k Sorted Lists.
时间复杂度应该是排序的 O(NlogK)
2.2 优先级队列排序
思路跟上面差不多,只是把排序替换为优先级队列。代码比较简洁。
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
Queue<Integer> queue =new PriorityQueue();
for(ListNode node:lists){
while(node!= null){
queue.add(node.val);
node= node.next;
}
}
ListNode res = new ListNode(-1);
ListNode pre = res;
while(!queue.isEmpty()){
pre.next= new ListNode(queue.poll());
pre = pre.next;
}
return res.next;
}
}
这个效率跟上面一样:
Runtime: 7 ms, faster than 47.49% of Java online submissions for Merge k Sorted Lists.
Memory Usage: 44.8 MB, less than 20.22% of Java online submissions forMerge k Sorted Lists.
3 分治法
这个方法我没做出来,即使知道这个方法,不知道如何分 。还是看了官网的solution。简单的说就是对半划分,知道剩2个或1个,合并起来。这种方式比逐个合并要快。就是官网的solution 4 O(kN) 。
为了方便理解,我贴下官网solution的解释:
This approach walks alongside the one above but is improved a lot. We don't need to traverse most nodes many times repeatedly
-
Pair up k lists and merge each pair.
-
After the first pairing, k lists are merged into k/2 lists with average 2N/k length, then k/4, k/8 and so on.
-
Repeat this procedure until we get the final sorted linked list.
Thus, we'll traverse almost NN nodes per pairing and merging, and repeat this procedure about \log2k times.
public static void main(String[] args) {
// TODO Auto-generated method stub
ListNode node1 = new ListNode(1);
node1.next = new ListNode(4);
node1.next.next = new ListNode(5);
ListNode node11 = new ListNode(1);
node11.next = new ListNode(3);
node11.next.next = new ListNode(4);
ListNode node111 = new ListNode(2);
node111.next = new ListNode(6);
ListNode[] lists = {node1,node11,node111};
ListNode res = mergeKLists(lists);
while(res != null){
System.out.print(res.val+"->");
res = res.next;
}
}
//分治法
public static ListNode mergeKLists(ListNode[] lists) {
//coner case
if(lists == null|| lists.length==0 ){
return null;
}
//中间划分
int n = lists.length;
while(n>1){
int k = (n+1)/2;
//遍历一半
for(int i=0;i<n/2;i++){
lists[i] = mergeTwoLists(lists[i],lists[i+k]);
}
n= k;//不断的去迭代归一
}
return lists[0];
}
private static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//临时用一个-1填充下
ListNode res =new ListNode(-1);
ListNode pre =res;
while(l1!= null && l2 != null){
if(l1.val<=l2.val){
pre.next =new ListNode(l1.val);
if(l1!= null){
l1 = l1.next;
}
pre = pre.next;
}else {
pre.next =new ListNode( l2.val);
if(l2!= null){
l2 = l2.next;
}
pre = pre.next;
}
}
while(l1 != null){
pre.next =new ListNode(l1.val);
l1 = l1.next;
pre = pre.next;
}
while(l2 != null){
pre.next =new ListNode(l2.val);
l2 = l2.next;
pre = pre.next;
}
return res.next;
}
static class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
Runtime: 2 ms, faster than 98.87% of Java online submissions for Merge k Sorted Lists.
Memory Usage: 43.5 MB, less than 26.78% of Java online submissions forMerge k Sorted Lists.
时间复杂度:O(Nlogk) 比之前的快了不少。从代码上看复用了原来的两个合并的部分,但是核心的是上面的那段不断对半划分这块,很巧妙。不知道大神们怎么想到的。
回头看,一些概念知道跟写出代码还是有差距的。