Lintcode 三数之和

给出一个有n个整数的数组S,在S中找到三个整数a, b, c,找到所有使得a + b + c = 0的三元组。

 注意事项

在三元组(a, b, c),要求a <= b <= c。

结果不能包含重复的三元组。

样例

如S = {-1 0 1 2 -1 -4}, 你需要返回的三元组集合的是:

(-1, 0, 1)

(-1, -1, 2)

标签 

两根指针 排序 数组 脸书


分析:先对每一个元素建立一个哈希表然后两个for循环 得到三个数中的num1 num2,并计算num3=-num1-num2,在哈希表中查找num3,找到则将其与result中的向量进行比较,没有重复的话则添加进去,有重复则舍弃。


class Solution {
public:    
    /**
     * @param numbers : Give an array numbers of n integer
     * @return : Find all unique triplets in the array which gives the sum of zero.
     */
     void my_sort(vector<int> &temp,int num1,int num2,int num3){
         temp.push_back(num1);
         temp.push_back(num2);
         temp.push_back(num3);
         for(int i=0;i<2;i++){
             for(int j=0;j<2-i;j++){
                 if(temp[j]>temp[j+1]){
                     int tem=temp[j];
                     temp[j]=temp[j+1];
                     temp[j+1]=tem;
                 }
             }
         }
     }
    vector<vector<int> > threeSum(vector<int> &nums) {
        // write your code here
        
        vector<vector<int> > result;
        vector<int> temp;
        if(nums.empty())
        return result;
        map<int,int> my_map;
        int num1,num2,num3;
       // map<int,int>::iterator it;
       // it=my_map.end();
        for(int i=0;i<nums.size();i++){
            if(my_map.find(nums[i])==my_map.end())
            my_map[nums[i]]=1;
            else
            my_map[nums[i]]++;
        }
        for(int i=0;i<nums.size()-1;i++){
            my_map[nums[i]]--;
            for(int j=i+1;j<nums.size();j++){
                my_map[nums[j]]--;
                int flag=0;
                if(my_map[-nums[i]-nums[j]]>0){
                    my_sort(temp,nums[i],nums[j],-nums[i]-nums[j]);
                    for(int k=0;k<result.size();k++){
                        for(int l=0;l<3;l++){
                        if(temp[l]==result[k][l]){
                        flag++;
                        }
                        }
                        if(flag==3)
                        break;
                        else
                        flag=0;
                    }
                    if(flag!=3)
                    result.push_back(temp);
                    temp.clear();
                }
                my_map[nums[j]]++;
            }
            my_map[nums[i]]++;
        }
        return result;
    }
};





看到了一个更好的方法

问题分析

最容易想到的是穷举法,挑选第一个元素,然后在其后挑选第二个元素,再从除已经挑选出的两个元素之外挑第三个元素,判断三者之和是否为0;第二种想到的是用回溯递归,这两种方法的时间复杂度均为O(n^3),可参阅代码部分关于这两种方法的实现。

那有没有复杂度低一些的呢,答案是有的,就是使用两指针的方法,从而使复杂度下降到O(n^2)

首先,将数组按从小到大的排序,然后从头挑选一个元素,接着使用首尾两个指针来挑选后两个元素。如图所示(可结合后面实现代码理解):

 

转自:http://blog.csdn.net/s634772208/article/details/46729197

//两指针法  
void GetResultSet_2Ptr( const IntArray& mSrcArray )   
{  
    for( int k = 0; k < mSrcArray.size(); ++k )  
    {  
        //第一个数大于0,因为是从小到大排了序的,所以以下就不可能了。  
        if ( mSrcArray[k] > 0 ) break;  
  
        //去除重复结果  
        if ( k > 0 && mSrcArray[k] == mSrcArray[k-1] ) continue;  
  
        int i = k + 1;  
        int j = mSrcArray.size()-1;  
  
        //两指针向中间靠拢找结果  
        while( i < j )  
        {  
            int sum = mSrcArray[i] + mSrcArray[j] + mSrcArray[k];  
  
            //和过小,左边指针移动  
            if ( sum < 0 )  
            {  
                ++i;  
            }  
            //和过大,右边指针移动  
            else if ( sum > 0 )  
            {  
                --j;  
            }  
            //找到一个结果  
            else  
            {  
                IntArray mTempArray;  
                mTempArray.push_back( mSrcArray[k] );  
                mTempArray.push_back( mSrcArray[i] );  
                mTempArray.push_back( mSrcArray[j] );  
                gResultSet.push_back( mTempArray );  
  
                //避免重复  
                do{ ++i; } while( i < j && mSrcArray[i-1] == mSrcArray[i] );  
  
                //避免重复  
                do{ --j; } while( i < j  && mSrcArray[j] == mSrcArray[j+1] );  
            }  
        }  
  
  
    }  
}  



class Solution {
public:    
    /**
     * @param numbers : Give an array numbers of n integer
     * @return : Find all unique triplets in the array which gives the sum of zero.
     */
     void my_sort(vector<int> &temp,int num1,int num2,int num3){
         temp.push_back(num1);
         temp.push_back(num2);
         temp.push_back(num3);
         for(int i=0;i<2;i++){
             for(int j=0;j<2-i;j++){
                 if(temp[j]>temp[j+1]){
                     int tem=temp[j];
                     temp[j]=temp[j+1];
                     temp[j+1]=tem;
                 }
             }
         }
     }
    vector<vector<int> > threeSum(vector<int> &nums) {
        // write your code here
        
        vector<vector<int> > result;
        vector<int> temp;
        if(nums.empty())
        return result;
        map<int,int> my_map;
        int num1,num2,num3;
       // map<int,int>::iterator it;
       // it=my_map.end();
        for(int i=0;i<nums.size();i++){
            if(my_map.find(nums[i])==my_map.end())
            my_map[nums[i]]=1;
            else
            my_map[nums[i]]++;
        }
        for(int i=0;i<nums.size()-1;i++){
            my_map[nums[i]]--;
            for(int j=i+1;j<nums.size();j++){
                my_map[nums[j]]--;
                int flag=0;
                if(my_map[-nums[i]-nums[j]]>0){
                    my_sort(temp,nums[i],nums[j],-nums[i]-nums[j]);
                    for(int k=0;k<result.size();k++){
                        for(int l=0;l<3;l++){
                        if(temp[l]==result[k][l]){
                        flag++;
                        }
                        }
                        if(flag==3)
                        break;
                        else
                        flag=0;
                    }
                    if(flag!=3)
                    result.push_back(temp);
                    temp.clear();
                }
                my_map[nums[j]]++;
            }
            my_map[nums[i]]++;
        }
        return result;
    }
};

如有问题请留言。

如有帮助请顶一个,你们的支持是我最大的动力。

文章均可以转载,但请注明文章链接,谢谢。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值