Two Sum & 3Sum & 3Sum Closest & 4Sum

(0) Two Sum (注,这道题最后才发现,所以坐标从0开始)

一开始看觉得好简单直接遍历所有可能不就好了吗,O(n*n)。

 vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> ret;
        int i,j,n=numbers.size();
        
        if(n<2)
            return ret;
        
        for(i=0;i<n-1;i++)
        {
            j=i+1;
            while(j<n){
                if(numbers[i]+numbers[j]==target)
                {
                    ret.push_back(i+1);
                    ret.push_back(j+1);
                    return ret;
                }
                j++;
            }
        }
    }

结果 Time Limit Exceeded果然n太大时会超时。

想了想觉得(1)的那种首尾指针时间复杂度会减少(O(nlogn)+O(n)=O(nlogn)),但是这个需要输出坐标的,如果排序之后坐标怎么保存呢,后来搜了搜参考了[4],巧妙利用了sort()函数第三个可定义判断条件的参数,还需自己新建一个结构体同时保存原来的值和坐标,结构体排序后保持对应不变。

typedef struct node{
    int val;
    int pos;
    }Node;

bool compare(Node a, Node b){
    return a.val<=b.val;
}

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
         vector<int> ret;
        int i,j,n=numbers.size(),sum;
        vector<Node> tmp(n);
        
        if(n<2)
            return ret;
        
        for(i=0;i<n;i++){
            tmp[i].val=numbers[i];
            tmp[i].pos=i+1;
        }
        
        sort(tmp.begin(),tmp.end(),compare);
        
        i=0;j=n-1;
        while(i<j){
            sum=tmp[i].val+tmp[j].val;
            if(sum==target)
                {
                    ret.push_back(min(tmp[i].pos,tmp[j].pos));
                    ret.push_back(max(tmp[i].pos,tmp[j].pos));
                    return ret;
                }
            else if(sum<target)
                i++;
            else j--;
        }
        }
   
};

Accepted(48ms)。
[2]中还介绍了一个O(n)的方法,很巧妙地利用map的特性。
class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {

        vector<int> result;
        map<int,int> mapping;
        
        for (int i=0; i<numbers.size(); ++i)
        {
            if (mapping[ target - numbers[i]] > 0)
            {
                result.push_back(mapping[ target - numbers[i]]);
                result.push_back(i+1);               
            }
            else
                mapping[ numbers[i] ] = i+1;
        }
        return result;        
    }
};
Accepted(103ms)。时间反而长了,可能是因为对map的处理(查找,插入)占了比较大的开销。

(1) 3sum

这是一道求三个数为固定值的题,参考到了编程之美中快速寻找符合条件的两个数或者三个数[1]的算法:

先用快速排序对数组排序,让后用双指针(双索引)法对排序好的数组进行反向遍历,并且遍历的方向不变。(若是计算两个数的和,则初始化为i=0,j=n-1,若是计算两个数的差,则初始化为i=0,j=1)。之所以这样遍历方式能成功,是因为排序后,若ai+aj<sum,则ai+ak<sum(k=i,i+1...j),而i之前j之后的情况已遍历过,所以只有i++才可能有等号的情况;若ai+aj>sum,则ak+aj>sum(k=i,i+1...j),而i之前j之后的情况已遍历过,所以只有j--才可能有等号的情况。时间复杂度为O(nlogn)+O(n*n)=O(n*n)。

然后就唰唰唰写下第一个版本:

class Solution { //version 1.0
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        
        vector<vector<int>> ret;
        int i,j,k,n=num.size(),sum;
        
        sort(num.begin(),num.end());
        
        if(n<=2)       
            return ret;        
                
        for( k=0;k<n;k++){
            sum=0-num[k];
            i=k+1;
            j=n-1;
            if(i==j)
                break;
            while(i<j){
            if(num[i]+num[j]==sum)
                {
                    vector<int> tmp;
                    tmp.push_back(num[i]);  
                    tmp.push_back(num[j]);  
                    tmp.push_back(num[k]);  
                    ret.push_back(tmp);
                    i++;
                    j--;
                }
            else if(num[i]+num[j]<sum)
                i++;
            else 
                j--;
            }
        }
        return ret;
    }
};
结果提示: Output Limit Exceeded

后来网上搜了一通参考到[2],才明白我的输出包含相同的例子,所以输出非常多导致时间超了。

然后改了改...

class Solution {  //version 2.0
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        
        vector<vector<int>> ret;
        int i,j,k,n=num.size(),sum;
        
