【堆】23. Merge k Sorted Lists

问题:

给定k个有序链表,将这些链表的所有节点排序,组合成一个新的有序链表

Example 1:
Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
  1->4->5,
  1->3->4,
  2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6

Example 2:
Input: lists = []
Output: []

Example 3:
Input: lists = [[]]
Output: []
 
Constraints:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] is sorted in ascending order.
The sum of lists[i].length won't exceed 10^4.

  

解法:

解法一:Min Heap (最小堆)

首先最小堆的实现:

class minHeap {
private:
    vector<ListNode*> heapary;
public:
    int n; //size
    minHeap() {
        n=0;
    }
};

 

操作:

  • push:插入一个元素
  • popmin:弹出最小元素
 1     void push(ListNode* val) {
 2         heapary.push_back(val);
 3         n++;
 4         swim(n-1);
 5     }
 6     ListNode* popmin() {
 7         ListNode* res = heapary[0];
 8         if(heapary[0]->next==NULL) {
 9             swap(heapary[0], heapary[n-1]);
10             heapary.pop_back();
11             n--;
12         } else {
13             heapary[0] = heapary[0]->next;
14         }
15         sink(0);
16         return res;
17     }

 

堆适应化:

  • swim:将底层新元素上升,使得堆适应化。(一般在插入push操作中使用<一般从数组尾部(堆底)插入>)
  • sink:将上层新元素下降,使得堆适应化。(一般在删除popmin操作中使用<swap堆顶和堆尾,删除移动后的堆尾,将新的堆顶下降>)
 1     void swim(int i) {
 2         int father = parent(i);
 3         while(father>=0 && heapary[father]->val > heapary[i]->val) {
 4             swap(heapary[father], heapary[i]);
 5             i = father;
 6             father = parent(i);
 7         }
 8     }
 9     void sink(int i) {
10         int l = left(i);
11         int r = right(i);
12         int minchild;
13         while(l<n) {
14             minchild = l;
15             if(r<n && heapary[r]->val<heapary[minchild]->val) {
16                 minchild = r;
17             }
18             if(heapary[minchild]->val >= heapary[i]->val) break;
19             swap(heapary[minchild], heapary[i]);
20             i = minchild;
21             l = left(i);
22             r = right(i);
23         }
24     }

 

另外,这里常用求node i 的父节点,子节点:

  • parent(i):(i-1)/2
  • child(i):
    • leftchild(i): i*2+1
    • rightchild(i): i*2+2
1     int parent(int i) {
2         return (i-1)/2;
3     }
4     int left(int i) {
5         return i*2+1;
6     }
7     int right(int i) {
8         return i*2+2;
9     }

 

对本题:

利用上述最小堆,将每个list的头节点放入堆中push(list),

每次弹出堆顶popmin,作为当前最小值,

⚠️ 注意:

  • 弹出堆顶时,若该节点为头的list还不为NULL,
    • 那么该节点->next最为新的堆顶,同时sink这个新堆顶,进行堆适应化。
  • 若该节点为头的list除了该节点外,无剩余节点,
    • 那么swap(堆底,堆顶),heap_size--(删除新堆底),同时sink这个新堆顶,进行堆适应化。

每次,将上次弹出的最小值->next=本次弹出的当前最小值

最终得到所求结果。

 

代码参考:

 1 class Solution {
 2 public:
 3     ListNode* mergeKLists(vector<ListNode*>& lists) {
 4         minHeap mh;
 5         ListNode* res = NULL, *p = NULL;
 6         for(ListNode* list : lists) {
 7             if(list) mh.push(list);
 8         }
 9         while(mh.n>0) {
10             if(!res) {
11                 res = mh.popmin();
12                 p = res;
13             } else {
14                 p->next = mh.popmin();
15                 p = p->next;
16             }
17         }
18         return res;
19     }
20 };

 

