题目描述
LeetCode原题链接:373. Find K Pairs with Smallest Sums
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.
Example 1:
Input: nums1 = [1,7,11], nums2 = [2,4,6], k = 3 Output: [[1,2],[1,4],[1,6]] Explanation: The first 3 pairs are returned from the sequence: [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
Example 2:
Input: nums1 = [1,1,2], nums2 = [1,2,3], k = 2 Output: [[1,1],[1,1]] Explanation: The first 2 pairs are returned from the sequence: [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
Example 3:
Input: nums1 = [1,2], nums2 = [3], k = 3 Output: [[1,3],[2,3]] Explanation: All possible pairs are returned from the sequence: [1,3],[2,3]
Constraints:
1 <= nums1.length, nums2.length <= 105
-109 <= nums1[i], nums2[i] <= 109
nums1
andnums2
both are sorted in ascending order.1 <= k <= 104
思路分析
这道题brute force的思路很清晰,就是去穷举所有可能的pair,然后把这些pair存放到MinPriorityQueue中,或者存放到一个array之后排序,最终结果返回前k个。
1 for(let u of nums1) { 2 for(let v of nums2) { 3 minPriorityQueue.enqueue(u + v) 4 } 5 }
那么有没有效率更高一点的方法呢?
提高效率的关键在于如何从nums1和nums2中选取数字组成pair,题目中强调了nums1和nums2都是递增顺序的数组,那么我们想啊,要想让取出的两个数和最小,肯定两个数就要分别是数组中较小的数。brute force中我们是先锁定nums1中某一位置的数作为u,然后去遍历nums2中所有的数来找v,显然越往后v肯定越大,虽然u是nums1中当前循环里较小的数,但是v可能会很大,显然不如重新去取nums1中第二小的数和nums2中前部分的数。
所以,关键就是怎么在nums2中找v,也就是该从哪个位置开始找v。这里我们可以用一个数组idx[i]来表示当u=nums1[i]时,应当从nums2的哪个位置开始去寻找v,idx中的元素初始化为0;
我们每次从i=0位置开始遍历nums1数组中的全部元素,用两个变量cur和sum分别记录局部最小值,cur初始化为0, sum初始化为Number.MAX_VALUE, 那么对应 u=nums1[i] 的v就是 nums2[idx[i]],u+v<sum 时去更新cur为i,sum为u+v,nums1遍历结束后就是当前要找的和最小的pair。将 [nums1[cur], nums2[idx[cur]]] 加入res数组,同时去更新 idx[cur] 为 idx[cur]+1 , 表明nums2中这个位置的数已经和nums1中cur位置的数组成过一个符合要求的pair了,不能够再使用了;下一轮nums1再遍历到这个位置的时候,nums2中v要从该位置后面的数中去寻找。idx数组是这个算法剪枝的关键。
需要注意,因为idx[i]表示的是nums2中的下标。所以前提肯定是 idx[i] < nums2.length.
另外,我们知道最多可以组成的pair个数是nums1和nums2数组长度的乘积。那么结果数组的长度就是min(k, nums1.length * nums2.length);我们用一个while循环来寻找这些数。
代码示例(JS)
1 var kSmallestPairs = function(nums1, nums2, k) { 2 k = Math.min(k, nums1.length * nums2.length); 3 let idx = new Array(nums1.length); 4 idx.fill(0); 5 let res = []; 6 while(k-- > 0){ 7 let cur = 0, sum = Number.MAX_VALUE; 8 for(let i = 0; i < nums1.length; i++) { 9 if(idx[i] < nums2.length && nums1[i] + nums2[idx[i]] < sum) { 10 sum = nums1[i] + nums2[idx[i]]; 11 cur = i; 12 } 13 } 14 res.push([nums1[cur], nums2[idx[cur]]]); 15 idx[cur]++; 16 } 17 return res; 18 };