15. 3Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
分析
题目需要找出所有的组合使得每个组合的和为0,可以将原数组排序后,然后遍历排序后的数组,遍历时先选定一个数,然后在这个数之后的序列中使用两端夹逼的方式寻找另外两个数,有点类似二分查找法。这里需要注意两个地方:
- 一个是如果当前遍历选定的数和前一次遍历选定的数相等时,就不需要继续寻找剩下的两个数了,如果继续寻找的话就有可能找到重复的组合,比如排序后的序列为:[-4,-1,-1,0,1,2],选定数为第一个
-1
,可以找到{[-1,-1,2],[-1,0,1]}
,然后选定下一个-1
时,如果继续向后寻找,则还可以找到{[-1,0,1]}
,这样就出现重复了; - 另一个需要注意的地方就是选定一个数并在后续数组里找到了另外两个数的组合,这种组合有可能在存在重复,比如排序后的序列为:
[-2,0,0,2,2]
,会出现两组[-2,0,2]
,这时需要考虑去重,去重方式有两种,一种是使用集合方式记录已经找到的集合,另一种方式是用夹逼的方式缩小下一步检索的范围。
源码
使用集合方式记录已经找到的集合,用时88ms
class Solution {
public:
// 先排序 然后先选取一个数 剩下两个数在剩下的数里使用两边夹逼的方式寻找
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
if(nums.size() < 3) return ret;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size() - 2; i++) {
// 当前数和前一个数相等时,不用再进行检查,否则会出现重复组合
if(i > 0 && nums[i - 1] == nums[i]) continue;
int target = 0 - nums[i];
int j = i + 1, k = nums.size() - 1;
set<int> visited; // 用于记录寻找到的two sum组合
while(j < k) {
int sum = nums[j] + nums[k];
if(target == sum && (visited.find(nums[j]) == visited.end() || visited.find(nums[k]) == visited.end()) ) {
//标记已寻找到的two sum 组合
visited.insert(nums[j]);
visited.insert(nums[k]);
vector<int> subRet;
subRet.push_back(nums[i]);
subRet.push_back(nums[j]);
subRet.push_back(nums[k]);
ret.push_back(subRet);
j++;
k--;
} else if(target < sum) {
k--;
} else {
j++;
}
}
}
return ret;
}
}
使用夹逼方式去重,用时64ms
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
if(nums.size() < 3) return ret;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size() - 2; i++) {
// 当前数和前一个数相等时,不用再进行检查,否则会出现重复组合
// 比如[-4,-1,-1,0,1,2]
if(i > 0 && nums[i - 1] == nums[i]) continue;
int target = 0 - nums[i];
int j = i + 1, k = nums.size() - 1;
while(j < k) {
int sum = nums[j] + nums[k];
if(target == sum ) {
vector<int> subRet;
subRet.push_back(nums[i]);
subRet.push_back(nums[j]);
subRet.push_back(nums[k]);
ret.push_back(subRet);
// 用于去重后面和当前相同的数 比如 [-2,0,0,2,2]
while(j < k && nums[j] == nums[j+1]) j++;
while(j < k && nums[k-1] == nums[k]) k--;
j++;
k--;
} else if(target < sum) {
k--;
} else {
j++;
}
}
}
return ret;
}
}