LeetCode23 Merge k Sorted Lists

题意:

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. (Hard)

分析:

方法1:

利用做过的merge 2 sorted list,将头两个归并,结果再与下一个归并,以此类推,归并完所有。

时间复杂度分析是个小问题, merge 2 sorted list的复杂度是O(n),本以为结果就是O(n * k)。

但是仔细考虑,随着归并不断进行,其中一个链表的长度不断增加,直至O(n * k),所以复杂度应该是O(n * k^2) 

代码:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 private:
11      ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
12         ListNode dummy(0);
13         ListNode* head = &dummy;
14         while (l1 != nullptr && l2 != nullptr) {
15             if (l1 -> val < l2 -> val) {
16                 head -> next = l1;
17                 l1 = l1 -> next;
18             }
19             else {
20                 head -> next = l2;
21                 l2 = l2 -> next;
22             }
23             head = head -> next;
24         }
25         if (l1 != nullptr) {
26             head -> next = l1;
27         }
28         if (l2 != nullptr) {
29             head -> next = l2;
30         }
31         return dummy.next;
32     }
33 public:
34     ListNode* mergeKLists(vector<ListNode*>& lists) {
35         if (lists.size() == 0) {
36             return nullptr;
37         }
38         ListNode dummy(0);
39         ListNode* head = &dummy;
40         head -> next = lists[0];
41         for (int i = 1; i < lists.size(); ++i) {
42             head -> next = mergeTwoLists(head->next, lists[i]);
43         }
44         return dummy.next;
45         
46     }
47 };

方法2:

利用最小堆。将所有链表的第一个节点加入堆,然后每次取出值最小的节点,并将该节点的next节点加入堆中。堆为空后所以节点处理完,归并后链表。

堆的大小为k,所以插入删除取节点复杂度均为O(logk),共对O(nk)个节点进行操作,所以时间复杂度为O(nklogk)。

注:

本题除了算法思想本身,还有对于实现过程还是有需要总结的。

1) STL的priority_queue默认的是最大堆,需要重载比较函数。 

  首先一般来讲priority有三个参数,即

template < class T, class Container = vector<T>,
           class Compare = less<typename Container::value_type> > class priority_queue;

可以看出,2、3有缺省值,即表示用vector实现priority_queue,实现的是最大堆。所以可以不写这两个参数。

但是如果要修改比较方式,即第三个参数的话,第二个参数也要给出(以前没用过,真是查文档才知道)。

对于内置类型,如int,可以使用stl的functor如 greater<int>直接修改堆的大小属性,但是自定义节点,如本例的ListNode,需要自己重载。

2)对于传入比较函数,一种方法是重载类的operator <,但是leetcode中ListNode的定义是不能修改的。

  还有就是写一个cmp函数,值得注意的是。funtor作为一个类需要重载其operator(),即对于本题,写为

    struct cmp{
      bool operator() (ListNode* l1, ListNode* l2) {  
          return l1 -> val > l2 -> val;
      }  
    };  

另外,priority_queue的第三个参数不能只传函数,必须封装成个functor(类),开始写的时候传函数报错了。

总之这题收货还是不少,不论从算法角度还是语言角度。

 

代码:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 private:
11     struct cmp{
12       bool operator() (ListNode* l1, ListNode* l2) {  //why?!
13           return l1 -> val > l2 -> val;
14       }  
15     };  
16 public:
17     ListNode* mergeKLists(vector<ListNode*>& lists) {
18         ListNode dummy(0);
19         ListNode* head = &dummy;
20         priority_queue<ListNode*, vector<ListNode*>, cmp> que;
21         for (int i = 0; i < lists.size(); ++i) {
22             if (lists[i] != nullptr) {
23                 que.push(lists[i]);
24             }
25         }
26         while (!que.empty()) {
27             ListNode* temp = que.top();
28             head -> next = temp;
29             head = head -> next;
30             que.pop();
31             if (temp -> next != nullptr) {
32                 que.push(temp->next);
33             }
34         }
35         return dummy.next;
36     }    
37 };

 

posted @ 2016-08-13 21:41  wangxiaobao1114  阅读(253)  评论(0编辑  收藏  举报