题目
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
思路1:二分递归
经典k路归并,这里采用二分递归
时间复杂度:
归并两个有序数组时间复杂度为O(n),将K个有序数组分治归并时间复杂度为O(logk),算法整体时间复杂度为O(nlogk)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty()) return NULL;
return mergeK(lists,0,lists.size()-1);
}
ListNode* mergeK(vector<ListNode*>& lists,int l,int r){
if(l > r) return NULL;
if(l == r) return lists[l];
if(l+1 == r) return merge(lists[l],lists[r]);
int mid = (l+r)/2;
ListNode* left = mergeK(lists,l,mid);
ListNode* right = mergeK(lists,mid+1,r);
return merge(left,right);
}
ListNode* merge(ListNode* l,ListNode* r){
ListNode *dummy_head = new ListNode(-1),*cur = dummy_head;
while(l && r){
if(l->val <= r->val){
cur->next = l;
l = l->next;
}else{
cur->next = r;
r = r->next;
}
cur = cur->next;
}
if(l) cur->next = l;
else if(r) cur->next = r;
return dummy_head->next;
}
};
思路2:堆
k大小的堆,时间复杂度也是nlogk
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
struct cmp{
bool operator()(ListNode* h1,ListNode* h2){
return h1->val > h2->val;
}
};
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.empty()) return NULL;
if(lists.size() < 2) return lists[0];
priority_queue<ListNode*,vector<ListNode*>,cmp> q;
ListNode* dummy_head = new ListNode(-1);
ListNode* pre = dummy_head,*cur;
for(auto &node:lists){
if(node) q.push(node);
}
while(!q.empty()){
cur = q.top(); q.pop();
pre->next = cur;
pre = pre->next;
if(cur->next) q.push(cur->next);
}
return dummy_head->next;
}
};
相似题目:632. 最小区间
你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。
我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。
示例 1:
输入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出: [20,24]
解释:
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。
注意:
给定的列表可能包含重复元素,所以在这里升序表示 >= 。
1 <= k <= 3500
-105 <= 元素的值 <= 105
思路
使用k路归并,堆来做,事实上每一次归并前的k个就是一个区间
这题实现起来比较难,因为是数组,不是链表,所以用堆的时候,堆内放置:
(数值(用来排序),来自哪个数值)的二元组
同时利用index数组,记录每个数组下一次需要访问的下标
// 小顶堆
struct cmp{
bool operator()(const pair<int,int> &a,const pair<int,int> &b){
return a.first > b.first;
}
};
class Solution {
public:
vector<int> smallestRange(vector<vector<int>>& nums) {
int n = nums.size(),cur_Max = INT_MIN;
// 记录每个数组下一次需要访问的下标
vector<int> index(n,1);
// 小顶堆
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q;
for(int i = 0;i < n;i++){
q.push({nums[i][0],i});
cur_Max = max(cur_Max,nums[i][0]);
}
vector<int> res{q.top().first,cur_Max};
// 没有一个数组走到尾
while(index[q.top().second] < nums[q.top().second].size()){
// 加入新的数,更新最大值
int cur = q.top().second;q.pop();
cur_Max = max(cur_Max,nums[cur][index[cur]]);
q.push({nums[cur][index[cur]],cur});
// 更新下一次需要访问的下标
index[cur]++;
// 获得此次堆中的最大最小区间
if((cur_Max-q.top().first) < res[1]-res[0]){
res[0] = q.top().first;res[1] = cur_Max;
}
}
return res;
}
};