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] ]
此题在Leetcode中的标签是Medium,但是试着做了以后发现需要考虑的边界情况不少。
求解思路:
1、整体的思路
求三个数的和为0可以先假设确定第一个数,再根据第一个数在数组中寻找符合条件的另外两个数,一旦找到了就将三个数保存到一个数组中。
2、确定第一个数环节
遍历数组,这里有个需要考虑到的情况是我们只需要从第一个数遍历到倒数第三个数即可,假定确定的第一个数为nums[i],注意:因为题目规定不允许重复的答案出现,所以需要判断每次确定的第一个数此前是否出现过,对此我们可以先将数组中数字排序,当然排序的好处不止这点,下文中还会提到。
3、如何寻找符合条件的另外两个数
暴力搜索确实可行,但因为此题并未规定输出需要按照原来数组中的顺序,所以我们可以先将数组中数字由小到大排序,再定义两个指针,分别指向nums[i+1]和nums[nums.size()-1],分别命名为左指针和右指针,注意:我们并不需要将指针指向第一个数,因为在nums[i]之前所有的情况已经在此次循环前搜索过了。
算法:
(1)如果两个数之和等于我们需要的值,就将此时的三个数记下,并将左指针右移、右指针左移,注意:因为题目规定不允许重复的答案出现,所以需要对当前指针所指的值和之前所指的值进行判断;
(2)否则,如果两个数之和小于我们需要的值,将左指针右移,因为之前已经将数组排序,所以可以确定下一个数肯定大于前一个数;
(3)如果两个数之和大于我们需要的值,则将右指针左移。
通过这种方法我们可以将时间复杂度由O(n^3)变成O(n^2)。
时间复杂度:O(n^2)
C++实现:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
for(int i=0;i+2<nums.size();++i)
{
if(i>0&&nums[i]==nums[i-1])
continue;
int target=-nums[i];
int left=i+1;
int right=nums.size()-1;
while(left<right)
{
if(nums[left]+nums[right]==-nums[i])
{
vector<int> oneRes;
oneRes.push_back(nums[i]);
oneRes.push_back(nums[left]);
oneRes.push_back(nums[right]);
res.push_back(oneRes);
left++;
right--;
while(left<right&&nums[left]==nums[left-1])
left++;
while(left<right&&nums[right]==nums[right+1])
right--;
}
else if(nums[left]+nums[right]>-nums[i])
{
right--;
}
else
{
left++;
}
}
}
return res;
}
};
Java实现:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res=new LinkedList<>();
for(int i=0;i<nums.length-2;++i)
{
if(i==0||i>0&&(nums[i]!=nums[i-1]))
{
int twoSum=-nums[i];
int left=i+1;
int right=nums.length-1;
while(left<right)
{
if(nums[left]+nums[right]==twoSum)
{
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
left++;
right--;
while(left<right&&nums[left]==nums[left-1])
left++;
while(left<right&&nums[right]==nums[right+1])
right--;
}
else if(nums[left]+nums[right]<twoSum)
left++;
else
right--;
}
}
}
return res;
}
}