leetcode-23. Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
leetcode-21. Merge Two Sorted Lists
常规思路n*k
第一次做这个的时候没有限制时间复杂度,写出来像下面这样的代码。这个的复杂度应该是n*k。n是节点数,k是多少个链表数。然后提交的时候TLE。。。。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode mergeKLists(ListNode[] lists) {
ListNode[] ds = new ListNode[lists.length];
for(int i = 0 ; i < lists.length; i++){
ds[i] = new ListNode(0);
ds[i].next = lists[i];
}
ListNode dump = new ListNode(0);
ListNode ret = dump;
int c = 0;
for(ListNode n : ds){
while(n.next !=null){
n = n.next;
c++;
}
}
while(c--!=0){
int p = 0,v=Integer.MAX_VALUE;
for(int i = 0 ; i < ds.length ; i++){
if(ds[i].next!= null && v > ds[i].next.val){
v = ds[i].next.val;
p = i;
}
}
dump.next = ds[p].next;
dump = dump.next;
ds[p].next = ds[p].next.next;
}
return ret.next;
}
}
n*logk的mergesort解法
不过也是显然的。。
想要优化的这里时间上基本的思路还是从SortList上面入手,基本上向这样多个链表的题用分治二分之类的算法都会把时间复杂度从n降到logn的水平。是一种比较常见的简化思路。加上之前做过的21题,实际上也算是有了提示。这里只要不断二分问题然后将其转换成为2个节点2个节点之间的merge就可以以logn的复杂度来做。
所以这里的时间复杂度是n*logk;
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists==null || lists.length<1) return null;
return helper(lists,0,lists.length-1);
}
private ListNode helper(ListNode[] lists, int start, int end){
if(start==end){
return lists[start];
}else if(start < end){
int mid = (end - start) / 2 + start;
ListNode left = helper(lists, start, mid);
ListNode right = helper(lists, mid + 1, end);
return mergeTwoLists(left, right);
}else{
return null;
}
}
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
ListNode d1 = new ListNode(0);
ListNode d2 = new ListNode(0);
ListNode ret = new ListNode(0);
ListNode tmp = ret;
d1.next = l1;
d2.next = l2;
while(d1.next != null && d2.next != null){
if(d1.next.val < d2.next.val){
tmp.next = d1.next;
d1.next = d1.next.next;
}else{
tmp.next = d2.next;
d2.next = d2.next.next;
}
tmp = tmp.next;
}
if(d2.next != null) d1.next =d2.next;
tmp.next = d1.next;
return ret.next;
}
}
AC的堆解法
另外,其实这题也可以用堆来做。。但是这样写法好像比较鸡贼,,但是确实也是一种有效的解法。而且简洁的多。。这个的复杂度实际上是nlogn。但是这个只是AC的解法。并不是说最好的,正确的堆的用法在下一小节说。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<Integer> deap = new PriorityQueue<Integer>();
for(ListNode n : lists){
while(n !=null){
deap.add(n.val);
n=n.next;
}
}
ListNode dump = new ListNode(0);
ListNode ret = dump;
while(!deap.isEmpty()){
dump.next = new ListNode(deap.poll());
dump = dump.next;
}
return ret.next;
}
}
n*logk的堆解法
正确的堆的解法应该是先将每个链表的头结点放入堆中,然后一个一个的poll出来,然后在将每个链表的头节点放入堆中,如此往复,这样的话时间复杂度也是n*logk。也是一种很有意思的方法。
这个解法我没有做了。下面的实现是讨论里的,他是重写写了一个比较器。这个实现上更优雅一些。。比我之前想的更好一些。
解法来源
public class Solution {
public ListNode mergeKLists(List<ListNode> lists) {
if (lists==null||lists.size()==0) return null;
PriorityQueue<ListNode> queue= new PriorityQueue<ListNode>(lists.size(),new Comparator<ListNode>(){
@Override
public int compare(ListNode o1,ListNode o2){
if (o1.val<o2.val)
return -1;
else if (o1.val==o2.val)
return 0;
else
return 1;
}
});
ListNode dummy = new ListNode(0);
ListNode tail=dummy;
for (ListNode node:lists)
if (node!=null)
queue.add(node);
while (!queue.isEmpty()){
tail.next=queue.poll();
tail=tail.next;
if (tail.next!=null)
queue.add(tail.next);
}
return dummy.next;
}
}