思路:
把3Sum问题降到n个2Sum子问题, 我们能通过O(n)的时间解决2Sum问题, 所以3Sum就是O(n^2)的时间复杂度.
首先将数据排序. 然后, 进入第一层循环, 让每个元素都先尝试做首元素. 因为排好序了, 就直接考虑后面的元素就好了. 下面将剩下的元素进行2Sum寻找. 我们用的方法是首尾各拿一个指针跟踪, 如果和第一层循环中的首元素的和加起来得0, 就放到结果组中. 接下来要注意要跳过和本次循环相同的元素, 否则会有重复的结果. 还有一些细节放到注释中了.
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
if (nums.size() < 3) return res; // 如果数据少于三个, 直接返回空数组
sort(nums.begin(), nums.end()); // 排序数组
for (int i = 0; i < nums.size() - 2; i++) {
if (i && nums[i] == nums[i - 1]) continue; // 节省时间, 同样的元素只做一次首元素
vector<int> triplet;
int head = i + 1, tail = nums.size() - 1; // 因为排序了, 只需要考虑后面的元素
int target = -nums[i];
while (head < tail) {
// 算是一种剪枝, 如果现在头两个元素已经大于目标值了, 就不用再考虑后面的情况了
if (nums[i] + nums[head] > 0) break;
if (nums[head] + nums[tail] == target) {
triplet.push_back(nums[i]);
triplet.push_back(nums[head]);
triplet.push_back(nums[tail]);
res.push_back(triplet);
triplet.clear();
// 下面两行都是跳过后面相同的元素, 为了避免重复结果
while (head < tail && nums[head] == nums[head + 1]) head++;
while (head < tail && nums[tail] == nums[tail - 1]) tail--;
head++, tail--;
}
else if (nums[head] + nums[tail] < target) head++;
else tail--;
}
}
return res;
}