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)
brute force时间复杂度为O(n^3)。这道题和Two Sum有所不同,使用哈希表的解法并不是很方便,因为结果数组中元素可能重复.
如果不排序对于重复的处理将会比较麻烦,因此这道题一般使用排序之后夹逼的方法,总的时间复杂度为O(n^2+nlogn)=(n^2),空间复杂度是O(n),
public class Solution {
public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(num==null || num.length<=2)
return res;
Arrays.sort(num);
for(int i=num.length-1;i>=2;i--){ //【注意3】
if(i<num.length-1 && num[i]==num[i+1]) //【注意3】
continue;
ArrayList<ArrayList<Integer>> curRes = twoSum(num,i-1,-num[i]);
for(int j=0;j<curRes.size();j++)
curRes.get(j).add(num[i]);
res.addAll(curRes);
}
return res;
}
private ArrayList<ArrayList<Integer>> twoSum(int[] num, int end, int target){
//【注意1】此处定义2D-list和twoSum还是threeSum无关,而是和是否有多重解有关,因为之前的TwoSum不考虑多重解
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
int left=0;
int right= end;
while(left<right){
if(num[left]+num[right]==target){ //case(1)
ArrayList<Integer> tempRes = new ArrayList<Integer>(); //【注意1】
tempRes.add(num[left]); //【注意2】
tempRes.add(num[right]);//【注意2】
res.add(tempRes);
left++; //【注意3】
right--;//【注意3】
while(left<right && num[left]==num[left-1]) //【注意3】
left++;
while(left<right && num[right]==num[right+1])//【注意3】
right--;
}
else if(num[left]+num[right]<target) //case(2),先写left右移的case调理会比较清楚
left++;
else //case(3)
right--;
}
return res;
}
}
【注意1】当解是一个ArrayList,且有多解的题,定义2D-ArrayList。
且之前定义的2D-ArrayList不用急于定义内部的ArrayList元素,在此行用到时候在定义。
【注意2】注意2D-ArrayList的范型,OJ时候出现范型的compile error。
【注意3】算是一个编程技巧。以前写程序时候,不知道比较两个相邻元素,使用A[i]==A[i+1],还是A[i-1]==A[i]。
此处总结一下:
(1)首先left++,才有了 left-1 的存在;其次比较时候考虑时间顺序:当前left == 之前left( 既 left-1 的状态)
“left++; num[left]==num[left - 1]”
(2)首先right--,才有了 right+1 的存在;其次比较时候考虑时间顺序:当前right == 之前right( 既 right+1 的状态)
“right--; num[right]==num[right + 1]”
【后记】此法可以推广到Ksum,先排序,然后做num.length-2 次循环,最内层循环左右夹逼。
时间复杂度是 max( nlogn , n^(k-1) ),排序和循环,谁的O高阶取谁。
【后记】另一种较简洁的代码:yunduomo的3Sum