题目: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.
For example, given array S = {-1 0 1 2 -1 -4}, A solution set is: (-1, 0, 1) (-1, -1, 2)
额外的要求是不能返回重复的triplets,返回的a,b,c的顺序要是非递减的。
解法一:首先想一下,三个数相加,要为0的话,如果不是都为0,那么至少有一个正数一个负数。可以从这一点出发,设置两个指针i和j,分别指向数组S的首尾,always保持numbers[i] <= 0 and numbers[j]>0。哦,对了,数组要先给它排序。然后判断numbers[i] + numbers[j]的符号,如果是大于0,我们就去数组负数部分search,反之去正数部分找。因为数组是sorted,search部分可以用binarySearch。代码如下:
1 //Program Runtime: 784 milli secs 2 public static ArrayList<ArrayList<Integer>> threeSum(int[] num) { 3 // Start typing your Java solution below 4 // DO NOT write main() function 5 Arrays.sort(num); 6 int i = 0; 7 int j = num.length - 1; 8 int firstNonNegativeIndex = -1; 9 ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); 10 if(j < 0 || num[i] > 0 || num[j] < 0) return result;//1.边界条件判断,如果没有非负数或者都是正数,返回空集合 11 for(int k = 0; k < num.length;k++){ 12 if(num[k] >= 0){ 13 firstNonNegativeIndex = k; 14 break; 15 } 16 } 17 for(i = 0;i <= firstNonNegativeIndex;i++){ 18 if(i > 0 && num[i] == num[i-1])continue; 19 for(j = num.length - 1; j> i + 1;j--) { 20 if(j <= num.length - 2 && num[j + 1] == num[j]) continue; 21 int twoSum = num[i] + num[j]; 22 if(twoSum > 0) { 23 int searchIdx = binarySearch(num, i+1,firstNonNegativeIndex - 1, -twoSum); 24 if(searchIdx != -1){ 25 addTuple(result,new int[]{num[i],num[searchIdx],num[j]}); 26 } 27 }else{ 28 int searchIdx = binarySearch(num, firstNonNegativeIndex,j-1, -twoSum); 29 if(searchIdx != -1){ 30 addTuple(result,new int[]{num[i],num[searchIdx],num[j]}); 31 } 32 } 33 } 34 } 35 return result; 36 }
这里要注意一些边界条件的判断。
1.如果是空数组,或者均是整数或均是负数,返回空集合;
2.在遍历i和j的时候,为满足题设“不能返回重复的triplets”,遇到和前一个相同的i或者后一个相同的j,continue。
虽然能通过大集合,但该解法并不是最好的。
解法二:这里提到一个O(n2)的解法。首先(当然最首先的还是要排序数组),固定i(for i from 0 to last)。对每个i,有j=i+1,k=last。对三者取和,如果大于0,说明k的那边取大了,k向前移动;如果小于0呢,说明j取得太小了,向后移动。如果等于0,记录i,j,k并同时移动j和k。
1 public static ArrayList<ArrayList<Integer>> threeSum2(int[] num) { 2 Arrays.sort(num); 3 ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>(); 4 for(int i = 0; i < num.length - 2;i++){ 5 if(i > 0 && num[i] == num[i-1])continue; 6 int j = i + 1; 7 int k = num.length - 1; 8 while(j < k){ 9 int twoSum = num[i] + num[j]; 10 if(twoSum + num[k] > 0){ 11 k--; 12 }else if(twoSum + num[k] < 0){ 13 j++; 14 }else { 15 addTuple(result,new int[]{num[i],num[j],num[k]}); 16 j++; 17 k--; 18 while(num[j] == num[j - 1] && num[k]==num[k+1] && j < k){ 19 j++; 20 k--; 21 } 22 } 23 } 24 } 25 return result; 26 }
这道虽然不是那么难,但是要注意的是边界条件的判断和代码的简洁;排序是个好东西。
解法二这种,还可以应用到4Sum,3SumCloset问题中。想对于穷举法,是一种“有计划”的搜索问题。