题目链接:https://leetcode.com/problems/3sum/#/description
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.
For example, given array S = [-1, 0, 1, 2, -1, -4], A solution set is: [ [-1, 0, 1], [-1, -1, 2] ]这题目挺有意思,你要注意到的是例解里可以拼凑出两个[-1,0,1],但是题目要求的是unique triplets,也就是说你要处理重复的解。
处理重复的方法不难,只要sort一下数组,你就可以根据index移动前后的值判断是不是重复解。麻烦点的方法,可以把求出来的所有解扔到set里去重,这种方法省脑但是费时费空间。
难点在于使用三层for循环会超时。看到abc三个元素,自然就想到用三个index去代表它们遍历数组,然而残酷的事实告诉我:不是每个index都配拥有一个for循环。
解题思路:
首先判断读入的数组大小是否够大,如果连两个元素都没有,就不用继续跑的,根本没有满足解。
然后才是正式的流程,对于i,使用一个for循环,但是对于j和k,使用的是从数组两头往中间靠拢的方法,这样算法的时间复杂度就从O(n^3)降到了O(n^2)。
由于是我们已经排过序的有序数组,所以可以根据三个元素的值的和的情况判断index的移动情况:
我们把三个元素按序命名为i,front,back
为了分析情况,我们假设front和back在a,b,c,d,e五个数中移动,其中i+b+d = 0
最开始front 指向a,back指向e,此时若
1) i+a+e<0,则front向右移动,指向b,此时i+b+e>0,back左移指向d,sum = 0,得到结果i,b,d
2) i+a+e>0,则back向左移动,指向d,此时i+a+d<0,front右移指向b,sum = 0,得到结果i,b,d
AC代码:
class Solution {
public:
vector<vector<int> > threeSum(vector<int>& nums) {
if(nums.size() <=2) return {};
vector<vector<int> > ans;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size() - 2;)
{
int front = i + 1, back = nums.size() - 1;
while (front < back)
{
int tmp_ans = nums[i] + nums[front] + nums[back];
if (tmp_ans == 0)
{
vector<int> tuple ;
tuple.push_back(nums[i]);
tuple.push_back(nums[front]);
tuple.push_back(nums[back]);
ans.push_back(tuple);
front ++;
back --;
while (front < back && nums[front - 1] == nums[front]) front ++;
while (front < back && nums[back + 1] == nums[back]) back --;
}
else if (tmp_ans < 0)
{
front ++;
while (front < back && nums[front - 1] == nums[front]) front ++;
}
else
{
back --;
while (front < back && nums[back + 1] == nums[back]) back --;
}
}
i ++;
while((i < nums.size()) && nums[i] == nums[i-1]) i++;
}
return ans;
}
};