题目描述
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
算法设计
这题基本思路很暴力,就是用三个指针i, j, k遍历这个数组,找到相加为0的三元组即可,但是时间是关键。可能超时的情况有两种:
- nums中元素个数很多且基本不重复或重复较少
- nums中元素个数多但全都一样。这里又分为两种情况:元素全为0和全不为0。
对于第二种情况,只需要判断元素是否全都一样且是否为0即可。
第一种情况就复杂很多。我的做法是将题目转换为-(nums[i] + nums[j]) = nums[k],具体处理如下:
- 将数组由小到大排序。
- 以i=0, j=i+1, k=j+1这三个起点开始遍历数组。如果nums[i]>0则结束遍历。
- 在第二层循环中,如果nums[j] == nums[j-1]且nums[j]与nums[i]不相等,说明nums[i]+nums[j]之前已经判断过了,则可不再判断。
- 在最内层循环中,如果-(nums[i] + nums[j]) = nums[k],就将三元组放入一个set中,利用set来去重。如果nums[k]的绝对值大于-(nums[i]+nums[j])的绝对值,说明从k位置及以后的数的绝对值都会大于-(nums[i]+nums[j])的绝对值,没有必要再继续判断。
**算法效率:**用时4068ms,战胜0.99%的人orz。
C++代码
class Solution {
public:
static bool cmp(const int a, const int b){
return a<b;
}
vector<vector<int>> threeSum(vector<int>& nums) {
int len = nums.size();
vector<vector<int> > resv;
if(len < 1){
return resv;
}
int num_i[len], num_j[len], num_k[len];
set<vector<int> > res;
set<int> iplusjs;
for(int i=0;i<len;i++){
num_i[i] = nums[i];
}
bool same = true;
for(int i=1;i<len;i++){
if(num_i[0] != num_i[i]){
same = false;
break;
}
}
if(same){
if(num_i[0] == 0 && len>=3){
vector<int> t;
t.push_back(0);
t.push_back(0);
t.push_back(0);
resv.push_back(t);
}
return resv;
}
sort(num_i, num_i+len, cmp);
for(int i=0;i<len;i++){
if(num_i[i] > 0){
break;
}
for(int j=i+1;j<len;j++){
if(num_i[j] == num_i[j-1] && num_i[j] != num_i[i]){
continue;
}
int iplusj = num_i[i] + num_i[j];
for(int k=j+1;k<len;k++){
if(num_i[k] == -iplusj){
vector<int> tmp;
tmp.push_back(num_i[i]);
tmp.push_back(num_i[j]);
tmp.push_back(num_i[k]);
res.insert(tmp);
}
else if(abs(iplusj) < abs(num_i[k])){
break;
}
}
}
}
set<vector<int> >::iterator its = res.begin();
for(its;its!=res.end();its++){
resv.push_back(*its);
}
return resv;
}
};