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)
Before solving it, let's first look how it works if we change the problem to finding two numbers which sum up to zero, as follows,
Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2
The solution is quite straightforward. We use a hash map to keep record what numbers have appeared in the list, then we search the negative of this number in the hash map. If it exists, then we know that these two numbers sum up to zero. The complexity of this algorithm is O(n).Output: index1=1, index2=2
Java: 2 Sum
01 public int[] twoSum(int[] numbers, int target) {
02 int[] results=new int[2];
03 HashMap<Integer,Integer> hm = new HashMap<Integer,Integer>();
04 for(int i=0; i<numbers.length;i++)
05 {
06 hm.put(numbers[i],i);
07 }
08
09 for (int i=0;i<numbers.length;i++)
10 {
11 int key= target - numbers [i];
12 if(hm.containsKey(key))
13 {
14 int indx2=hm.get(key);
15 if(i<indx2)
16 {
17 results[0]=i+1;
18 results[1]=indx2+1;
19 }
20 }
21 }
22 return results;
23 }
02 int[] results=new int[2];
03 HashMap<Integer,Integer> hm = new HashMap<Integer,Integer>();
04 for(int i=0; i<numbers.length;i++)
05 {
06 hm.put(numbers[i],i);
07 }
08
09 for (int i=0;i<numbers.length;i++)
10 {
11 int key= target - numbers [i];
12 if(hm.containsKey(key))
13 {
14 int indx2=hm.get(key);
15 if(i<indx2)
16 {
17 results[0]=i+1;
18 results[1]=indx2+1;
19 }
20 }
21 }
22 return results;
23 }
However, to calculate 3 sum is more tricky. We can still use a hash map or a hash table to store existing values. Then, we need to map the sum of every pairs to the hash table to decide whether the 3 sum triples exist. This will need a complexity of O(n^2).
Is there a better solution?
Another method introduced by wiki is to do a sorting first.
http://en.wikipedia.org/wiki/3SUM
You first do a sorting using O(nlogn), then you have two nested loops to traverse the array, which still cost O(n^2). The sorting method is still not fast enough, the large test case cannot pass the OJ.
We should think about how theses three numbers might be. Think about many situations.
1. What if there are more than three of zeros? Then we should output<0,0,0>
2. If there is one 0, and if there exists a and -a, then we should output<-a,0,a>
3. a, b are two positive numbers, c is a negative number
4. a, b are two negative numbers, c is a positive number
In the following approach, we use two hash tables, one for positive numbers, one for negative numbers. Further, we use a counter to store the number of zeros. The algorithm is also O(n^2), however, as we do not need to compute the sum of every pair, it use less time than the above methods. O(n^2/2)<O(n^2/8)*2.
Java: 3 Sum
001 public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
002 ArrayList<ArrayList<Integer>> result=new ArrayList<ArrayList<Integer>>();
003 ArrayList<Integer>posArray=new ArrayList<Integer>();
004 ArrayList<Integer>negArray=new ArrayList<Integer>();
005 Hashtable <Integer,Integer> pos = new Hashtable<Integer,Integer>();
006 Hashtable <Integer,Integer> neg = new Hashtable<Integer,Integer>();
007 int numZeros=0;
008
009 for(int i=0;i<num.length;i++)
010 {
011 if(num[i]==0)
012 {
013 numZeros++;
014 }
015 else if(num[i]<0)
016 {
017 if(!neg.containsKey(num[i]))
018 {
019 neg.put(num[i],1);
020 }
021 else
022 {
023 int time=neg.get(num[i]);
024 time++;
025 neg.put(num[i],time);
026 }
027 if(!negArray.contains(num[i]))
028 {
029 negArray.add(num[i]);
030 }
031 }
032 else //num[i]>0
033 {
034 if(!pos.containsKey(num[i]))
035 {
036 pos.put(num[i],1);
037 }
038 else
039 {
040 int time=pos.get(num[i]);
041 time++;
042 pos.put(num[i],time);
043 }
044 if(!posArray.contains(num[i]))
045 {
046 posArray.add(num[i]);
047 }
048 }
049 }
050
051 if(numZeros>=3)
052 {
053 ArrayList<Integer>tripple=new ArrayList<Integer>();
054 for(int i=0;i<3;i++)
055 tripple.add(0);
056 result.add(tripple);
057 }
058 if(numZeros>=1)
059 {
060 for(int i=0;i<negArray.size();i++)
061 {
062 int negValue=negArray.get(i);
063 int posValue=0-negValue;
064 if(pos.containsKey(posValue))
065 {
066 ArrayList<Integer>tripple=new ArrayList<Integer>();
067 tripple.add(negValue);
068 tripple.add(0);
069 tripple.add(posValue);
070 result.add(tripple);
071 }
072 }
073 }
074
075 for(int i=0;i<negArray.size();i++)
076 {
077 int negValue=negArray.get(i);
078 if(neg.get(negValue)>=2)
079 {
080 int posValue=negValue*(-2);
081 if(pos.containsKey(posValue))
082 {
083 ArrayList<Integer>tripple=new ArrayList<Integer>();
084 tripple.add(negValue);
085 tripple.add(negValue);
086 tripple.add(posValue);
087 result.add(tripple);
088 }
089 }
090 }
091
092 for(int i=0;i<posArray.size();i++)
093 {
094 int posValue=posArray.get(i);
095 if(pos.get(posValue)>=2)
096 {
097 int negValue=posValue*(-2);
098 if(neg.containsKey(negValue))
099 {
100 ArrayList<Integer>tripple=new ArrayList<Integer>();
101 tripple.add(negValue);
102 tripple.add(posValue);
103 tripple.add(posValue);
104 result.add(tripple);
105 }
106 }
107
108 }
109
110 for(int i=0;i<negArray.size();i++)
111 {
112 for(int j=i+1;j<negArray.size();j++)
113 {
114 int small=negArray.get(i);
115 int large=negArray.get(j);
116
117 if(small>large)
118 {
119
120 small=negArray.get(j);
121 large=negArray.get(i);
122 }
123 int sum=small+large;
124 int posValue=0-sum;
125 if(pos.containsKey(posValue))
126 {
127
128 ArrayList<Integer>tripple=new ArrayList<Integer>();
129 tripple.add(small);
130 tripple.add(large);
131 tripple.add(posValue);
132 result.add(tripple);
133 }
134
135 }
136 }
137
138 for(int i=0;i<posArray.size();i++)
139 {
140 for(int j=i+1;j<posArray.size();j++)
141 {
142 int small=posArray.get(i);
143 int large=posArray.get(j);
144
145 if(small>large)
146 {
147
148 small=posArray.get(j);
149 large=posArray.get(i);
150 }
151 int sum=small+large;
152 int negValue=0-sum;
153 if(neg.containsKey(negValue))
154 {
155
156 ArrayList<Integer>tripple=new ArrayList<Integer>();
157 tripple.add(negValue);
158 tripple.add(small);
159 tripple.add(large);
160
161 result.add(tripple);
162 }
163
164 }
165 }
166
167 return result;
168 }
002 ArrayList<ArrayList<Integer>> result=new ArrayList<ArrayList<Integer>>();
003 ArrayList<Integer>posArray=new ArrayList<Integer>();
004 ArrayList<Integer>negArray=new ArrayList<Integer>();
005 Hashtable <Integer,Integer> pos = new Hashtable<Integer,Integer>();
006 Hashtable <Integer,Integer> neg = new Hashtable<Integer,Integer>();
007 int numZeros=0;
008
009 for(int i=0;i<num.length;i++)
010 {
011 if(num[i]==0)
012 {
013 numZeros++;
014 }
015 else if(num[i]<0)
016 {
017 if(!neg.containsKey(num[i]))
018 {
019 neg.put(num[i],1);
020 }
021 else
022 {
023 int time=neg.get(num[i]);
024 time++;
025 neg.put(num[i],time);
026 }
027 if(!negArray.contains(num[i]))
028 {
029 negArray.add(num[i]);
030 }
031 }
032 else //num[i]>0
033 {
034 if(!pos.containsKey(num[i]))
035 {
036 pos.put(num[i],1);
037 }
038 else
039 {
040 int time=pos.get(num[i]);
041 time++;
042 pos.put(num[i],time);
043 }
044 if(!posArray.contains(num[i]))
045 {
046 posArray.add(num[i]);
047 }
048 }
049 }
050
051 if(numZeros>=3)
052 {
053 ArrayList<Integer>tripple=new ArrayList<Integer>();
054 for(int i=0;i<3;i++)
055 tripple.add(0);
056 result.add(tripple);
057 }
058 if(numZeros>=1)
059 {
060 for(int i=0;i<negArray.size();i++)
061 {
062 int negValue=negArray.get(i);
063 int posValue=0-negValue;
064 if(pos.containsKey(posValue))
065 {
066 ArrayList<Integer>tripple=new ArrayList<Integer>();
067 tripple.add(negValue);
068 tripple.add(0);
069 tripple.add(posValue);
070 result.add(tripple);
071 }
072 }
073 }
074
075 for(int i=0;i<negArray.size();i++)
076 {
077 int negValue=negArray.get(i);
078 if(neg.get(negValue)>=2)
079 {
080 int posValue=negValue*(-2);
081 if(pos.containsKey(posValue))
082 {
083 ArrayList<Integer>tripple=new ArrayList<Integer>();
084 tripple.add(negValue);
085 tripple.add(negValue);
086 tripple.add(posValue);
087 result.add(tripple);
088 }
089 }
090 }
091
092 for(int i=0;i<posArray.size();i++)
093 {
094 int posValue=posArray.get(i);
095 if(pos.get(posValue)>=2)
096 {
097 int negValue=posValue*(-2);
098 if(neg.containsKey(negValue))
099 {
100 ArrayList<Integer>tripple=new ArrayList<Integer>();
101 tripple.add(negValue);
102 tripple.add(posValue);
103 tripple.add(posValue);
104 result.add(tripple);
105 }
106 }
107
108 }
109
110 for(int i=0;i<negArray.size();i++)
111 {
112 for(int j=i+1;j<negArray.size();j++)
113 {
114 int small=negArray.get(i);
115 int large=negArray.get(j);
116
117 if(small>large)
118 {
119
120 small=negArray.get(j);
121 large=negArray.get(i);
122 }
123 int sum=small+large;
124 int posValue=0-sum;
125 if(pos.containsKey(posValue))
126 {
127
128 ArrayList<Integer>tripple=new ArrayList<Integer>();
129 tripple.add(small);
130 tripple.add(large);
131 tripple.add(posValue);
132 result.add(tripple);
133 }
134
135 }
136 }
137
138 for(int i=0;i<posArray.size();i++)
139 {
140 for(int j=i+1;j<posArray.size();j++)
141 {
142 int small=posArray.get(i);
143 int large=posArray.get(j);
144
145 if(small>large)
146 {
147
148 small=posArray.get(j);
149 large=posArray.get(i);
150 }
151 int sum=small+large;
152 int negValue=0-sum;
153 if(neg.containsKey(negValue))
154 {
155
156 ArrayList<Integer>tripple=new ArrayList<Integer>();
157 tripple.add(negValue);
158 tripple.add(small);
159 tripple.add(large);
160
161 result.add(tripple);
162 }
163
164 }
165 }
166
167 return result;
168 }
话说这道题真的很奇葩。无论是排序还是整个hash,都要n^2了,后来在小老鼠的提醒下,才想出应该这么做。谢谢小老鼠^_^
回复删除