LeetCode 23 Merge k Sorted Lists
题干:
合并k个有序链表为一个升序链表。
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
-----------------
Input: lists = []
Output: []
-----------------
Input: lists = [[]]
Output: []
解:
合并k个有序链表的逻辑类似合并两个有序链表。问题是,如何快速得到k个节点中的最小节点,接到结果链表上?
用到了优先级队列(二叉堆),把链表节点放入一个最小堆,就可以每次获得k个节点中的最小节点。
复杂度:
时间复杂度:O(nlogk)
分析
维护一个大小为k的最小值堆,每次维护复杂度O(logk)
一共有n个数据,每个数据都会加入最小值堆,所以总体时间复杂度O(nlogk)
比串成一个链表进行排序的O(nlogn)要小
去掉空节点,k一定<=len
所以比起完全无序的n个数据排序O(nlogn),该算法把复杂度降至O(nlogk)
原因就在于数据部分有序
启示:若给定数据部分有序,理应能时间复杂度优于O(nlogn)的算法
空间复杂度:可直接改动原链表O(k),不允许破坏原链表O(n+k) ->需要确认!
大小为k的最小值堆
若不允许破坏原链表,需要额外O(n)
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode dummy(-1, nullptr);
ListNode* prev = &dummy;
priority_queue<pair<int,ListNode*>, vector<pair<int, ListNode*>>, greater<pair<int, ListNode*>> > q;
for(auto it: lists){
if(it) q.push({it->val,it});
}
while(!q.empty()){
prev->next = q.top().second;
q.pop();
prev = prev->next;
if(prev->next) q.push({prev->next->val, prev->next});
}
prev->next = nullptr;
return dummy.next;
}
Tips
链表节点放入优先队列排序,类型可以是pair<int, ListNode*>,first放val值,second放节点。
priority_queue<pair<int,ListNode*>, vector<pair<int, ListNode*>>, greater<pair<int, ListNode*>> > q; //最小堆
遍历vector的简便写法:for(auto it: lists)
for(int i = 0; i < lists.size(); i++){
auto it = lists[i];
if(it) q.push({it->val, it});
}
//等价于
for(auto it: lists){
if(it) q.push({it->val,it});
}