两道题大同小异,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;
}
};