题目要求:
给定一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
C++代码1:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();++i){
if(i>0 && nums[i-1]==nums[i]){
continue; //skip duplicate
}
int j=i+1,k=nums.size()-1;
while(j<k){
int sum=nums[i]+nums[j]+nums[k];
if(sum<0){
++j;
}
else if(sum>0){
--k;
}
else{
vector<int> temp_result(3,nums[i]);
temp_result[1]=nums[j];
temp_result[2]=nums[k];
if(!count(result.begin(),result.end(),temp_result)){
result.push_back(temp_result);
}
j++;
k--;
}
}
}
return result;
}
};
结果(超出时间限制):
解析:
首先对nums数组里的数字先排序
sort(nums.begin(),nums.end());
对输入的数字进行遍历获得第一个数
for(int i=0;i<nums.size();++i)
然后在计算后面两数的和的时候可以设两个迭代器
一个从该数的后一个数开始(j=i+1),一另个从最后一个数开始(k=nums.size()-1)
如果加和小了就移动第一个迭代器
if(sum<0){
++j;
}
如果加和大了就移动第二个迭代器
else if(sum>0){
--k;
}
就可以遍历所有情况了,遇到sum=0的就保存起来
不过最终超出时间限制!!!
需要改进!
C++代码2:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
if(nums.size()<=2)return result;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size() - 2; i++) {
int a = nums[i];
if(a > 0) break;
if (i > 0 && a == nums[i - 1]) continue;
for (long j = i + 1, k = nums.size() - 1; j < k;) {
int b = nums[j];
int c = nums[k];
int value = a + b + c;
if (value == 0) {
result.push_back(vector<int>({a, b, c}));
while (j<k && b == nums[++j]);
while (j < k &&c == nums[--k]);
} else if (value > 0) {
k--;
} else {
j++;
}
}
}
return result;
}
};
结果:
解析:
其实大致思路还是跟代码1差不多,只不过数字整个都是排序的,如果有重复数字肯定是相邻的,我们在处理完前一个数之后,如果后一个数与之相等就删掉,这样避免了重复情况的出现
即多了
if (i > 0 && a == nums[i - 1]) continue;
以及
while (j<k && b == nums[++j]);
while (j < k &&c == nums[--k]);
详细解释如下:
首先定义一个双层的int类型的vector容器装结果result
vector<vector<int>> result;
然后判断,因为我们是求三数之和,如果nums里的元素小于等于2的话,就直接返回result,因为当前的result为空
if(nums.size()<=2)return result;
然后就是先对元素排序,目的就是我们在固定好第一个元素后,第二个元素取该数的后一个数,第三个元素从最后一个数开始,它们的和如果大于(0-第一个元素)的结果,那么第三个元素就减小,如果小于(0-第一个元素)的结果,那么第二个元素就增大
sort(nums.begin(), nums.end());
然后对于从第一个元素开始,逐渐遍历每一个元素,注意,因为需要判断三数之和,所以最后一个需要判断的数字是nums.size() - 2
for (int i = 0; i < nums.size() - 2; i++)
把第一个元素的值赋给变量a
int a = nums[i];
因为已经排好序了,所以从左边开始,第一个数肯定是当前nums里最小的了,如果它的值都大于0了,那么nums里就不可能存在三数之和刚好等于0,所以直接break退出循环
if(a > 0) break;
然后就是做一个判断,如果有重复数字肯定是相邻的,我们在处理完前一个数之后,如果后一个数与之相等就删掉,这样避免了重复情况的出现
if (i > 0 && a == nums[i - 1]) continue;
然后再进行一个for循环,j也就是第二个元素的初始值,去第一个元素的下一位元素,而k是第三个元素的初始值,取最后一个元素
for (long j = i + 1, k = nums.size() - 1; j < k;)
把它们的值分别赋给b和c
int b = nums[j];
int c = nums[k];
然后求当前三数之和value
int value = a + b + c;
如果刚好三数之和value等于0了,那么就把a,b,c做成一个vector,然后push_back进result里
然后只要j的值和它右边的值相同,或者k的值和它左边的值相同,那就是重复的可能性了,这时,直接不进行任何操作
if (value == 0) {
result.push_back(vector<int>({a, b, c}));
while (j<k && b == nums[++j]);
while (j < k &&c == nums[--k]);
}
如果三数之和value大于0,那么我们就把第三个元素k减小,也就是向左移动一位,k--
else if (value > 0) {
k--;
}
如果三数之和value小于0了,那么我们就把第二个元素j增大,也就是向右移动一位,j++
else {
j++;
}
最后 return result; 即可