目录
一,题目描述
英文描述
中文描述
示例与说明
二,解题思路
三,AC代码
C++
Java
四,解题过程
第一博
第二搏
第三博
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个;
C++版本采用大根堆,维持最小的k个组合,堆顶为最小k个组合中最大的一个。
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; } };
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小的一员,并弹出堆顶。