*LeetCode 3sum 3sum-closest 4sum k-sum类型 总结【待解决证明问题】


https://leetcode.com/problems/3sum/

https://leetcode.com/problems/3sum-closest/

https://leetcode.com/problems/4sum/


首先说2sum, 这个以前以为只有hash可以做,但是这种做法的问题在于,对于数的范围小或者整数可以,如果是double,那么考虑double的位数,比如3位小数,那么所有的数都乘以1000然后按照整数hash最后还原即可,但是这样容易爆掉啊,比如double精确到小数点5位,来1e7个数,所有数的和加起来,,恩 可能用long long安全些,并且空间开销也很大。。。

然而后来发现,原来也可以线性扫描做。 《编程之美》2.12有的,于是这几个题都是这个变形。 如果是k-sum 可以O(n^(k-1))搞定。之前有人说  4sum可以hash更高效,然而我觉得思路有问题。比如 http://www.2cto.com/kf/201503/383807.html

其实生成pair之后,pair的个数已经是n^2个了,然后即使不考虑你在判重上的问题,只是把这n^2个pair对线性扫描,时间已经是n^2*n^2了,因为这里所谓的线性扫描 针对的是所有的n^2个pair啊


关于3-sum 做法的证明,看这里http://blog.csdn.net/zhz1993622/article/details/44422017,然而没看懂 而且找了很多也没找到合理的证明过程。  正在看算法导论 以后再证明


下面帖3-sum代码:

3-sum时间卡的挺紧的,

 if(i>0 && nums[i-1] == nums[i])continue;

这一行如果不加 会超时,然而这一行代码在4sum产生了新的坑


class Solution {
public:
    inline void add(vector < vector<int> >& ret, int a, int b, int c) {
        if(ret.size() != 0) {
            if( ret[ ret.size()-1 ][0] == a &&
               ret[ ret.size()-1 ][1] == b &&
               ret[ ret.size()-1 ][2] == c ) return;

        }
        vector <int> nums;
        nums.push_back(a);
        nums.push_back(b);
        nums.push_back(c);
        ret.push_back(nums);
    }

    vector<vector<int> > threeSum(vector<int>& nums) {
        vector < vector<int> > ret;
        if(nums.size() < 3)return ret;
        sort(nums.begin(), nums.end());
        for(int i=0;i<nums.size()-2;i++) {
            if(i>0 && nums[i-1] == nums[i])continue;
            int j=i+1,k=nums.size()-1;
            while(j < k) {
                int sum = nums[i] + nums[j] + nums[k];
                if(sum == 0) {
                    add(ret, nums[i], nums[j], nums[k] );
                    while(j<k && nums[j+1] == nums[j])j++;
                    while(j<k && nums[k-1] == nums[k])k--;
                    j++;
                    k--;
                }
                if(sum < 0) j++;
                if(sum > 0) k--;
            }
        }
        return ret;
    }
};

3sum-closest

class Solution {
public:
    inline int Abs(int x) {
        return (x>0) ? x : -x ;
    }

    bool bigger(int x, int y) {
        return Abs(x)>=Abs(y);
    }
    int threeSumClosest(vector <int> & nums, int target) {
        int ret = 0, dis = 200000000;
        if(nums.size() < 3) {
            for(int i=0; i<nums.size(); i++) {
                ret += nums[i];
            }
            return ret;
        }
        sort(nums.begin(), nums.end());
        int sum =0;
        for(int i=0; i<nums.size()-2; i++) {
            if(i && nums[i]==nums[i-1])continue;
            int l = i+1, r = nums.size()-1;
            while(l < r) {
                sum = nums[l] + nums[r] + nums[i];
                int tmpdis = sum - target;
                //dis = min(Abs(tmpdis), dis);
                if(dis > Abs(tmpdis)) {
                    ret = sum;
                    dis = Abs(tmpdis);
                    //cout << nums[i] << nums[l] << nums[r] << endl;
                }
                if(tmpdis == 0) return target;
                else {
                    if(tmpdis < 0)l++;
                    else r--;
                }
            }
        }
        return ret;
    }

};

4-sum  

class Solution {
public:
    inline void add(vector< vector<int> >& ret, int a, int b, int c, int d) {
        if(ret.size() != 0) {
            if(ret[ ret.size()-1 ][0] == a &&
               ret[ ret.size()-1 ][1] == b &&
               ret[ ret.size()-1 ][2] == c &&
               ret[ ret.size()-1 ][3] == d) return ;
        }
        vector<int> nums;
        nums.push_back(a);
        nums.push_back(b);
        nums.push_back(c);
        nums.push_back(d);
        ret.push_back(nums);
    }
    vector< vector<int> > fourSum(vector<int>& nums, int target) {
        vector < vector<int> > ret;
        if(nums.size() < 4) return ret;
        sort(nums.begin(), nums.end());
        for(int i=0;i<nums.size()-3;i++) {
            if(i>0 && nums[i] == nums[i-1])continue;
            bool flag=false;
            for(int j=i+1;j<nums.size()-2; j++) {
                if(flag && nums[j] == nums[j-1])continue;
                int l=j+1,r=nums.size()-1;
                while( l < r ) {
                    int sum = nums[i] + nums[j] + nums[l] + nums[r];
                    if(sum == target) {
                        add(ret, nums[i], nums[j], nums[l], nums[r]);
                        flag = true;
                        while(l  < r && nums[l+1]==nums[l] )l++;
                        while(l<r && nums[r-1]==nums[r])r--;
                        l++;
                        r--;
                    } else if( sum < target ) {
                                l++;
                            } else {
                                r--;
                            }
                }
            }
        }
        return ret;
    }
};

这里面有个坑,

 if( nums[j] == nums[j-1])continue;
这样的判重方式在什么情况下是合理的? 答案:target=0的时候

因为有这样的数据 比如  输入{1,1,1,1} target=4, 那么如果这么判断就没有答案,因为直接num[j] == num[j-1]一直成立,导致第二个指针直接把数都扫完。我的解决方法:只有在得到一个解的时候,才在某个元素重复的时候直接跳过。也就是上面flag的作用



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值