373. 查找和最小的K对数字 - 力扣(LeetCode)
优先级队列的典型题目,思路很像数据结构与算法:44 |最短路径:地图软件如何计算最优出行路径?_zj-CSDN博客里面的总结引申部分,思路也和LeetCode第 743 题:网络延迟时间(C++)_zj-CSDN博客的Dijkstra算法的优先级优化思路很像。
最直接的,把所有可能的元素对都加入优先级队列,最后取出前k个就可以。但是这样一股脑全加进去,内存可能会爆。
这题难点在于处理的时候怎么去重,如果使用双指针在两个数组之间滚动的话,可能会出现重复项(虽然用哈希表很方便去重,但是优先级队列一用,代码真的很臃肿了,而且哈希表好像不能存储pair类型),非要使用二维数组也不是不可以。
不过,有更好的思路,不用双指针在两个数组上滚动。参考:8ms 100%,记录位置指针快速求解 - 查找和最小的K对数字 - 力扣(LeetCode)
class Solution{
public:
vector> kSmallestPairs(vector &nums1, vector &nums2, int k){
int rows = nums1.size(), cols = nums2.size();
vector> res; //输出结果
if (rows == 0 || cols == 0) return res;
auto cmp = [&nums1, &nums2](const pair &a, const pair &b) { return nums1[a.first] + nums2[a.second] > nums1[b.first] + nums2[b.second]; };//这个函数很巧,放在里面可以调用nums1和nums2比较对应下标处的元素
priority_queue, vector>, decltype(cmp)> q(cmp);
for (int i = 0; i < rows; i++) q.emplace(i, 0);//一次性添加nums1的下标
while (k > 0 && !q.empty()){
k--;
auto cur = q.top();
q.pop();
//这儿的添加方式也很巧,不会导致重复添加
if (cur.second+1 <= cols-1){//一次添加一个nums2的下标
q.emplace(cur.first, cur.second + 1); //nums2对应的下标右走一步
}
res.push_back({nums1[cur.first], nums2[cur.second]});
}
return res;
}
};
哎,思维僵化了,受了数据结构那个博客的影响就一直往那个方向想,思维跳不出来,一直想重复项的事,把自己带进去了。
出门走一趟神清气爽,接着梳理:
最差的方法,将所有的元素对一次性放入优先级队列,然后向外取k次就行了。缺点是内存占用很大。
那怎么进行优化呢?想想,我们并没有使用到数组元素有序这个条件,我们不是需要把全部元素放入队列才可以取出最小值的。举个例子,将nums1的第一个元素和nums2中的所有元素形成的元素对放入优先级队列,那此时的队头必然是最小值,因为数组都是升序的,所以我们就可以将队头取出来放入结果里面。
上面的思路其实就是第一个代码体现的思路,但是还是不够高效。我们其实也没有必要放那么多元素到队列里面去,因为我们只需要找到前k个最小的元素对就可以了,所以我们就维护一个大小为k的队列就可以了。
说到这儿发现这个思路之前刷题已经使用过很多次了,但是自己竟然没有想起来。。。
class Solution{
public:
struct cmp{
bool operator() (const pair &a, const pair &b) {
return a.first + a.second < b.first + b.second;
}
};
vector> kSmallestPairs(vector &nums1, vector &nums2, int k){
int n = nums1.size(), m = nums2.size();
vector> res; //输出结果
if (n == 0 || m == 0) return res;
priority_queue, vector>, cmp> q;//这儿用的是大顶堆
for(int i = 0; i < n; ++i){//两层循环遍历元素
for(int j = 0; j < m; ++j){
if(q.size() < k)
q.push({nums1[i], nums2[j]});
else if(nums1[i]+nums2[j] < q.top().first + q.top().second){//比此时对首的最大值要小,那就取而代之
q.pop();
q.push({nums1[i], nums2[j]});
}
}
}
while(!q.empty()){
res.push_back({q.top().first, q.top().second});
q.pop();
}
reverse(res.begin(), res.end());
return res;
}
};