373. 查找和最小的K对数字

题目:

链接: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:

直接套用TOP-K问题的解法(https://www.cnblogs.com/FdWzy/p/12321325.html

由于求最小的k个元素对,那么首先定义小于重载关系。

之后利用最大堆,堆大小不超过k直接push,超过k要看是否比堆顶小,是则pop并push,否则跳过。

效率O(m*n*log k)

 

 1 class Solution {
 2 public:
 3     vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
 4         vector<vector<int>> res;
 5         int len1=nums1.size(),len2=nums2.size();
 6         auto cmp=[](vector<int>& a,vector<int>& b){return a[0]+a[1]<b[0]+b[1];};
 7         priority_queue<vector<int>,vector<vector<int>>,decltype(cmp)> max_heap(cmp);
 8         vector<int> tmp1,tmp2;
 9         for(int i=0;i<len1;++i){
10             for(int j=0;j<len2;++j){
11                 if(max_heap.size()<k){
12                     max_heap.push({nums1[i],nums2[j]});
13                 }
14                 else{
15                     tmp1=max_heap.top();
16                     tmp2={nums1[i],nums2[j]};
17                     if(not cmp(tmp1,tmp2)){
18                         max_heap.pop();
19                         max_heap.push(move(tmp2));
20                     }
21                 }
22             }
23         }
24         while(not max_heap.empty()){
25             res.emplace_back(max_heap.top());
26             max_heap.pop();
27         }
28         reverse(res.begin(),res.end());
29         return res;
30     }
31 };

 

 

 

 

方法2:

前一个方法比较暴力,直接枚举所有可能的元素对全部加入最大堆进行筛选。这样呢,没有利用两个数组有序的性质,两个无序数组也可以这样做。

所以我们考虑记录对于每一个nums1中的索引i,如果已经把[i,0],[i,1],[i,2]...[i,j]全部的元素对加入了结果,那么下次再查找[i,xx]元素对的时候,就可以从j+1开始找,因为数组是有序的。

 

用一个memo数组记录。

做法,我们这回用最小堆,堆顶直接指示为最小的元素对。每次取堆顶,更改相应的memo数组值。如果memo[i]=len(nums2),那说明[i,xx]元素对已经无法再取了,就直接从堆中删掉。

如果memo[i]<len(nums2),那么再push回堆里。

效率:O(m*log m+k*log m)

 1 class Solution {
 2 public:
 3     vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
 4         vector<vector<int>> res;
 5         int len1=nums1.size(),len2=nums2.size();
 6         if(len1==0 or len2==0){return res;}
 7         vector<int> memo(len1,-1);
 8         //memo[i]指示:{nums1[i],nums2[j]}元素对中上次已经放入结果数组那个的j的值,i对应第一个未使用的j为memo[i]+1
 9         auto cmp=[&](int x,int y){return nums1[x]+nums2[memo[x]+1]>nums1[y]+nums2[memo[y]+1];};
10         priority_queue<int,vector<int>,decltype(cmp)> min_heap(cmp);//小顶堆
11         for(int i=0;i<len1;++i){
12             min_heap.push(i);
13         }
14         while(res.size()<k and not min_heap.empty()){//每次循环把小顶堆的堆顶(也就是目前的最小对)push进结果
15             int tp=min_heap.top();
16             min_heap.pop();
17             res.push_back({nums1[tp],nums2[memo[tp]+1]});
18             memo[tp]+=1;                            //memo[tp]+1已经遍历,所以memo[tp]+=1;
19             if(memo[tp]<len2-1){                    //如果tp对应nums2中的索引到达nums2尾部,那就不push了
20                 min_heap.push(tp);
21             }
22         }
23         return res;
24     }
25 };

 

posted @ 2020-03-02 10:42  NeoZy  阅读(487)  评论(0编辑  收藏  举报