题目描述:
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: The solution set must not contain duplicate triplets.
解决思路:
方法一:暴力解决,写三个循环。好傻……时间复杂度是O(N^3)
方法二:之前有一个2sum的题,是给定一个和以及一个数组,让从数组里头找到两个数字,使其和为给定的和。那道题用了hash,直接把时间复杂度降到了O(N)。所以我们想一想这道题其实也可以用到hash。
将数组放到hash表中,因为本题还要求输出结果不要有重复的三元组,所以我们可以利用hash表去重:在放入每一个数组元素前判断一下该数组元素是否在hash表中存在(O(N)),不存在则放入,存在则跳过这个元素判断下一个数组元素。
在hash表中依次遍历,从第一个元素开始,遍历之后的元素中是否有两个元素的和为该元素的相反数(O(N)),因为要遍历一遍hash表,所以是在O(N)的情况下继续O(N),即总体的时间复杂度为O(N^2)
方法三:不用hash
我们先对数组进行排序(没有思路就排序……从小到大排序),为O(NlogN)的时间复杂度。还是要先从第一个元素开始作最外层循环,之后在它之后的元素中找到两个元素的和为这个元素的相反数(设为a)。
这里我们就可以利用到排序后的有序数组了,使用两个指针分别指向剩下的元素的两端,如果指针指向的两个数之和小于a,则把左边的指针向右移;若指针指向的两个数之和大于a,则把右边的指针向左移。这样利用了排序后的数组带来的便利性,即如果两数之和小于a,则要把数据往大了找,但同时为了不遗漏一定要按照一定规律地找,所以这个排序和动一个数就保证了有规律,往大了找代表这较小的元素太小所以它需要往大了调,所以左边的指针右移。这样我们可以在O(N)找出剩下的元素中的两数和为a的元素为哪些。为了去重也要作一些操作在移动指针的时候。
所以在最外层循环O(N)里头又有O(N),故而总的时间复杂度为O(N^2)
代码:
class Solution {
public:
/**
* @param numbers : Give an array numbers of n integer
* @return : Find all unique triplets in the array which gives the sum of zero.
*/
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] == nums[i - 1]) {
continue;
}
// two sum;
int start = i + 1, end = nums.size() - 1;
int target = -nums[i];
while (start < end) {
if (start > i + 1 && nums[start - 1] == nums[start]) {
start++;
continue;
}
if (nums[start] + nums[end] < target) {
start++;
} else if (nums[start] + nums[end] > target) {
end--;
} else {
vector<int> triple;
triple.push_back(nums[i]);
triple.push_back(nums[start]);
triple.push_back(nums[end]);
result.push_back(triple);
start++;
}
}
}
return result;
}
};
这里推荐一下ksum问题的总结式的解决思路: