https://leetcode.com/problems/3sum/
https://leetcode.com/problems/3sum-closest/
https://leetcode.com/problems/4sum/
首先说2sum, 这个以前以为只有hash可以做,但是这种做法的问题在于,对于数的范围小或者整数可以,如果是double,那么考虑double的位数,比如3位小数,那么所有的数都乘以1000然后按照整数hash最后还原即可,但是这样容易爆掉啊,比如double精确到小数点5位,来1e7个数,所有数的和加起来,,恩 可能用long long安全些,并且空间开销也很大。。。
然而后来发现,原来也可以线性扫描做。 《编程之美》2.12有的,于是这几个题都是这个变形。 如果是k-sum 可以O(n^(k-1))搞定。之前有人说 4sum可以hash更高效,然而我觉得思路有问题。比如 http://www.2cto.com/kf/201503/383807.html
其实生成pair之后,pair的个数已经是n^2个了,然后即使不考虑你在判重上的问题,只是把这n^2个pair对线性扫描,时间已经是n^2*n^2了,因为这里所谓的线性扫描 针对的是所有的n^2个pair啊
关于3-sum 做法的证明,看这里http://blog.csdn.net/zhz1993622/article/details/44422017,然而没看懂 而且找了很多也没找到合理的证明过程。 正在看算法导论 以后再证明
下面帖3-sum代码:
3-sum时间卡的挺紧的,
if(i>0 && nums[i-1] == nums[i])continue;
这一行如果不加 会超时,然而这一行代码在4sum产生了新的坑
class Solution {
public:
inline void add(vector < vector<int> >& ret, int a, int b, int c) {
if(ret.size() != 0) {
if( ret[ ret.size()-1 ][0] == a &&
ret[ ret.size()-1 ][1] == b &&
ret[ ret.size()-1 ][2] == c ) return;
}
vector <int> nums;
nums.push_back(a);
nums.push_back(b);
nums.push_back(c);
ret.push_back(nums);
}
vector<vector<int> > threeSum(vector<int>& nums) {
vector < vector<int> > ret;
if(nums.size() < 3)return ret;
sort(nums.begin(), nums.end());
for(int i=0;i<nums.size()-2;i++) {
if(i>0 && nums[i-1] == nums[i])continue;
int j=i+1,k=nums.size()-1;
while(j < k) {
int sum = nums[i] + nums[j] + nums[k];
if(sum == 0) {
add(ret, nums[i], nums[j], nums[k] );
while(j<k && nums[j+1] == nums[j])j++;
while(j<k && nums[k-1] == nums[k])k--;
j++;
k--;
}
if(sum < 0) j++;
if(sum > 0) k--;
}
}
return ret;
}
};
3sum-closest
class Solution {
public:
inline int Abs(int x) {
return (x>0) ? x : -x ;
}
bool bigger(int x, int y) {
return Abs(x)>=Abs(y);
}
int threeSumClosest(vector <int> & nums, int target) {
int ret = 0, dis = 200000000;
if(nums.size() < 3) {
for(int i=0; i<nums.size(); i++) {
ret += nums[i];
}
return ret;
}
sort(nums.begin(), nums.end());
int sum =0;
for(int i=0; i<nums.size()-2; i++) {
if(i && nums[i]==nums[i-1])continue;
int l = i+1, r = nums.size()-1;
while(l < r) {
sum = nums[l] + nums[r] + nums[i];
int tmpdis = sum - target;
//dis = min(Abs(tmpdis), dis);
if(dis > Abs(tmpdis)) {
ret = sum;
dis = Abs(tmpdis);
//cout << nums[i] << nums[l] << nums[r] << endl;
}
if(tmpdis == 0) return target;
else {
if(tmpdis < 0)l++;
else r--;
}
}
}
return ret;
}
};
4-sum
class Solution {
public:
inline void add(vector< vector<int> >& ret, int a, int b, int c, int d) {
if(ret.size() != 0) {
if(ret[ ret.size()-1 ][0] == a &&
ret[ ret.size()-1 ][1] == b &&
ret[ ret.size()-1 ][2] == c &&
ret[ ret.size()-1 ][3] == d) return ;
}
vector<int> nums;
nums.push_back(a);
nums.push_back(b);
nums.push_back(c);
nums.push_back(d);
ret.push_back(nums);
}
vector< vector<int> > fourSum(vector<int>& nums, int target) {
vector < vector<int> > ret;
if(nums.size() < 4) return ret;
sort(nums.begin(), nums.end());
for(int i=0;i<nums.size()-3;i++) {
if(i>0 && nums[i] == nums[i-1])continue;
bool flag=false;
for(int j=i+1;j<nums.size()-2; j++) {
if(flag && nums[j] == nums[j-1])continue;
int l=j+1,r=nums.size()-1;
while( l < r ) {
int sum = nums[i] + nums[j] + nums[l] + nums[r];
if(sum == target) {
add(ret, nums[i], nums[j], nums[l], nums[r]);
flag = true;
while(l < r && nums[l+1]==nums[l] )l++;
while(l<r && nums[r-1]==nums[r])r--;
l++;
r--;
} else if( sum < target ) {
l++;
} else {
r--;
}
}
}
}
return ret;
}
};
这里面有个坑,
if( nums[j] == nums[j-1])continue;
这样的判重方式在什么情况下是合理的? 答案:target=0的时候
因为有这样的数据 比如 输入{1,1,1,1} target=4, 那么如果这么判断就没有答案,因为直接num[j] == num[j-1]一直成立,导致第二个指针直接把数都扫完。我的解决方法:只有在得到一个解的时候,才在某个元素重复的时候直接跳过。也就是上面flag的作用