leetcode解答完整代码:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode() : val(0), next(nullptr) {}
 7  *     ListNode(int x) : val(x), next(nullptr) {}
 8  *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 9  * };
10  */
11 class minHeap {
12 private:
13     vector<ListNode*> heapary;
14     int parent(int i) {
15         return (i-1)/2;
16     }
17     int left(int i) {
18         return i*2+1;
19     }
20     int right(int i) {
21         return i*2+2;
22     }
23     void swim(int i) {
24         int father = parent(i);
25         while(father>=0 && heapary[father]->val > heapary[i]->val) {
26             swap(heapary[father], heapary[i]);
27             i = father;
28             father = parent(i);
29         }
30     }
31     void sink(int i) {
32         int l = left(i);
33         int r = right(i);
34         int minchild;
35         while(l<n) {
36             minchild = l;
37             if(r<n && heapary[r]->val<heapary[minchild]->val) {
38                 minchild = r;
39             }
40             if(heapary[minchild]->val >= heapary[i]->val) break;
41             swap(heapary[minchild], heapary[i]);
42             i = minchild;
43             l = left(i);
44             r = right(i);
45         }
46     }
47 public:
48     int n;
49     minHeap() {
50         n=0;
51     }
52     void push(ListNode* val) {
53         heapary.push_back(val);
54         n++;
55         swim(n-1);
56     }
57     ListNode* popmin() {
58         ListNode* res = heapary[0];
59         if(heapary[0]->next==NULL) {
60             swap(heapary[0], heapary[n-1]);
61             heapary.pop_back();
62             n--;
63         } else {
64             heapary[0] = heapary[0]->next;
65         }
66         sink(0);
67         return res;
68     }
69 };
70 
71 class Solution {
72 public:
73     ListNode* mergeKLists(vector<ListNode*>& lists) {
74         minHeap mh;
75         ListNode* res = NULL, *p = NULL;
76         for(ListNode* list : lists) {
77             if(list) mh.push(list);
78         }
79         while(mh.n>0) {
80             if(!res) {
81                 res = mh.popmin();
82                 p = res;
83             } else {
84                 p->next = mh.popmin();
85                 p = p->next;
86             }
87         }
88         return res;
89     }
90 };
完整代码

 

解法二:C++ priotity queue(优先队列)

类似上述最小堆。

构造方法:

priority_queue<ListNode*, vector<ListNode*>, cmp>
  • ListNode*:每个元素的类型
  • vector<ListNode*>:优先队列的存储方式:vector
  • cmp:比较方式
    • 默认:less:<,最大堆

本题中,需要构建最小堆,因此通过重载operator方法实现:

1     struct cmp {//min heap(greater '>')
2         //(default: max heap-> less '<')
3         bool operator()(ListNode *a, ListNode *b) {
4             return a->val > b->val;
5         }
6     };

 

代码参考:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode() : val(0), next(nullptr) {}
 7  *     ListNode(int x) : val(x), next(nullptr) {}
 8  *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 9  * };
10  */
11 class Solution {
12 public:
13     struct cmp {//min heap(greater '>')
14         //(default: max heap-> less '<')
15         bool operator()(ListNode *a, ListNode *b) {
16             return a->val > b->val;
17         }
18     };
19     ListNode* mergeKLists(vector<ListNode*>& lists) {
20         priority_queue<ListNode*, vector<ListNode*>, cmp> mh;
21         ListNode* res = NULL, *p = NULL;
22         for(ListNode* list : lists) {
23             if(list) mh.push(list);
24         }
25         while(!mh.empty()) {
26             if(!res) {
27                 res = mh.top();
28                 p = res;
29             } else {
30                 p->next = mh.top();
31                 p = p->next;
32             }
33             mh.pop();
34             if(p->next){
35                 mh.push(p->next);
36             }
37         }
38         return res;
39     }
40 };

 

posted @ 2020-10-25 10:44  habibah_chang  阅读(112)  评论(0编辑  收藏  举报