題目難度:Medium
題目:
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.
翻譯:
給定一個n個整數的數組S,S中是否存在有a b c三個元素使得a+b+c=0?在S中找到所有的非重復組合,使它們的和為0
注意:答案集不能包含重復的數組。例如:[1,5,-6]與[-6,5,1]等數組,答案只出現其中一個
思路:三。。。個。。for循環??

(⊙x⊙;)。。。就要寫!
Code:311 / 313 test cases passed. —— Time Limit Exceeded 時間復雜度:O(N3)
1 public static List<List<Integer>> threeSum1(int[] nums) { 2 Arrays.sort(nums); 3 List<List<Integer>> outList = new ArrayList<List<Integer>>(); 4 for (int i = 0; i < nums.length; i++) { 5 if ((i > 0 && nums[i] == nums[i - 1])) // 各循環起始點不需要判斷重復 6 continue; // 不用i++ 的原因:避免最后的k還要增加邊界判斷,進入下一個循環則會自動邊界判斷“i < nums.length” 7 for (int j = i + 1; j < nums.length; j++) { 8 if ((j > i + 1 && nums[j] == nums[j - 1])) 9 continue; // 當有很多for和if的時候,條件取反后用continue,以此取代if的{}縮進,使代碼可讀性增加 10 for (int k = j + 1; k < nums.length; k++) { 11 if ((k > j + 1 && nums[k] == nums[k - 1])) 12 continue; 13 if ((nums[i] + nums[j] + nums[k] == 0)) { 14 List<Integer> inList = new ArrayList<Integer>(); 15 inList.add(nums[i]); 16 inList.add(nums[j]); 17 inList.add(nums[k]); 18 outList.add(inList); 19 break; 20 } 21 } 22 } 23 } 24 return outList; 25 }
(因為只有在尾巴進行添加並無其他功能,所以采用ArrayList比較實惠和效率)
312測試用例超時:
[82597,-9243,…………此處省略N千個數字]
不想說話,讓蠢人靜靜。。。。
————————————————————————智商分割線————————————————————————
參考答案:74ms——beats85.12% 時間復雜度O(N2)
public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<List<Integer>>(); Arrays.sort(nums); for (int i = 0; i < nums.length-2; i++) { int left = i + 1; int right = nums.length - 1; if (i > 0 && nums[i] == nums[i - 1]) continue; // 去掉重復的起點 while (left < right) { int sum = nums[left] + nums[right] + nums[i]; if (sum == 0) { result.add(Arrays.asList(new Integer[]{nums[i], nums[left], nums[right]})); while (left < right && nums[left] == nums[left + 1]) left++; // 去掉重復的左點 while (left < right && nums[right] == nums[right - 1]) right--; // 去掉重復的右點 right--; // 進入下一組左右點判斷 left++; } else if (sum > 0) { right--; // sum>0 ,說明和過大了,需要變小,所以移動右邊指針 } else { left++; // 同理,需要變大,移動左指針 } } } return result; }
精髓在於: (排序 + 去重) + 雙指針移動相向定位
注意:不需要“重復的雙胞胎數組”,所以是“組合”即Cn/m(從M個數里隨機選出N個數有多少種情況)而不是排列An/m——考慮去重
所查找的數之間具有一定的關聯(本題為:和為0),那么就應該利用這個屬性,而不是簡單的進行搜索。

思路解析:
首先考慮數組遍歷時去重:方法一:先將數組排好序,在遍歷的時候與上一個進行比較,相同則直接進入下一個
方法二:用容器Set——簡單,但是同樣需要排序,增加算法復雜度並且此題三個數操作不方便【在第一題TwoSum的后面有更新用Set的方法】。
所以數組遍歷去重在已排序的情況下優先采取方法一。
其次考慮降低多次循環算法復雜度:
降低復雜度一般的途徑就是利用已有的而未用到的條件將多余的步驟跳過或者刪去!
由於三個數是具有一個特點的:和為某個定值,這個條件只是用來判斷了而並沒有使用
並且,由上個去重得知,后面使用的數組是已經排序好的。
此時仔細想想應該就能想到
從兩端使用兩個指針相向移動,兩端指針所指數之和如果小於目標值,只需要移動左邊的指針,否則只需要移動右邊的指針!!
例如[1,1,1,4,5,7,8,8,9]中 定目標值為15,從兩邊開始,1+9為10,小於15,移動右邊指針左移變成1+8只會更少,所以移動左邊變成4+9以此類推。。
媽耶!我想不到啊!

此時為兩數定位,而題目中要找3個數
………………外嵌for循環!!!又回到了我的領域 ahhhhhhh~
分析搞定!
