LeetCode中3Sum,4Sum思路,以及kSum思路

题目:

Given an array nums of n integers, are there elements abc in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note:The solution set must not contain duplicate triplets.

Example:Given array nums = [-1, 0, 1, 2, -1, -4],A solution set is:[[-1, 0, 1],[-1, -1, 2]]

一眼看这个题目感觉还行啊,于是无脑穷举遍历。

代码如下: 

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<int>::iterator iter,iter2,iter3;
        vector <vector<int> >::iterator iter4; 
        int i,a,b,c,k;
        vector<vector<int>> res;
        if(nums.size()<3)
            return res;
        sort(nums.begin(),nums.end());
        k=0;
        for(iter=nums.begin();iter!=(nums.end()-2);++iter)
        {
            for(iter2=(iter+1);iter2!=(nums.end()-1);++iter2)
            {
                for(iter3=(iter2+1);iter3!=nums.end();++iter3)
                {
                    vector<int> res1;
                    if((*iter+*iter2+*iter3)==0)
                    {
                            res1.push_back(*iter);
                            res1.push_back(*iter2);
                            res1.push_back(*iter3);
                            res.push_back(res1);
                            k++;
                    }
                }
            }
        }
        //去重
	    sort(res.begin(),res.end());
        for(iter4=res.begin();iter4!=res.end();)
        {
            if(*iter4==*(iter4+1))
            {
                res.erase(iter4);
            }else{
                iter4++;   
            }
        }
        return res;
    }
};

 想着数据量好像也不大啊,没啥毛病啊,可能是我写的太low了。(三个循环肯定超时)

 于是只能想着用别的办法了。

 想象下一个数组中,找两个数,他们的和为0,那么可以表示为a+b=0,一般用两个循环就可以解决了,(当然还有更加高效的解法,这里就不引申了),那么三个数呢?题目中三个数是这样表达的a+b+c=0,那是不是可以变换下等式,a+b=-c,这样一来我们可以先找到两个数的组合,然后匹配第三个数,这样一来就可以用两个循环解决了,时间复杂度上就降了很多,从原来的O(N^{3}),就变成了O(N^{2})。那么具体要怎么实现能?我下面简单说明下思路。(了解了三个数相加后可以考虑下k个数相加的情况)

首先当然是对这个数组进行排序处理,这样有什么好处呢?排序处理后我们就可以从数组当中首位开始取数,一个个往后找,可以避免很多重复的,对重复的数可以直接跳过,同时在找第三个数c的时候也可以按顺序往前找,可以减少很多操作。

然后第一重循环可以先找数组中的第一个数nums[0]作为a,然后nums[0+1]作为b。那么第二重循环,可以找第三个数了,这样两个循环就能解决问题了,但是第三个数从哪里开始找呢?

 看上面这张图,既然a和b都是复数,并且这两个数是整个数组中最小的,那么它们俩相加的值是不是也是所有数中的两个数相加和最小的呢?答案是肯定的,那么后面c就要取一个最大的数来相加了,这样结果才可能等于0,数组已经排好序了,这样我们就可以从数组中从后往前找了,最后一个数肯定是最大的,这就是前面为什么排序的原因之一了。

 这里分别用i,j,k,来控制a,b,c的下标移动。

那么如上图所示一开始可以取a=nums[i],b=nums[j]  (j=i+1),c=nums[len-1]  (len=nums.size())

然后第一重循环可以控制i的移动,第二重循环可以控制j和k的移动。

首先i取0的时候,j取1,k取len-1 (第一重循环)

(下面是第二重循环)

1.然后判断a+b是否小于-c,如果a+b小于-c了,那么这种情况c的下标k就不需要往前移了,再往前移c只会越来越小。所以这里就直接b的下标往后移一个j++,如上图这个时候j就是在2这个位置了,然后再继续从最后找c。

2.再判断a+b是否大于-c,如果a+b大于-c,那么c的下标k就往前移找一个更小的,直到a+b=-c,如果找不到那就结束,继续找下个b

3.最后一个判断a+b是否等于-c,如果a+b等于-c,那么结果就是需要找的这个-c,然后就把这三个数存到vector中去。

关键步骤就结束了,但是在第二个循环开始之前还是可以再做点优化的,比如第一个数a_{i}=a_{i+1},这种情况可以直接跳过,因为和前面那个数重复了,后面都是做重复的操作。还有当a_{i}>0的时候,这里就可以直接结束整个遍历了,因为一个排好序的数组,不可能三个大于0的整数相加等于0的,所以直接结束。

下面贴上代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        if(nums.size()<3)
            return res;
        sort(nums.begin(),nums.end());
        for(int i=0,len=nums.size();i<len;i++)
        {
            if(nums[i]<=0)
            if(i==0||nums[i]>nums[i-1]){
            int j=i+1;
            int k=len-1;
            while(j<k)
            {
                vector<int> res1;
                if(nums[i]+nums[j]+nums[k]<0){
                    j++;
                }else if(nums[i]+nums[j]+nums[k]>0){
                    k--;
                }else{
                    res1.push_back(nums[i]);
                    res1.push_back(nums[j]);
                    res1.push_back(nums[k]);
                    res.push_back(res1);
                    j++;
                    k--;
                }
            }
            }
        }
        //去重
        vector<vector<int>>::iterator iter;
	    sort(res.begin(),res.end());
        for(iter=res.begin();iter!=res.end();)
        {
            if(*iter==*(iter+1))
            {
                res.erase(iter);
            }else{
                iter++;   
            }
        }
        return res;
    }
};

了解了上面的三个数相加等于0的思路后,可以思考下四个数相加等于0的情况。

三个数相加等于0,我们可以看成两个数相加的问题的扩展,也就是时间复杂度从 O(N^{3}),就变成了O(N^{2}),降了一次,那么四个数相加的问题是不是也可以考虑成三个数相加的问题呢?答案是肯定的。这样一来,我们可以在第一个循环后面加一个循环就能解决四个数相加的问题了。那么k个数呢?

k个数也可以模仿上面的思路把O(N^{k})降为O(N^{k-1}),所以这种思路最多也就能优化降一次方,不知道还有没有别的优化方法了,如果还有别的更好的办法大家可以在下面评论中给出。

下面贴下4Sum的代码,对应leetcode上的第十八题。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
         vector<vector<int>> res;
        if(nums.size()<4)
            return res;
        sort(nums.begin(),nums.end());
        int len=nums.size();
        for(int i=0;i<len;i++)
        {
            //if(nums[i]<=target)
            if(i==0||nums[i]>nums[i-1]){
            for(int m=i+1;m<len;m++)//这里多加了一个循环
            {
                int j=m+1;
                int k=len-1;
                while(j<k)
                {
                    vector<int> res1;
                    if(nums[i]+nums[m]+nums[j]+nums[k]<target){
                        j++;
                    }else if(nums[i]+nums[m]+nums[j]+nums[k]>target){
                        k--;
                    }else{
                        res1.push_back(nums[i]);
                        res1.push_back(nums[m]);
                        res1.push_back(nums[j]);
                        res1.push_back(nums[k]);
                        res.push_back(res1);
                        j++;
                        k--;
                    }
                }
            }
            
            }
        }
        //去重
        vector<vector<int>>::iterator iter;
	    sort(res.begin(),res.end());
        for(iter=res.begin();iter!=res.end();)
        {
            if(*iter==*(iter+1))
            {
                res.erase(iter);
            }else{
                iter++;   
            }
        }
        return res;
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章