目录
一,题目描述
英文描述
You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k.
Define a pair (u, v) which consists of one element from the first array and one element from the second array.
Return the k pairs (u1, v1), (u2, v2), ..., (uk, vk) with the smallest sums.
中文描述
给定两个以升序排列的整数数组 nums1 和 nums2 , 以及一个整数 k 。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。
请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk) 。
示例与说明
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二,解题思路
java版本的代码采用小根堆,暴力双重循环,将所有组合依次放入堆中,最后筛选前k个;
- 上面的做法会超时,可以对部分条件进行筛选。由于只需要前k个最小组合,每个数组(nums1或nums2)中选出的数字个数不可能超过k(有k个小的不选,选大的?)。这样可以通过目前的测试用例。
C++版本采用大根堆,维持最小的k个组合,堆顶为最小k个组合中最大的一个。
- 当堆的大小达到k时,判断新组合与堆顶的优先级关系,若堆顶优先级高(说明堆顶的组合大于新组合),则将新组合放入堆中,并弹出堆顶。
三,AC代码
C++
class Solution {
public:
struct cmp {
// 大根堆,维持k个最小值,堆顶为最小值中最大的一个
bool operator()(pair<int, int> a, pair<int, int> b) {
return (a.first + a.second) < (b.first + b.second);
}
};
vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
vector<vector<int>> ans;
priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> queue;// !!!
for (int i = 0; i < nums1.size() && i < k; i++) {
for (int j = 0; j < nums2.size() && j < k; j++) {
if (queue.size() < k) {
queue.push({nums1[i], nums2[j]});
}
// 当堆顶的优先级大于新的数据时,将其加入到堆中
// 在这里直观的理解就是新数据小于堆顶,因此允许进入前k小
else if (cmp()({nums1[i], nums2[j]}, queue.top())) {
queue.push({nums1[i], nums2[j]});
queue.pop();
}
}
}
while (queue.size()) {
ans.push_back({queue.top().first, queue.top().second});
queue.pop();
}
return ans;
}
};
Java
class Solution {
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
List<List<Integer>> ans = new ArrayList<>();
PriorityQueue<List<Integer>> queue = new PriorityQueue<>(new Comparator<>(){
public int compare(List<Integer> a, List<Integer> b) {
return (b.get(0) + b.get(1)) - (a.get(0) + a.get(1));
}
});
// 也可以这样写
// PriorityQueue<List<Integer>> queue = new PriorityQueue<>((a, b)->(
// (b.get(0) + b.get(1) - a.get(0) - a.get(1))
// ));
for (int i = 0; i < nums1.length && i < k; i++) {
for (int j = 0; j < nums2.length && j < k; j++) {
queue.offer(new ArrayList(Arrays.asList(nums1[i], nums2[j])));
if (queue.size() > k) queue.poll();
}
}
while (queue.size() > 0) {
ans.add(queue.poll());
}
return ans;
}
}
四,解题过程
第一博
借助优先队列——小根堆,双重循环将每一个可能的结果放进去,然后返回队列中的内容。
class Solution {
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
List<List<Integer>> ans = new ArrayList<>();
PriorityQueue<List<Integer>> queue = new PriorityQueue<>(new Comparator<>(){
public int compare(List<Integer> a, List<Integer> b) {
return (b.get(0) + b.get(1)) - (a.get(0) + a.get(1));
}
});
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
queue.offer(new ArrayList(Arrays.asList(nums1[i], nums2[j])));
if (queue.size() > k) queue.poll();
}
}
while (queue.size() > 0) {
ans.add(queue.peek());
queue.poll();
}
return ans;
}
}
第二搏
上述解法和暴力解法基本差不多。现在考虑剪枝。
由于nums1和nums2是从小到大的排序数组,所以当双重循环中的指针(i 或 j )超过k时,就不需要继续计算下去了
第三博
换C++试一试大根堆的方法,直观来说就是维持一个大小为k的堆,堆中存放值最小的元素,这样堆顶就是最小k个元素中,最大的那一个。
当堆的大小达到k,可以将后面的元素与堆定比较,若小于堆顶(也就是堆顶优先级高,这里有点绕),则加入堆中成为前k小的一员,并弹出堆顶。