Leetcode23:Merge k Sorted Lists

Leetcode23:Merge k Sorted Lists

1. 题目描述

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
把K个已经排序的链表合并到一起并返回它的链表头,分析并描述它的复杂度。

2. 解题思路

这道题的难度是hard。这道题的解题思路有三种:

  1. 首先合并前两个链表得到一个链表,然后再和第三个合并,然后再和第四个合并,依次合并下去,直到最后得到一个链表。我们计算一下复杂度。

1、2合并,遍历2n个节点
1,2结果和3合并,遍历3n个节点
1,2,3结果和4合并,遍历4n个节点
1,2,3,..,k-1结果和k合并,遍历kn个节点
总共遍历的节点数目为n(2+3+…+k) = n*(k^2+k-2)/2, 因此时间复杂度是O(n*(k^2+k-2)/2) = O(nk^2)。

  1. 利用分治的思想,先把m个链表的合并分成m/2个双链表的合并,然后一次递减直到只有最后的一个或者两个链表。仔细想想这个过程是不是二分的逆过程。这个可以使用递归来实现,同样的思路在合并两个链表里也使用过。因此算法复杂度为T(k) = 2T(k/2) + O(nk),推导得到算法复杂度为O(nklogk)。

1、3合并,合并结果放到1的位置
2、4合并,合并结果放到2的位置
再把1、2合并(相当于原来的13 和 24合并)

  1. 维护一个k个大小的最小堆,初始化堆中元素为每个链表的头结点,每次从堆中选择最小的元素加入到结果链表,再选择该最小元素所在链表的下一个节点加入到堆中。这样当堆为空时,所有链表的节点都已经加入了结果链表。元素加入堆中的复杂度为O(longk),总共有kn个元素加入堆中,因此,复杂度也和算法2一样是O(nklogk)。

3.代码实现

  1. 依次合并
class Solution {
 public ListNode mergeKLists(ListNode[] lists) {
	 int n = lists.length;
	
	 if(n==0 || lists==null)
		 return null;
     //if(n==1)
       //  return lists[0];       
		 for(int i=1;i<n;i++) {
			 lists[0] = mergeTwoLists(lists[0],lists[i]);
		 }
		 n = k;
	 }
	 return lists[0];
 }
     public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1==null)
            return l2;
        if(l2==null)
        	return l1;
        ListNode head = null;
       
        ListNode head_c = l1.val>l2.val?l2:l1;
        head = head_c;
        
        while(l1 !=null && l2!=null) {
        	if(l1.val>l2.val) {
        		head.next = l2;
        		l2 = l2.next;
        	}
        	else {
        		head.next = l1;
        		l1 = l1.next;
        	}
        }
        if(l1==null) {
        	head.next = l2;
        }
        else {
        	head.next = l1;
        }
        return head_c.next;
    }
}
  1. 分支思想、两两合并
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
 public ListNode mergeKLists(ListNode[] lists) {
	 int n = lists.length;
	
	 if(n==0 || lists==null)
		 return null;
     //if(n==1)
       //  return lists[0];
	 while(n>1) {
		 int k = (n+1)/2;
         
		 for(int i=0;i<n/2;i++) {//注意这里i的取值范围,因为n存在奇偶两种情况
			 lists[i] = mergeTwoLists1(lists[i],lists[i+k]);
		 }
		 n = k;
	 }
	 return lists[0];
 }
  public ListNode mergeTwoLists1(ListNode l1, ListNode l2) {
        if(l1==null)
            return l2;
        if(l2==null)
        	return l1;
       if(l1.val<l2.val) {
    	   l1.next = mergeTwoLists1(l1.next,l2);
    	   return l1;
       }else {
    	   l2.next = mergeTwoLists1(l2.next,l1);
    	   return l2;  
       }
    }
}
  1. 维护最小堆
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;
            }
        });//建堆并重写compare函数
        
        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;
    }
}

原文地址:https://www.cnblogs.com/chailinbo/p/7586689.html