LeetCode 笔记系列三 3Sum

题目Given an array S of n integers, are there elements abc 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     }
O(n^2logn)

这里要注意一些边界条件的判断。

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     }
O(n^2)

这道虽然不是那么难,但是要注意的是边界条件的判断和代码的简洁;排序是个好东西。

解法二这种,还可以应用到4Sum,3SumCloset问题中。想对于穷举法,是一种“有计划”的搜索问题。

posted on 2013-07-04 11:47  lichen782  阅读(1785)  评论(0编辑  收藏  举报