@@ -104,17 +104,21 @@ public class ThreeSumSlow implements ThreeSum {
104104 public int count (int [] nums ) {
105105 int N = nums. length;
106106 int cnt = 0 ;
107- for (int i = 0 ; i < N ; i++ )
108- for (int j = i + 1 ; j < N ; j++ )
109- for (int k = j + 1 ; k < N ; k++ )
110- if (nums[i] + nums[j] + nums[k] == 0 )
107+ for (int i = 0 ; i < N ; i++ ) {
108+ for (int j = i + 1 ; j < N ; j++ ) {
109+ for (int k = j + 1 ; k < N ; k++ ) {
110+ if (nums[i] + nums[j] + nums[k] == 0 ) {
111111 cnt++ ;
112+ }
113+ }
114+ }
115+ }
112116 return cnt;
113117 }
114118}
115119```
116120
117- ### 2. ThreeSumFast
121+ ### 2. ThreeSumBinarySearch
118122
119123通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。
120124
@@ -123,42 +127,83 @@ public class ThreeSumSlow implements ThreeSum {
123127该方法可以将 ThreeSum 算法增长数量级降低为 O(N<sup >2</sup >logN)。
124128
125129``` java
126- public class ThreeSumFast {
127- public static int count (int [] nums ) {
130+ public class ThreeSumBinarySearch implements ThreeSum {
131+
132+ @Override
133+ public int count (int [] nums ) {
128134 Arrays . sort(nums);
129135 int N = nums. length;
130136 int cnt = 0 ;
131- for (int i = 0 ; i < N ; i++ )
137+ for (int i = 0 ; i < N ; i++ ) {
132138 for (int j = i + 1 ; j < N ; j++ ) {
133139 int target = - nums[i] - nums[j];
134140 int index = BinarySearch . search(nums, target);
135141 // 应该注意这里的下标必须大于 j,否则会重复统计。
136- if (index > j)
142+ if (index > j) {
137143 cnt++ ;
144+ }
138145 }
146+ }
139147 return cnt;
140148 }
141149}
142150```
143151
144152``` java
145153public class BinarySearch {
154+
146155 public static int search (int [] nums , int target ) {
147156 int l = 0 , h = nums. length - 1 ;
148157 while (l <= h) {
149158 int m = l + (h - l) / 2 ;
150- if (target == nums[m])
159+ if (target == nums[m]) {
151160 return m;
152- else if (target > nums[m])
161+ } else if (target > nums[m]) {
153162 l = m + 1 ;
154- else
163+ } else {
155164 h = m - 1 ;
165+ }
156166 }
157167 return - 1 ;
158168 }
159169}
160170```
161171
172+ ### 3. ThreeSumTwoPointer
173+
174+ 更有效的方法是先将数组排序,然后使用双指针进行查找,时间复杂度为 O(N<sup >2</sup >)。
175+
176+ ``` java
177+ public class ThreeSumTwoPointer implements ThreeSum {
178+
179+ @Override
180+ public int count (int [] nums ) {
181+ int N = nums. length;
182+ int cnt = 0 ;
183+ Arrays . sort(nums);
184+ for (int i = 0 ; i < N - 2 ; i++ ) {
185+ int l = i + 1 , h = N - 1 , target = - nums[i];
186+ if (i > 0 && nums[i] == nums[i - 1 ]) continue ;
187+ while (l < h) {
188+ int sum = nums[l] + nums[h];
189+ if (sum == target) {
190+ cnt++ ;
191+ while (l < h && nums[l] == nums[l + 1 ]) l++ ;
192+ while (l < h && nums[h] == nums[h - 1 ]) h-- ;
193+ l++ ;
194+ h-- ;
195+ } else if (sum < target) {
196+ l++ ;
197+ } else {
198+ h-- ;
199+ }
200+ }
201+ }
202+ return cnt;
203+ }
204+ }
205+ ```
206+
162207## 倍率实验
163208
164209如果 T(N) \~ aN<sup >b</sup >logN,那么 T(2N)/T(N) \~ 2<sup >b</sup >。
@@ -180,29 +225,20 @@ public class BinarySearch {
180225public class RatioTest {
181226
182227 public static void main (String [] args ) {
183-
184228 int N = 500 ;
185229 int loopTimes = 7 ;
186230 double preTime = - 1 ;
187-
188231 while (loopTimes-- > 0 ) {
189-
190232 int [] nums = new int [N ];
191-
192233 StopWatch . start();
193-
194234 ThreeSum threeSum = new ThreeSumSlow ();
195-
196235 int cnt = threeSum. count(nums);
197236 System . out. println(cnt);
198-
199237 double elapsedTime = StopWatch . elapsedTime();
200238 double ratio = preTime == - 1 ? 0 : elapsedTime / preTime;
201239 System . out. println(N + " " + elapsedTime + " " + ratio);
202-
203240 preTime = elapsedTime;
204241 N *= 2 ;
205-
206242 }
207243 }
208244}
0 commit comments