leetcode 第 373 题:查找和最小的K对数字(C++)

373. 查找和最小的K对数字 - 力扣(LeetCode)

优先级队列的典型题目,思路很像数据结构与算法:44 |最短路径:地图软件如何计算最优出行路径?_zj-CSDN博客里面的总结引申部分,思路也和LeetCode第 743 题:网络延迟时间(C++)_zj-CSDN博客的Dijkstra算法的优先级优化思路很像。

最直接的,把所有可能的元素对都加入优先级队列,最后取出前k个就可以。但是这样一股脑全加进去,内存可能会爆。

这题难点在于处理的时候怎么去重,如果使用双指针在两个数组之间滚动的话,可能会出现重复项(虽然用哈希表很方便去重,但是优先级队列一用,代码真的很臃肿了,而且哈希表好像不能存储pair类型),非要使用二维数组也不是不可以。

不过,有更好的思路,不用双指针在两个数组上滚动。参考:8ms 100%,记录位置指针快速求解 - 查找和最小的K对数字 - 力扣(LeetCode)

class Solution{
public:
    vector> kSmallestPairs(vector &nums1, vector &nums2, int k){
        int rows = nums1.size(), cols = nums2.size();
        vector> res; //输出结果
        if (rows == 0 || cols == 0) return res;
        auto cmp = [&nums1, &nums2](const pair &a, const pair &b) { return nums1[a.first] + nums2[a.second] > nums1[b.first] + nums2[b.second]; };//这个函数很巧,放在里面可以调用nums1和nums2比较对应下标处的元素
        priority_queue, vector>, decltype(cmp)> q(cmp);

        for (int i = 0; i < rows; i++)  q.emplace(i, 0);//一次性添加nums1的下标

        while (k > 0 && !q.empty()){
            k--; 
            auto cur = q.top();
            q.pop(); 
            //这儿的添加方式也很巧,不会导致重复添加
            if (cur.second+1 <= cols-1){//一次添加一个nums2的下标
                q.emplace(cur.first, cur.second + 1); //nums2对应的下标右走一步
            }

            res.push_back({nums1[cur.first], nums2[cur.second]});
        }
        return res;
    }
};

哎,思维僵化了,受了数据结构那个博客的影响就一直往那个方向想,思维跳不出来,一直想重复项的事,把自己带进去了。

出门走一趟神清气爽,接着梳理:

最差的方法,将所有的元素对一次性放入优先级队列,然后向外取k次就行了。缺点是内存占用很大。

那怎么进行优化呢?想想,我们并没有使用到数组元素有序这个条件,我们不是需要把全部元素放入队列才可以取出最小值的。举个例子,将nums1的第一个元素和nums2中的所有元素形成的元素对放入优先级队列,那此时的队头必然是最小值,因为数组都是升序的,所以我们就可以将队头取出来放入结果里面。

上面的思路其实就是第一个代码体现的思路,但是还是不够高效。我们其实也没有必要放那么多元素到队列里面去,因为我们只需要找到前k个最小的元素对就可以了,所以我们就维护一个大小为k的队列就可以了。

说到这儿发现这个思路之前刷题已经使用过很多次了,但是自己竟然没有想起来。。。

class Solution{
public:
    struct cmp{
        bool operator() (const pair &a, const pair &b) {
            return a.first + a.second < b.first + b.second; 
        }
    };
    vector> kSmallestPairs(vector &nums1, vector &nums2, int k){
        int n = nums1.size(), m = nums2.size();
        vector> res; //输出结果
        if (n == 0 || m == 0) return res;
        priority_queue, vector>, cmp> q;//这儿用的是大顶堆

        for(int i = 0; i < n; ++i){//两层循环遍历元素
            for(int j = 0; j < m; ++j){
                if(q.size() < k)
                    q.push({nums1[i], nums2[j]});
                else if(nums1[i]+nums2[j] < q.top().first + q.top().second){//比此时对首的最大值要小,那就取而代之
                    q.pop();
                    q.push({nums1[i], nums2[j]});
                }
            }
        }
        while(!q.empty()){
            res.push_back({q.top().first, q.top().second});
            q.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

你可能感兴趣的:(leetcode,leetcode,队列)