【LeetCode每日一题】373:查找和最小的K对数字

119 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

LeetCode每日一题打卡专栏正式启动!不出意外将日更LeetCode的每日一题,敬请期待。

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

题意

给定两个以 升序排列 的整数数组 和 , 以及一个整数 。

定义一对值 (u,v),其中第一个元素来自 ,第二个元素来自 。

请找到和最小的 k 个数对 , ... 。

示例

示例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]

提示:

  • 和均为升序排列

题解:优先队列

由于题目所求为前k个最小的数对,同时两个数组均为升序排列,所以最小的一定为,次小的必定在之中,而具体是哪个需要判断:

  • 如果直接二重循环暴力判断,题目给定数组长度为显然不行
  • 考虑用数据结构存储,从而判断哪个次小,于是首选优先队列

如果优先队列存储和的值的话,可能会出现重复,于是优先队列只存储和的下标.

因为题目只需要前k个最小的,于是最多只需要出队k次即可。

  • 时间复杂度:O(klogk) (因为需要出队k次,同时每次入队需要logk的时间复杂度)
  • 空间复杂度:O(k)

C++代码:

!!记得a,b数组初始置空,LeetCode判题机到现在还没摸清

vector<int> a,b;
class Solution {
public:
    struct node{
        int u,v;
        bool operator < (const node p) const{
            return a[u]+b[v] > (a[p.u]+b[p.v]);
        }
    };
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        a.clear();b.clear();
        int n=nums1.size(),m=nums2.size();
        for(int i=0;i<n;i++) a.push_back(nums1[i]);
        for(int i=0;i<m;i++) b.push_back(nums2[i]);
        vector<vector<int> >ans;
        
        //定义优先队列,存储两数组元素下标
        priority_queue<node> q;
        //先nums1所有的下标入队
        for(int i=0;i<min(k,n);i++){
            q.push(node{i,0});
        }
        //最多出队k次
        while(k>0&&!q.empty()){
            node now=q.top();q.pop();
​
            vector<int> ve;ve.push_back(nums1[now.u]);ve.push_back(nums2[now.v]);
            ans.push_back(ve);
            //nums2数字下标右移一位,然后入队
            if(++now.v<m)
                q.push(node{now});
            k--;
        }
        return ans;
    }
};

Java代码:

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        //返回值
        List<List<Integer>> ans=new ArrayList<>();
        
        //定义优先队列存储两数组下标
        PriorityQueue<int[]> q=new PriorityQueue<>((a,b) ->
                nums1[a[0]]+nums2[a[1]]-(nums1[b[0]]+nums2[b[1]])); 
        //首先将nums1数组下标入队(因为不需要只需要入队k个即可,所以取个最小值)
        for(int i=0;i<Math.min(k, nums1.length);i++){
            q.add(new int[]{i,0});
        }
        //最多弹出k次
        while(k>0&&!q.isEmpty()){
            int[] a=q.poll();
            //返回当前最小的和
            List now=new ArrayList<>();
            now.add(nums1[a[0]]);now.add(nums2[a[1]]);
            ans.add(now);
            
            //nums2数组下标右移,然后继续入队
            if(++a[1]<nums2.length)
                q.add(a);
            k--;
        }
        return ans;
    }
}