373查找和最小的K对数字(大顶堆、归并+小顶堆、大小堆)

本文介绍了如何高效地找到两个已排序数组中和最小的K对数字。提出了两种解法,分别是使用大顶堆和小顶堆。大顶堆方法的时间复杂度为O(m*n*logk),而小顶堆方法的时间复杂度为O(k*logn)。通过示例和代码实现详细阐述了这两种方法的工作原理。
摘要由CSDN通过智能技术生成

1、题目描述

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

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

找到和最小的 k 对数字 (u1,v1), (u2,v2) ... (uk,vk)。

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、题解

解法一:

基本思想:大顶堆,问题转化为215数组中的前K个最小元素,时间复杂度O(m*n*logk)。构造大顶堆,将前K个最小元素保存至大顶堆中,只要有比堆顶元素小的就入堆。

解法二:

基本思想:小顶堆,问题转化为23合并K个有序数组,时间复杂度O(k*logn)。如nums1={1,7,11} nums2={2,4,6},那么所有的组合就是:

(1,2)->(1,4)->(1,6)

(7,2)->(7,4)->(7,6)

(11,2)->(11,4)->(11,6)

构建nums1.size()也就是3个队首元素,在3个队首元素中找到最小的那个元素保存至res,同时该队指针后移一位,继续比较找出最小的k个元素。

  • 构造小顶堆,将前K个最小元素保存至小顶堆中,pair<int,int>中first是nums1中的元素,second是nums2中的下标
  • 把每个数组的第一个元素加入小顶堆,弹出小顶堆的堆顶元素
  • 弹出的元素来自哪个数组,就把那个数组的下一个元素加入小顶堆
  • 直到所有数组都没有下一个元素,我们只需要弹出K次,即可获取前K小的元素
#include<vector>
#include<iostream>
#include<algorithm>
#include<deque>
using namespace std;
bool compare(vector<int>& a,vector<int>& b)
{
    return a[0]+a[1]<b[0]+b[1];
}
class Solution {
public:
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        //基本思想:大顶堆,问题转化为215数组中的前K个最小元素,时间复杂度O(m*n*logk)
        //构造大顶堆,将前K个最小元素保存至大顶堆中,只要有比堆顶元素小的就入堆
        vector<vector<int>> res;
        k=nums1.size()*nums2.size()<k?nums1.size()*nums2.size():k;
        vector<vector<int>> max_heap;
        for(int i=0;i<nums1.size();i++)
        {
            for(int j=0;j<nums2.size();j++)
            {
                if(max_heap.size()<k||max_heap[0][0]+max_heap[0][1]>nums1[i]+nums2[j])
                {
                    max_heap.push_back({nums1[i],nums2[j]});
                    push_heap(max_heap.begin(), max_heap.end(),compare);
                }
                if(max_heap.size()>k)
                {
                    pop_heap(max_heap.begin(), max_heap.end(),compare);
                    max_heap.pop_back();
                }
            }
        }
        while(!max_heap.empty())
        {
            pop_heap(max_heap.begin(), max_heap.end(),compare);
            res.push_back(max_heap.back());
            max_heap.pop_back();
        }
        reverse(res.begin(),res.end());
        return res;
    }
};
class Solution {
public:
    struct Node{
        int i,j;
        int u,v;
        Node(){}
        Node(int i,int j,int u,int v):i(i),j(j),u(u),v(v){}
        bool operator>(const Node& a)const {return this->u+this->v>a.u+a.v;}
    };
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<vector<int>> res;
        if(nums1.size()==0||nums2.size()==0)  return res;
        k=nums1.size()*nums2.size()<k?nums1.size()*nums2.size():k;
        Node tmp;
        priority_queue<Node,vector<Node>,greater<Node>> min_heap;
        for(int i=0;i<nums1.size();i++)
            min_heap.push(Node(i,0,nums1[i],nums2[0]));
        while(k--)
        {
            tmp=min_heap.top();
            min_heap.pop();
            res.push_back(vector<int>{tmp.u,tmp.v});
            if(tmp.j+1!=nums2.size())
            {
                tmp.j++;
                tmp.v=nums2[tmp.j];
                min_heap.push(tmp);
            }
        }
        return res;
    }
};
class Solution1 {
public:
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        //基本思想:小顶堆,问题转化为23合并K个有序数组,时间复杂度O(k*logn)
        //构造小顶堆,将前K个最小元素保存至小顶堆中,pair<int,int>中first是nums1中的元素,second是nums2中的下标
        //把每个数组的第一个元素加入小顶堆,弹出小顶堆的堆顶元素
        //弹出的元素来自哪个数组,就把那个数组的下一个元素加入小顶堆
        //直到所有数组都没有下一个元素,我们只需要弹出K次,即可获取前K小的元素
        vector<vector<int>> res;
        if(nums1.size()==0||nums2.size()==0)  return res;
        k=nums1.size()*nums2.size()<k?nums1.size()*nums2.size():k;
        vector<pair<int,int>> min_heap;
        for(int i=0;i<nums1.size();i++)
        {
            min_heap.push_back(pair<int,int>{nums1[i],0});
            push_heap(min_heap.begin(), min_heap.end(),[nums2](pair<int,int>& a,pair<int,int>& b){return a.first+nums2[a.second]>b.first+nums2[b.second];});
        }
        while(k--)
        {
            res.push_back(vector<int>{min_heap[0].first,nums2[min_heap[0].second]});
            min_heap[0].second++;
            pair<int,int> temp=min_heap[0];
            pop_heap(min_heap.begin(), min_heap.end(),[nums2](pair<int,int>& a,pair<int,int>& b){return a.first+nums2[a.second]>b.first+nums2[b.second];});
            min_heap.pop_back();
            if(temp.second!=nums2.size())
            {
                min_heap.push_back(temp);
                push_heap(min_heap.begin(), min_heap.end(),[nums2](pair<int,int>& a,pair<int,int>& b){return a.first+nums2[a.second]>b.first+nums2[b.second];});
            }
        }
        return res;
    }
};
int main()
{
    Solution1 solute;
    vector<int> nums1{1,7,8};
    vector<int> nums2{2,4,6};
    int k=4;
    vector<vector<int>> res=solute.kSmallestPairs(nums1,nums2,k);
    for_each(res.begin(),res.end(),[](vector<int> v){cout<<v[0]<<" "<<v[1]<<endl;});
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值