题解Find K Pairs with Smallest Sums


You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k.

Define a pair (u,v) which consists of one element from the first array and one element from the second array.

Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums.

题目链接:

https://leetcode.com/problems/find-k-pairs-with-smallest-sums/ 


这题犯2了,想了个思路,实现了好久,很麻烦,但因为思路是错的,最后还是错的。
一开始就想找最好的方法,但想出来的方法又是错的,所以啥也没干成。
其实一个题目到手的时候,先想最brute force的方法,这样至少有一个保底的方法,然后再想想是不是可以优化一下,是不是有更好的算法,这样的顺序可能更好一点。
这题这里有一个比较好的循序渐进的解法:  http://www.cnblogs.com/grandyang/p/5653127.html 

思路比较难理解,理一理,递归解决,假设我们已经得到了第m个pair pij,那么下一个pair肯定是p(i+1, j)或者是p(i, j+1)中间的一个,但是不能把这两个都推进去,因为可能会有重复,比如如果之前已经有最小的 p(i+1, j-1)或者有最小的p(i-1, j+1)的话,那priority_tree里头肯定就存在p(i+1,j)或者p(i, j+1)的一个了,这样就重复了。
简单的解决办法: 增加1个机制判断是否已经访问过,访问过的话就不加了。我用了一个unordered_set<int> 来做判断, 开始用的不是int而是pair<int, int>,但是这个需要自己写hash函数,所以还是改成int,用i * nums2.size() + j做key value

<span style="color:#000000;">class Solution {
public:
    vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<pair<int, int> > result;
        unordered_set<int> visited;//judge weh
        auto comp = [&nums1, &nums2](const pair<int,int>& p1, const pair<int, int>& p2){
            return nums1[p1.first] + nums2[p1.second] > nums1[p2.first] + nums2[p2.second];
        };
        priority_queue<pair<int, int>, vector<pair<int,int> >, decltype(comp) > pair_q(comp);
        
        if(nums1.size() == 0 || nums2.size() == 0) return result;
        pair_q.push(pair<int,int>(0, 0));
        visited.insert(0);
        while(pair_q.size() && result.size() < k){
            pair<int, int> cur_pair = pair_q.top();
            result.emplace_back(nums1[cur_pair.first], nums2[cur_pair.second]);
            pair_q.pop();
            if(cur_pair.first+1 < nums1.size()){
                pair<int,int> next_pair(cur_pair.first+1, cur_pair.second);
                int key = next_pair.first * nums2.size() + next_pair.second;
                if(visited.find(key) == visited.end()) {
                    visited.insert(key);
                    pair_q.push(next_pair);
                }
            }
            
            if(cur_pair.second+1 < nums2.size()){
                pair<int,int> next_pair(cur_pair.first, cur_pair.second+1);
                int key = next_pair.first * nums2.size() + next_pair.second;
                if(visited.find(key) == visited.end()) {
                    visited.insert(key);
                    pair_q.push(next_pair);
                }
            }
        }
        return result;
    }
};</span>


复杂一点的解决办法:一般情况只推进去(i, j+1),这样可以避免重复,因为只有当前i,j才能得到i, j+1。那么(i+1, j)怎么办没推进去会不会影响呢?如果(i+1, j)比(i, j+1大,那无所谓,不推就不推。如果(i+1, j)比(i, j+1)小并且没有访问过,那么(i+1, j-1)肯定也没有访问过,但是它比(i+1, j)和(i, j+1)都要小,这样就跟紧跟在(i, j)后面的是(i+1,j)或者(i, j+1)矛盾了,所以假设不成立。当然唯一的例外是j==0的时候,这个时候就都要推进去。

class Solution {
public:
    vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<pair<int, int> > result;
        auto comp = [&nums1, &nums2](const pair<int,int>& p1, const pair<int, int>& p2){
            return nums1[p1.first] + nums2[p1.second] > nums1[p2.first] + nums2[p2.second];
        };
        priority_queue<pair<int, int>, vector<pair<int,int> >, decltype(comp) > pair_q(comp);
        
        if(nums1.size() == 0 || nums2.size() == 0) return result;
        pair_q.push(pair<int,int>(0, 0));
        while(pair_q.size() && result.size() < k){
            pair<int, int> cur_pair = pair_q.top();
            result.emplace_back(nums1[cur_pair.first], nums2[cur_pair.second]);
            pair_q.pop();
            if(cur_pair.first+1 < nums1.size() && cur_pair.second == 0)
                pair_q.emplace(cur_pair.first+1, cur_pair.second);
            
            if(cur_pair.second+1 < nums2.size())
                pair_q.emplace(cur_pair.first, cur_pair.second+1);
        }
        return result;
    }
};


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值