avatar

目录
leetcode373、632 多路归并+堆

leetcode 373

这是一道top k问题,首先从最原始的思路想,可以用双指针暴力枚举所有情况然后取前k个,但这就没有利用好两个数组都是已排序的条件,假设我们已经得到了前n个最小的,那么下一个要考虑的就是这n个数中的每一个的(ai,bi+1)(a_{i},b{i+1})(ai+1,bi)(a_{i+1},b{i})中最小的那一个,可以用堆来动态的维护当前的最小值并完成多路归并,这里的多路可能还比较缥缈,先考虑一个可能会重复的问题,很明显a[0]+b[0]是最小的,之后有两条路都可以走到a[1]+b[1]这里导致了重复,所以我们可以先把第一列放入堆里,然后进行按每一路动态的游走,所有路的步数和为k次就停止,可见下图

另一个要考虑的问题是,我们堆里存的是索引对,但是是根据数组和来确实排序的,所以要把这些信息都要放进堆里,设计为pair<int, pair<int, int>,即<和,<索引,索引>。

c++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<pair<int, pair<int, int>>>> heap;
int m = nums1.size();
int n = nums2.size();
for (int i = 0; i < min(m, k); i++) {
heap.push({nums1[i] + nums2[0], {i, 0}});
}
vector<vector<int>> res;
while (k-- && !heap.empty()) {
auto p = heap.top();
heap.pop();
int a = p.second.first, b = p.second.second;
if (b + 1 < n) {
heap.push({nums1[a]+ nums2[b + 1], {a, b + 1}});
}
res.emplace_back(initializer_list<int>{nums1[a], nums2[b]});
}
return res;
}
};

还有一种官方题解的方法是用lambda表达式来捕获数组直接完成建堆

c++
1
2
3
4
auto cmp = [&nums1, &nums2](const pair<int, int> & a, const pair<int, int> & b) {
return nums1[a.first] + nums2[a.second] > nums1[b.first] + nums2[b.second];
};
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);

下面解释一下这个优先队列的用法,
1.为什么用decltype?
优先队列的第三个参是需要指定一个实现了 operator< 操作符的类或者结构体,而lambda是一个值,
2.为什么最后还有吧cmp传入构造函数?
因为lambda没有默认构造函数的(构造函数和赋值运算符删除,拷贝构造和析构隐试定义

c++
1
2
3
4
5
6
7
struct node {
int x, y;
int i, j;
bool operator < (const node &other) {
return x + y > other.x + other.y;
}
}

参考:https://ask.csdn.net/questions/7399659
https://stackoverflow.com/questions/5807735/c-priority-queue-with-lambda-comparator-error
https://stackoverflow.com/questions/41053232/c-stdpriority-queue-uses-the-lambda-expression

leetcode 632

这道题可以理解为是从k个列表中分别取一个数,使得这k个数中的最大值与最小值的差最小。同样利用好每个列表都是非递减排的条件,可以基于贪心的思想,首先给每个列表分配一个指针,起始都指向表头,然后用最小堆维护当前k个数的最小值同时维护当前的最大值,每次移动最小值也就是左边界,直到有某个列表遍历完成
同样采样类似上题的数据结构

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
vector<int> smallestRange(vector<vector<int>>& nums) {
priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, greater<pair<int, pair<int, int>>>> heap;
int res = INT_MAX;
vector<int> ans(2);
ans[0] = -1;
int n = nums.size();
vector<int> index(n);
int max_value = 0;
for (int i = 0; i < n; i++) {
heap.push({nums[i][0], {i, 0}});
max_value = max(max_value, nums[i][0]);
}
while (!heap.empty()) {
auto top_value = heap.top();
heap.pop();
int dis = max_value - top_value.first;
if (dis < res || (dis == res && res != INT_MAX && top_value.first < ans[0])) {
res = dis;
ans[0] = top_value.first;
ans[1] = max_value;
}
int a = top_value.second.first;
int b = top_value.second.second;
if (b + 1 >= nums[a].size()) {
break;
}
if (nums[a][b + 1] > max_value) {
max_value = nums[a][b + 1];
}
heap.push({nums[a][b + 1], {a, b + 1}});
}
return ans;
}
};
文章作者: Sunxin
文章链接: https://sunxin18.github.io/2022/01/17/heap/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 lalala
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论