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] ]
题目:给一个数组,求三个和为0的组合;
思路一:最简单的就是暴力解法,把所有的组合都列出来,算出结果。这种方法肯定不是题目想要的解法,但是还是尝试了编写,也把这份代码贴上。
public List<List<Integer>> threeSum(int[] sum) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
for(int i = 0; i < sum.length; i++) {
for(int j = i+1; j< sum.length;j++) {
for(int k = j+1 ; k < sum.length;k++) {
if(isZero(sum[i], sum[j], sum[k])){
List<Integer> listNum = new ArrayList<Integer>();
listNum.add(sum[i]);
listNum.add(sum[j]);
listNum.add(sum[k]);
Collections.sort(listNum, new Comparator<Integer>() {
public int compare(Integer arg0, Integer arg1){
return arg0.compareTo(arg1);
}
});
if(!list.contains(listNum)){
list.add(listNum);
}
}
}
}
}
return list;
}
public boolean isZero(int n1, int n2, int n3) {
if(n1+n2+n3 == 0) {
return true;
}else {
return false;
}
}
思路二:3sum是求三个和为0,那就可以转换成2sum(和为另一个的相反数)。先考虑2sum如何做,一般解法是O(n^2),所以不能简单的遍历所有结果,需要舍弃一些组合,那就先把数组排个序,这样好找规律。一般我考虑是不是可以头尾向中间循环,这样复杂度会小一些,当两个和为0时即为目标组合。
public static List<List<Integer>> twoSum(int[] num) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
Arrays.sort(num);
int left = 0;//左指针
int right = num.length-1;//右指针
while(left < right) {
if((num[left] + num[right])==0){
List<Integer> listNum = new ArrayList<Integer>();
listNum.add(num[left]);
listNum.add(num[right]);
list.add(listNum);
while(left < right && num[left] == num[left+1])left++;//排除和num[left]下一位相同的数
while(left < right && num[right] == num[right-1])right--;
left++;
right--;
} else if(num[left] + num[right] < 0) {
left++;//偏小,左端增加
} else{
right--;//偏大,右端增加
}
}
return list;
}
回到3sum,就可以先固定一个数,然后另外求两个数的组合为这个数的相反数。
List<List<Integer>> list = new ArrayList<List<Integer>>();
public static List<List<Integer>> threeSum(int[] num) {
if(num == null || num.length<3) return list;
Arrays.sort(num);
for(int i = 0;i < num.length;i++) {
int left = i+1;
int right = num.length-1;
twoSum(num, left, right, num[i]);
}
return list;
}
public static List<List<Integer>> twoSum(int[] num, int l, int r, int n) {
Arrays.sort(num);
int left = l;//左指针
int right = r;//右指针
while(left < right) {
if((num[left] + num[right]) + n ==0){
List<Integer> listNum = new ArrayList<Integer>();
listNum.add(num[left]);
listNum.add(num[right]);
listNum.add(n);
Collections.sort(listNum);
if(!list.contains(listNum)){
list.add(listNum);
};//排除重复的listNum
while(left < right && num[left] == num[left+1])left++;//排除和num[left]下一位相同的数
while(left < right && num[right] == num[right-1])right--;
left++;
right--;
} else if(num[left] + num[right] + n < 0) {
left++;//偏小,左端增加
} else{
right--;//偏大,右端增加
}
}
return list;
}
but!!!这种方式也超时了,想破脑袋也没发现哪里有问题,没办发,百度后发现是listNum排序这段代码的问题,从一开始就应该排除掉相同的数字,因为这个数组是有序的,只需前后比较即可排除。不同之处以标红。
List<List<Integer>> list = new ArrayList<List<Integer>>();
public List<List<Integer>> threeSum(int[] num) {
if(num == null || num.length<3) return list;
Arrays.sort(num);
for(int i = 0;i < num.length-2;i++) {
int left = i+1;
int right = num.length-1;
<span style="white-space:pre"> </span>if (i > 0 && num[i] == num[i-1]) continue;
twoSum(num, left, right, num[i]);
}
return list;
}
public List<List<Integer>> twoSum(int[] num, int l, int r, int n) {
int left = l;//左指针
int right = r;//右指针
while(left < right) {
if((num[left] + num[right]) + n ==0){
List<Integer> listNum = new ArrayList<Integer>();
listNum.add(num[left]);
listNum.add(num[right]);
listNum.add(n);
// Collections.sort(listNum);
// if(!list.contains(listNum)){
// list.add(listNum);
// };</span><span style="color:#333333;">
list.add(listNum);
while(left < right && num[left] == num[left+1])left++;//排除和num[left]下一位相同的数
while(left < right && num[right] == num[right-1])right--;
left++;
right--;
} else if(num[left] + num[right] + n < 0) {
left++;//偏小,左端增加
} else{
right--;//偏大,右端增加
}
}
return list;
}</span>