前言
3Sum算是LeetCode最经典的十几道题之一了,据说在面试中出现的频率相当高。所以在这里花点篇幅讨论一下此题。
题目
https://leetcode.com/problems/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)
题目大意就是在给定的数组中找出三个数字,使得它们的和为零。把所有满足此条件且不重样的一组数字塞进vector< vector >里并返回之。
分析
这道题算是当年那个TwoSum的进阶版,关于TwoSum这道题,我已经写了一篇博文,其中有这么一句话,
没有多想,这种与数组的某个特征值相关的问题,先排个序总是没错的。可以先从小到大sort一下,然后给扔两个指针到数组头尾,开始寻找满足要求的值。
这种排序+双指针的思路,可以说是屡试不爽,对于此题亦然。
我们可以先把给定的数组sort一下,然后先进入一层index循环,由于题目要求三个数的和为零,所以我们可以设置一个目标值target,使之等于最外层遍历的元素的相反数,即-nums[i],然后此题就转化为了一个在剩下的元素中寻找两个数的和等于target的TwoSum问题。
这里还需要注意两点:
- 题目要求找出所有满足条件的unique的三个数的组合,所以我们需要设置过滤机制。
- 当target值本身小于零的时候,即nums[i]是一个大于零的数。由于数组已经是从小到大排序了的,对于这种情况,nums[i]之后的元素都会比零大,此时显然无法组成一个和为零的组合。
具体见代码。
代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
std::sort(nums.begin(),nums.end());
int n = nums.size();
for (int i = 0; i < n; i++) {
int target = -nums[i], front = i + 1, back = n - 1;
if (target < 0) break;
while (front < back) {
int sum = nums[front] + nums[back];
if (sum < target) front++;
else if (sum > target) back--;
else {//已经找到了一组答案
vector<int> tri(3,0);
tri[0] = nums[i], tri[1] = nums[front], tri[2] = nums[back];
ans.push_back(tri);//将这组数字放进答案
while (front < back && nums[front] == tri[1]) front++;//寻找下一个不同的数字
while (front < back && nums[back] == tri[2]) back--;//寻找下一个不同的数字
}
}
while (i + 1 < n && i + 2 < n && nums[i+1] == nums[i])
i++;//寻找下一个不同的数字
}
return ans;
}
};