        sort(num.begin(),num.end());
        
        if(n<=2)
            return ret;
                
        for( k=0;k<n;k++){
            
            if(k > 0 && num[k]==num[k-1])  //过滤相同的k
                continue;  
                
            sum=0-num[k];
            i=k+1;
            j=n-1;
            if(i==j)
                break;
            while(i<j){
                
            if(i>k+1&&num[i]==num[i-1]){   //过滤相同的i
                    i++;  
                    continue;  
                } 
            
            if(j<num.size()-1&& num[j]==num[j+1]){  //过滤相同的j
                    j--;  
                    continue;  
                }  
                
            if(num[i]+num[j]==sum)
                {
                    vector<int> tmp;
                    tmp.push_back(num[k]);  
                    tmp.push_back(num[i]);  
                    tmp.push_back(num[j]);  
                    ret.push_back(tmp);
                    i++;
                    j--;
                }
            else if(num[i]+num[j]<sum)
                i++;
            else 
                j--;
            }
        }
        return ret;
    }
};

Accepted (254ms)!!!

(2) 3sum Closest
这是看起来是(1)的加强版,其实更简单了,只要求符合条件为三个数的和最接近目标数的那个和。算法基本不变,只要先设定好一个初值ret(参考[3],发现其实一开始随便指定三个数和为ret的初始值就可以了),然后按照(1)的算法每次循环中选出的三个数的和与ret比较谁更接近目标数,更新ret。时间复杂度依旧是O(n*n)。
class Solution {
public:
    int threeSumClosest(vector<int> &num, int target) {
        
        int i,j,k,n=num.size(),sum,ret,flag=true;
        
        sort(num.begin(),num.end());
        
        if(n<=2)
            return 0;
                
        ret=num[0]+num[1]+num[2];  //ret初始值
        
        for( k=0;k<n;k++){
            
            if(k > 0 && num[k]==num[k-1])  //过滤相同的k
                continue;  
                
            i=k+1;
            j=n-1;
            if(i==j)
                break;
            
            while(i<j){
                
            if(i>k+1&&num[i]==num[i-1]){   //过滤相同的i
                    i++;  
                    continue;  
                } 
            
            if(j<num.size()-1&& num[j]==num[j+1]){  //过滤相同的j
                    j--;  
                    continue;  
                }  
                
            sum=num[i]+num[j]+num[k];   
            
            if(abs(sum-target)<abs(ret-target))
                ret=sum;
                
            if(sum==target)
                return target;
            else if(sum<target)
                i++;
            else 
                j--;
            }
        }
        return ret;
        
    }
};
Accepted (54ms)!!!
(3) 4sum
这个比(1)单纯的多了一维,所以时间复杂度是O(n*n*n)。
class Solution {
public:
    vector<vector<int> > fourSum(vector<int> &num, int target) {
         vector<vector<int>> ret;
        int i,j,k,l,n=num.size(),sum;
        
        sort(num.begin(),num.end());
        
        if(n<4)
            return ret;
         
        for(l=0;l<=n-4;l++){
           if(l > 0 && num[l]==num[l-1])  //过滤相同的l
                continue;  
                
        for( k=l+1;k<=n-3;k++){
            
            if(k > l+1 && num[k]==num[k-1])  //过滤相同的k
                continue;  
                
            sum=target-num[l]-num[k];
            i=k+1;
            j=n-1;
            if(i==j)
                break;
            while(i<j){
                
            if(i>k+1&&num[i]==num[i-1]){   //过滤相同的i
                    i++;  
                    continue;  
                } 
            
            else if(j<num.size()-1&& num[j]==num[j+1]){  //过滤相同的j
                    j--;  
                    continue;  
                }  
                
            else if(num[i]+num[j]==sum)
                {
                    vector<int> tmp;
                    tmp.push_back(num[l]);
                    tmp.push_back(num[k]);  
                    tmp.push_back(num[i]);  
                    tmp.push_back(num[j]);  
                    ret.push_back(tmp);
                    i++;
                    j--;
                }
            else if(num[i]+num[j]<sum)
                i++;
            else 
                j--;
            }
        }
        }
        return ret;
    }
};
Accepted (454ms)!!!

小结:一个晚上刷了四道题真爽 !!!



参考:
[1] http://blog.csdn.net/linyunzju/article/details/7720413
[2] http://blog.csdn.net/xshalk/article/details/8148422

[3] http://www.cnblogs.com/remlostime/archive/2012/10/27/2742667.html

[4]http://blog.csdn.net/ithomer/article/details/8783952


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值