题目描述
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/1fGaJU 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
首先是暴力求解法,但效率肯定低,有没有什么办法更好呢?
其实最关键的在于,如何搜索判断不重复?
方法一:
我设立 A B C 三个数在滑动
A从1 B从2 C从3
AB固定,C滑动搜索,
C搜索完一组,B向后滑动一位,C顺位向后搜索
然后就是A向后滑动
这种有剪枝的搜索判断
方法二:
但是实际上感觉还有可以优化的空间,因为你在前面搜索的时候,就其实已经遇到了所有的数字,
那么,能不能在搜索的过程中,利用哈希表的思想,保存已出现的数字和个数,一旦发现缺哪个数字,就去找一下这个数,如果这个数字位置不重复,那么就找到了一组
先遍历一遍,统计出现的数字和出现的次数
然后问题转换为,我取出一个数,去看剩下的组合里是否能出现相加和为 -a的数的组合
其他人的思路:
先排序一遍,保证数组有序
然后后面的思路和我的差不多
代码实现:
方法一:排序后暴力求解
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int len = nums.size();
vector<vector<int>> ans;
if(len<=2) return ans;
sort(nums.begin(),nums.end());
for(int a=0;a<len-2;a++){
if(a>0&&nums[a]==nums[a-1]) continue;
int A = nums[a];
//去寻找剩下的里面有没有和为-A的两个数
for(int b=a+1;b<len-1;b++){
if(b>a+1&&nums[b]==nums[b-1]) continue;
for(int c = b+1;c<len;c++){
if(c>b+1&&nums[c]==nums[c-1]) continue;
if(nums[c]+nums[b]==-A){
vector<int> tmp_ans;
tmp_ans.push_back(nums[a]);
tmp_ans.push_back(nums[b]);
tmp_ans.push_back(nums[c]);
ans.push_back(tmp_ans);
}
}
}
}
return ans;
}
};
方法二:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
if (nums.size() < 3) return {};
vector<vector<int>> res;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size() - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
// 在 i + 1 ... nums.length - 1 中查找相加等于 -nums[i] 的两个数
int target = - nums[i];
int left = i + 1, right = nums.size() - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
res.push_back({nums[i], nums[left], nums[right]});
/*
下面的代码相当于:
while (left < right) {
// 不管前后相不相等,left 都要往前走
left++;
if (nums[left - 1] != nums[left]) break;
}
while (left < right) {
// 不管前后相不相等,right 都要往后走
right--;
if (nums[right + 1] != nums[right]) break;
}
*/
// 去重
while (left < right && nums[left] == nums[++left]);
while (left < right && nums[right] == nums[--right]);
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
return res;
}
};
复杂度分析
时间:排序O(nlogn)+双指针查找O(n)
空间:O(n)