来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums
题目描述
给定两个以升序排列的整数数组 nums1 和 nums2 , 以及一个整数 k 。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 。
请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk) 。
示例 1:
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3 输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
示例 2: 输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2 输出: [1,1],[1,1] 解释: 返回序列中的前 2 对数: [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
示例 3: 输入: nums1 = [1,2], nums2 = [3], k = 3 输出: [1,3],[2,3] 解释: 也可能序列中所有的数对都被返回:[1,3],[2,3] 提示: 1 <= nums1.length, nums2.length <= 104 -109 <= nums1[i], nums2[i] <= 109 nums1, nums2 均为升序排列 1 <= k <= 1000
解题思路
根据提示,nums1和nums2数组的规模很大,所以全部求出所有数对的方法肯定第一个被否定,并且也没有必要求出所有的数对。
基本思路是使用记忆化的方法,将nums2中与nums1中配对的下标记录下来,减少重复计算。
从题目中可以得知,nums1和nums2都已经经过升序排序,那么对于最初情况来说,nums1[0] + nums2[0]必定为最小的数,对于第二小的数来说,他的候选最小数为num1[0] + nums2[1] 或者 nums1[1] + nums[2]。对于一般情况第k小的数,他的候选数为nums1[0] + nums2[i0]、nums1[1] + nums2[i1]、nums1[2] + nums2[i2]……nums1[j] + nums2[ij]、nums1[j + 1] + nums2[0],其中j是之前遍历访问过的num1的下标最大值,ij 是对于nums2 中未与nums1[i]配对过的下标最小值,这里可以用一个vector来记录。由于nums2是升序的,所以nums1[j + 1] + nums2[0] 肯定比 nums1[j + m] + nums2[n] 小,之后的数肯定不属于第k个最小数。
代码展示
class Solution { public: vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) { int iIndex1 = 0, iIndex2 = 0, iCount = 0; vector<vector<int>> vviRet; vector<int> viJIndex; viJIndex.push_back(0); while(iCount < k && iCount < nums1.size() * nums2.size()) { int min = INT_MAX; iCount++; for(int i = 0; i < viJIndex.size(); i++) { if(viJIndex[i] != -1 && nums1[i] + nums2[viJIndex[i]] < min) { min = nums1[i] + nums2[viJIndex[i]]; iIndex1 = i; iIndex2 = viJIndex[i]; } } if(viJIndex.size() < nums1.size() && nums1[viJIndex.size()] + nums2[0] < min) { iIndex1 = viJIndex.size(); iIndex2 = 0; viJIndex.push_back(0); } viJIndex[iIndex1]++; if(viJIndex[iIndex1] >= nums2.size()) { viJIndex[iIndex1] = -1; } vviRet.push_back({nums1[iIndex1], nums2[iIndex2]}); } return vviRet; } };
运行结果