LeetCode/solutions/373. Find K Pairs with Smallest Sums.md
2019-12-16 16:34:41 +08:00

90 lines
4.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [373. Find K Pairs with Smallest Sums](https://leetcode.com/problems/find-k-pairs-with-smallest-sums/)
# 思路
给定两个有序数组让我们从每个数组中任意取出一个数字来组成不同的数字对返回前k个和最小的数字对。
直接的思路就是直接将所有pair排序复杂度O(nlogn)n为两个数组的平均长度
因为只求前k个最小的所以稍微改进一点的就是类似[347. Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/)的思路,
利用堆复杂度O(nlogk)利用快排复杂度O(n),可参考[347题解](https://github.com/ShusenTang/LeetCode/blob/2adb057f3ff1faa2f7b02b360afc1e471b1fff7d/solutions/347.%20Top%20K%20Frequent%20Elements.md)。
上面的思路复杂度最小可至O(n),但是都没有利用题目给的两个数组都是有序的这个条件,所以还有优化空间。
参考[讨论区](https://leetcode.com/problems/find-k-pairs-with-smallest-sums/discuss/84569/Java-9ms-heap-queue-solution-k-log(k))
我们可以根据数组有序这个条件以O(klogk)的复杂度解此题。
由于数组都是有序的所以和最小的pair只能是从
```
(nums1[0], nums2[0]), (nums1[1], nums2[0]), (nums1[2], nums2[0]).....(nums1[k-1], nums2[0])
```
这k个候选者中取
这里我们暂时假设k不超过nums1的大小假设就是(nums1[i], nums2[0])那么下一个最小的pair该怎么选呢应该是从刚刚剩下的k-1个候选者和
(nums1[i], nums2[1])这k个候选者中选最小的那个假设就是(nums1[j], nums2[k])那么再下一个最小的pair该怎么选呢
应该是从刚刚剩下的k-1个候选者和(nums1[j], nums2[k+1])这k个候选者中选最小的那个, 以此类推直到选取了k个。
上述过程我们只需要维护一个大小为k的最小堆就可以了建堆时间复杂度O(k)然后有k次pop和push共O(klogk)所以总的复杂度为O(klogk)。
上述思想的核心就是将所有的pair分成k类第一个数为nums1[0]、第一个数为nums1[k]、......第一个数为nums1[k-1];每一类又是一个从小到大排好序的队列。
我们每次只需要将这k个队的队头进行比较然后选取最小的那个就可以了这有点类似多路归并排序的归并过程。
几个注意点:
* 自定义结构体如何使用`priority_queue`,有三种常见的写法,见代码。
* k有可能比较大以至于超过了所有pair数。
时间复杂度为O(klogk)空间复杂度O(k)。
# C++
``` C++
struct mypair{
int a, b, sum;
int b_idx; // 在nums2中的idx
mypair(const int a_, const int b_, const int b_idx_){
a = a_; b = b_; b_idx = b_idx_;
sum = a_ + b_;
}
// // 方法一 重载运算符<,写在结构体里面(注意后面必须加const修饰!!!)
// // 后面必须加const修饰, 否则报错!!!
// bool operator < (const mypair &p) const{
// return sum >= p.sum;
// }
};
// // 方法二 重载运算符<,写在结构体外面
// bool operator < (const mypair &p1, const mypair &p2){
// return p1.sum >= p2.sum;
// }
// 方法三 定义一个比较类, 重载operator()
struct cmp{
bool operator () (const mypair &p1, const mypair &p2){
return p1.sum >= p2.sum;
}
};
class Solution {
public:
vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
int size1 = nums1.size(), size2 = nums2.size();
int p1 = 0, p2 = 0;
k = min(k, size1 * size2); // k有可能比较大以至于超过了所有pair数
// priority_queue<mypair>min_heap; // 方法一、二
priority_queue<mypair, vector<mypair>, cmp>min_heap; // 方法三
for(int i = 0; i < size1 && i < k; i++)
min_heap.push(mypair(nums1[i], nums2[0], 0));
vector<vector<int>>res;
while(k--){
mypair tmp = min_heap.top(); min_heap.pop();
res.push_back({tmp.a, tmp.b});
int next_b_idx = tmp.b_idx + 1;
if(next_b_idx < size2)
min_heap.push(mypair(tmp.a, nums2[next_b_idx], next_b_idx));
}
return res;
}
};
```