⭐【分类思想+堆】LeetCode 373. Find K Pairs with Smallest Sums

题目描述

在这里插入图片描述

知识点

分类思想+堆

结果

在这里插入图片描述

实现

码前思考

  1. 碰见这个问题,我的第一感受是暴力,即nums1nums2两个数组的数匹配,然后排序。但是这实在是太暴力了,肯定会超时的,所以要想其他的巧解的方法!;
  2. 首先我们得想我们每次都要找所有pair(u,v)里面最小的值min ,直到找到第k个,那么怎么才能快速找到呢?
  3. 其实,由于nums1nums2的升序的特性,我们将求所有pair(u,v)里面最小的值min转换成当前nums1中的每个数能得到的与nums2中的最小和的里面最小的那个。比如下面的例子:
    在这里插入图片描述

代码实现

//对于暴力的问题要使用自己的思考将其转换成另一个问题,最重要的思想就是分类
//分类就是对结果的所有可能的结果进行分类
//所谓最小的数,就是u里面每个数的对应v里面最小的数的和的最小的数
//其实可以用优先队列的!!!
struct node{
    int u;  //代表下标
    int v;  //代表下标
    int sum;    //代表值

    //注意要自己添上默认的构造函数
    node(){}

    //自己定义的构造函数
    node(int _u,int _v,int _sum):u(_u),v(_v),sum(_sum){}
};

class Solution {
public:
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        //堆的大小恒定为nums1.size
        int sizeu = nums1.size();
        int sizev = nums2.size();
        vector<node> heap;

        //结果
        int sizer = min(sizeu*sizev,k);
        vector<vector<int>> res(sizer);

        if(sizer == 0){
            return vector<vector<int>>();
        }

        //初始化堆,即建堆
        for(int i=0;i<sizeu;i++){
            heap.push_back(node(i,0,nums1[i]+nums2[0]));
        }

        int sizeh = sizeu;

        int idx = 0;
        while(idx < sizer){
            //将堆顶拿出来,并且放入下一个,再向下调整
            res[idx].push_back(nums1[heap[0].u]);
            res[idx].push_back(nums2[heap[0].v]);
            
            //如果v到头了
            if(heap[0].v == sizev-1){
                heap[0] = heap[sizeh-1];
                sizeh--;
            }else{
                heap[0].v = heap[0].v+1;
                heap[0].sum = nums1[heap[0].u] + nums2[heap[0].v];
            }
            //自上而下进行调整
            downAdjust(heap,0,sizeh-1);

            idx++;
        }

        return res;
    }

    void downAdjust(vector<node> &heap,int low,int high){
        int i=low;
        int j = (2*i)+1;
        while(j<=high){
            if(j+1 <= high && heap[j+1].sum < heap[j].sum){
                j = j+1;
            }
            if(heap[i].sum > heap[j].sum){
                //不知道swap能不能这么用
                swap(heap[i],heap[j]);
                i = j;
                j = (2*i)+1;
            }else{
                break;
            }
        }
    }
};

码后反思

  1. 最重要的一点就是求解本题的思想——对于暴力的问题要使用自己的思考将其转换成另一个问题,最重要的思想就是 分类分类就是对结果的所有可能的结果进行分类,巧妙地利用每个类别的 特殊性质快速找到该类的答案,然后汇总所有的答案得到最终结果

    通过这些类别里面的值,这种思想在我们的爬楼梯有多少种方法分糖果有多少种方法里面都有!动态规划的最优子结构也是这种思想的一种体现!

  2. ⭐⭐⭐⭐⭐我看网友的题解说 只要涉及到了前K个最优值的问题,一般都要想到堆 。我觉得这句话说的有道理,因为堆恰好能每次求一个最值,而不像其他排序算法要全部排完才行!

  3. 巩固了结构体的使用
    在这里插入图片描述在这里插入图片描述

  4. 原来 swap() 不仅可以作用域基本数据类型,自己定义的结构体也可以这样使用!

  5. 可以使用STL自带的 priority_queue 来实现堆的,使用 priority_queue 修改后的代码:
    在这里插入图片描述

    //对于暴力的问题要使用自己的思考将其转换成另一个问题,最重要的思想就是分类
    //分类就是对结果的所有可能的结果进行分类
    //所谓最小的数,就是u里面每个数的对应v里面最小的数的和的最小的数
    //其实可以用优先队列的!!!
    struct node{
        int u;  //代表下标
        int v;  //代表下标
        int sum;    //代表值
    
        //注意要自己添上默认的构造函数
        node(){}
    
        //自己定义的构造函数
        node(int _u,int _v,int _sum):u(_u),v(_v),sum(_sum){}
    
        friend bool operator <(node a,node b){
            return a.sum > b.sum;
        }
    };
    
    class Solution {
    public:
        vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
            //堆的大小恒定为nums1.size
            int sizeu = nums1.size();
            int sizev = nums2.size();
    
            priority_queue<node> q;
    
            //结果
            int sizer = min(sizeu*sizev,k);
            vector<vector<int>> res(sizer);
    
            if(sizer == 0){
                return vector<vector<int>>();
            }
    
            //初始化堆,即建堆
            for(int i=0;i<sizeu;i++){
                q.push(node(i,0,nums1[i]+nums2[0]));
            }
    
            int sizeh = sizeu;
    
            int idx = 0;
            while(idx < sizer){
                int u = q.top().u;
                int v = q.top().v;
                res[idx].push_back(nums1[u]);
                res[idx].push_back(nums2[v]);
                
                //如果v到头了
                if(v == sizev-1){
                    q.pop();
                }else{
                    q.pop();
                    q.push(node(u,v+1,nums1[u]+nums2[v+1]));
                }
    
                idx++;
            }
    
            return res;
        }
    };
    
  6. 注意要考虑nums1nums2其中有一个为空的情况,这真的是LeetCode的传统艺能了,每次都栽在这上面。。。

发布了138 篇原创文章 · 获赞 3 · 访问量 3789

猜你喜欢

转载自blog.csdn.net/yc_cy1999/article/details/105301786