leetcode15 3Sum leetcode16 3SumClosest

两道题大同小异,15题是从一个数组中找到三个数(一个triplet)的和为0,找到所有和为0的triplet,返回一个vector<vector<int>>。这道题我试过brute force是超时的。

我最开始的思路是这样的:

设数组nums[0]  nums[1] ......  nums[n-2]  nums[n-1]  首先对数组进行排序操作。

在nums[0]~nums[n-1]中寻找triplet:

寻找包含nums[0]和nums[1]的triplet,即在nums[1]和nums[n-2]当中二分查找是否有等于0-nums[0]-nums[1]的元素。

接着,查找不含nums[0]的triplet,即递归地在nums[1]~nums[n-1]中寻找triplet。

接下来,查找不含nums[n-1]的triplet,即递归地在nums[0]~nums[n-2]中寻找triplet

最后,查找既不含nums[0]也不含nums[n-1]的triplet,即递归地在nums[1]~nums[n-2]中寻找triplet。

设一个二维数组table,talbe[i][j]=true表示已经完成了在nums[i]~nums[j]搜索triplet的操作,如果又重复调用到在nums[i]~nums[j]搜索triplet,直接返回不进行重复计算。

把所有求出来的triplet放在一个set中,最后遍历set返回结果。

class Solution {
public:
    bool **table;
    vector<vector<int>> ret;
    set<vector<int>> result_set;
    vector<vector<int>> threeSum(vector<int>& nums) {
      init(nums.size());
      ret.clear();
      result_set.clear();

      if(nums.size()<3) return ret;

      sort(nums.begin(),nums.end());
      threeSum(nums,0,nums.size()-1);
      for(set<vector<int>>::iterator it=result_set.begin();it!=result_set.end();it++){
        ret.push_back(*it);
      }
      finish(nums.size());
      return ret;
    }
    void init(int nums_size){
      table=new bool*[nums_size];
      for(int i=0;i<nums_size;++i){
        table[i]=new bool[nums_size];
        for(int j=0;j<nums_size;++j) table[i][j]=false;
      }
    }
    void finish(int nums_size){
      for(int i=0;i<nums_size;++i){
        delete []table[i];
      }
      delete []table;
    }

    void threeSum(vector<int>& nums,int l,int r);
    int binarySearch(vector<int>& nums,int l,int r,int target);
};

//nums[i]~nums[j]之间查找是否有triplet的和为3
void Solution::threeSum(vector<int>& nums,int l,int r){
  //cout<<"l="<<l<<"  r="<<r<<endl;
  if(r-l<2 || r<0 || l>=nums.size()) return;
  if(table[l][r]==true) return;

  int result=binarySearch(nums,l+1,r-1,0-nums[l]-nums[r]);
  if(result!=-1){
    vector<int> t{nums[l],nums[result],nums[r]};
    result_set.insert(t);
  }

  int _left,_right;
  _left=l;
  while(_left<nums.size()-1 && nums[_left+1]==nums[_left]) ++_left;
  ++_left;
  threeSum(nums,_left,r);

  _right=r;
  while(_right>0 && nums[_right-1]==nums[_right]) --_right;
  --_right;
  threeSum(nums,l,_right);

  threeSum(nums,_left,_right);

  table[l][r]=true;

}
int Solution::binarySearch(vector<int>& nums,int l,int r,int target){
  int _left=l,_right=r;
  int mid;
  while(_left<=_right){
    mid=(_left+_right)/2;
    if(nums[mid]>target) _right=mid-1;
    else if(nums[mid]<target) _left=mid+1;
    else break;
  }
  if(_left<=_right) return mid;
  else return -1;
}

这个运行出来用了600ms,具体的时间复杂度的推到我推导不出来,基本是通过一棵三叉树的叶子节点,指数级别的时间复杂度。实在是太慢了。。。

后来看了另外一个大神的解答,瞬间思路清晰了很多(n.^2的时间复杂度):

[-1, 0, 1, 2, -1, -4]

为例:

首先,对数组进行排序:

[-4, -1, -1, 0, 1, 2]

首先i=-4,寻找包含-4的triplet,在-1~2之间寻找和为4的元素,设置两个指针l=-1,r=2

由于-1+2<4所以l向右移动,l=0

0+2依然小于4,l向右移动l=1

由于1+2依然小于4,所以l向右移动,但此时l已经在r的左边,所以包含-4的triplet寻找完毕,接下来寻找不含-4的triplet。

寻找包含-1的triplet,步骤同上。

注意过滤相同的元素,比如上例有两个-1,寻找完包含-1的triplet以后,直接跳过第二个-1,直接寻找包含0的triplet。

这次运行时间直接降低到110ms:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
		vector<vector<int>> ret;
        if(nums.size()==0) return ret;
		for(int i=0;i<(int)nums.size();){
			
			//cout<<"i="<<i<<endl;
			
			int target=0-nums[i];
			int l=i+1,r=nums.size()-1;

			while(l<(int)nums.size() && r>=0 && l<r){
				
				//cout<<"l="<<l<<"  r="<<r<<endl;
				
				if(nums[l]+nums[r]<target) ++l;
				else if(nums[l]+nums[r]>target) --r;
				else{
					vector<int> tmp={nums[i],nums[l],nums[r]};
					ret.push_back(tmp);
					
					//cout<<"和为0"<<endl;
					
					//去重操作
					int tmp1=nums[l];
					while(l<(int)nums.size() && nums[l]==tmp1) ++l;
					tmp1=nums[r];
					while(r>=0 && nums[r]==tmp1) --r;
				}
			}
			
			//找到了包含nums[i]的triplet,对nums[i]进行去重操作
			int tmp1=nums[i];
			while(i<(int)nums.size() && nums[i]==tmp1) ++i;
		}
		return ret;
    }
};

第16题类似,查找一个triplet使得这个triplet的和最接近target,相同的思路:

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
		if(nums.size()==0) return 0;
		
        sort(nums.begin(),nums.end());

        //cout<<"nums:"<<endl;
        //for(auto it=nums.begin();it!=nums.end();++it) cout<<*it<<" ";
        //cout<<endl;
        //cout<<"target="<<target<<endl;

        int ret=(*nums.begin())+*(nums.begin()+1)+*(nums.begin()+2);
        for(auto it=nums.begin();it!=nums.end();){
            //cout<<"it:"<<it-nums.begin()<<endl;

            auto l_it=it+1;
            auto r_it=nums.end()-1;
            while(l_it<r_it){
                //cout<<"l_it:"<<l_it-nums.begin()<<" r_it:"<<r_it-nums.begin()<<endl;
                //cout<<"*l_it="<<*l_it<<" *r_it="<<*r_it<<endl;

                int target2=target-*it;
                int tmp=*it+*l_it+*r_it;
				
				
                if( abs(tmp-target)< abs(ret-target) ) ret=tmp;

                if(*l_it+*r_it<target2){
                    int tmp2=*l_it;
                    while(l_it!=nums.end() && *l_it==tmp2) ++l_it;
                }
                else if(*l_it+*r_it>target2){
                    int tmp2=*r_it;
                    while(r_it>=nums.begin() && *r_it==tmp2) --r_it;
                }
                else return tmp;
            }
            int tmp3=*it;
            while(it!=nums.end() && *it==tmp3) ++it;
        }
        return ret;
    }
};

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值