题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路
时间复杂度O(N^2), 空间复杂度O(1)
1. 先对数组排序,便于判断重复元素O(NlogN)
2. 一层遍历O(N):对于每一个nums[i],在[i+1,n-1]范围内找出包含自己的不重复的三元组。
- 具体来说,就是双指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集。
- 如果不满足,就根据sum比0大还是比0小,来判断左指针右移还是右指针左移。这个过程是O(N)。
- 所以总的来说时间复杂度O(N^2)。
3. 遍历过程中为了保证三元组的唯一性,在找到一组合适的三元组后,对于重复值,要加快指针的移动:
- (1)nums[i]如果和nums[i-1]相等,则忽略;
- (2)sum为0时,如果nums[L]==nums[L+1],则后面的都要跳过;
- (3)sum为0时,如果nums[R]==nums[R-1],则后面的都要跳过。
4. 为了优化遍历,当 nums[i]大于 0,则三数之和必然大于 0,可以直接结束循环
class Solution {
public:
/*
错误超时思路:两层循环遍历数组,将两两组合放入哈希表中,key为两数之和,value为二维数组,里面放的都是和为key的序号对;再来一次遍历数组,寻求刚好满足的第三者。需要注意的是,把符合条件的三元组放进vector中,然后在去去重,这样是非常费时的,很容易超时。
时间复杂度O(N^2),空间复杂度O(N)。
*/
/*
换一种思路。
1、先对数组排序,便于判断重复元素O(NlogN)
2、一层遍历O(N):对于每一个nums[i],在[i+1,n-1]范围内找出包含自己的不重复的三元组。
具体来说,就是双指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集。如果不满足,就根据sum比0大还是比0小,来判断左指针右移还是右指针左移。这个过程是O(N)。所以总的来说时间复杂度O(N^2)。
3、遍历过程中为了保证三元组的唯一性,在找到一组合适的三元组后,对于重复值,要加快指针的移动:(1)nums[i]如果和nums[i-1]相等,则忽略;(2)sum为0时,如果nums[L]==nums[L+1],则后面的都要跳过;(3)sum为0时,如果nums[R]==nums[R-1],则后面的都要跳过。
4、为了优化遍历,当 nums[i]大于 0,则三数之和必然大于 0,可以直接结束循环
*/
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int> > anws;
if( nums.size() <= 2 ){
return anws;
}
//先对数组排序,便于判断重复元素O(NlogN)
sort( nums.begin(), nums.end() );
if( nums[0] > 0 ){ //全是正数肯定无解
return anws;
}
//对于每一个nums[i],在[i+1,n-1]范围内找出包含自己的不重复的三元组
int l,r,sum;
for(int i=0; i<nums.size(); i++){
//优化遍历,可直接结束
if( nums[i] > 0 ){
break;
}
//左右双指针遍历[i+1,n-1]
l = i+1, r = nums.size()-1, sum;
while( l < r ){
sum = nums[i] + nums[l] + nums[r];
if( sum == 0 ){
anws.push_back({nums[i], nums[l], nums[r]});
//三元组不能重复,保留最后一次符合的情况。注意l,r相邻的情况不用跳。
while( l<r && nums[l]==nums[l+1] ){
l++;
}
if( l<r && nums[r]==nums[r-1] ){
r--;
}
//这里不能丢啊!前面那是去重操作,这个是一定要有的正常指针移动操作
l++;
r--;
}else if( sum < 0 ){//左指针右移
l++;
}else{ // >0则右指针左移动
r--;
}
}
//三元组不能重复。避免nums[i]作为第一个数重复出现
while(i+1<nums.size() && nums[i] == nums[i+1])
i++;
}
return anws;
}
};