一、问题描述
Given an array nums
of n integers, are there elements a, b, c in nums
such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
二、解题思路
3 Sum问题可以转化为2 Sum问题(类似4 Sum问题可以转化为3 Sum)。这类问题最优解的时间复杂度为O(n*(k-1))。即:2 Sum最优复杂度为O(n),3 Sum最优为O(n * n)。
对于2 Sum有两种解题思路:
①先排序,然后用left和right分别指向开头和结尾,之后计算和。同时根据情况移动即可。感觉这种方法像是快排。时间复杂度是:O(NlogN);
②就是利用unordered_map(底层为哈希表),查找一个数为线性时间,这样的话一次遍历即可找到,所以复杂度就降低为:O(n)。
因此,对于3 Sum也有两种方法。
基于①:首先要进行一次排序,然后就转为2 Sum问题,复杂度:O(N*NlogN)
基于②:先进行一次排序,然后也是2 Sum问题,复杂度:O(N * N).
三、代码实现(C++)
基于①:首先要进行一次排序,然后就转为2 Sum问题,复杂度:O(N*NlogN)
#include <vector>
#include <algorithm>
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
if(nums.size() < 3){
return result;
}
sort(nums.begin(),nums.end());
//因为题目中要求和为0,而且此时已经排过序,因此第一个元素必须要<0
for(int i=0; i<nums.size() && nums[i]<=0; i++){
int target = -nums[i];
int left = i+1;
int right = nums.size()-1;
while(left < right){
int sum = nums[left] + nums[right];
if(sum < target){
left++;
} else if(sum > target){
right--;
} else{
result.push_back({nums[i],nums[left],nums[right]});
//防止把相同的第二个/第三个数放进去
while( left<right && (nums[left] == nums[left+1])) left++;
while( left<right && (nums[right] == nums[right-1])) right--;
//找到满足的数后,下标要变化
left++;
right--;
}
}
//第一次写时这里用nums[i++] == nums[i],得出的结果不对。原因就是忽略了++特性
//这里i++,后面由来了个i++,因此导致得不出正确的结果
while( (i+1)<nums.size() && nums[i+1] == nums[i] ) i++;
}
return result;
}
};
基于②:先进行一次排序,然后也是2 Sum问题,复杂度:O(N * N).
#include <unordered_map>
#include <algorithm>
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
unordered_map<int, int> map;
if(nums.size() < 3){
return result;
}
sort(nums.begin(),nums.end());
for(int i=0; i<(nums.size()-1) && nums[i]<=0; i++){
int target = -nums[i];//从下面开始就是2 Sum的问题了
for(int j=(i+1); j<nums.size(); j++){
int other = target - nums[j];
if(map.find(other) != map.end()){
//加这个判断是防止[-1,0,1],[-1,1,0]情况,因为vector是有序的
if(nums[j]<=nums[map[other]]){
result.push_back({nums[i],nums[j],nums[map[other]]});
}
}else {
//去掉重复的
if(map.find(nums[j]) == map.end()) map[nums[j]] = j;
}
}
//防止第一个数据重复,即:[-1,-1,2],[-1,-1,2].因为样例可能是[-1,0,1,-1,-1,2]
while( (i+1)<nums.size() && nums[i+1] == nums[i] ) i++;
}
return result;
}
};
说明,对于这个方法。有个测试用例一直不过:
Input:[-1,0,1]
Output:[]
Expected:[[-1,0,1]]
我也一步一步的跟下去,发现是这样的:初始当i=0,j=1时,此时map为空,因此找不到1.所以,将0添加到map中(此时map为<0,1>);当i=0,j=2时,本来可以用map中拿到0,但是我在代码21行加了判断:if(nums[j]<=nums[map[other]]),因此导致找不到。这也是个遗留问题吧。希望后面可以解决!!!