題目要求:
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/3sum
示例:
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:
一
很明顯,第一個辦法就是暴力法:
- 先排序
- 找到數組中的第一個元素,判斷是否 > 0,true 直接返回結果。
- 找到第二個元素,判斷這兩個元素相加是否 > 0 true 直接返回結果
- 從第三個位置,開始往後依次找能讓三數相加==0的值,找到就加入list,(然後對第二個位置的數往後移,繼續第3、4步)沒找到就第二次循環
這樣的時間複雜度肯定很高,已經接近了O(N³),肯定不是我們想要的結果,而且提交該答案也會報超時。
二
一個數去找另一個數,加起來再去找最後一個數太慢了
我們可以用雙指針,前後各一個指針,中間的數通過for循環依次更新
依然是先排序,每次更新完middle之後, 就計算left + right + middle的和,如果和大於0了 ,right-- ;如果小於0, left++ 直到找到 == 0的組合,或者找不到 == 0的組合
。
整理一下,這個時候我們再去考慮一下去重的問題和特殊判斷
特殊判斷可以讓特殊情況下不進行那麼多的步驟
比如
- 排序完後,nums[0] > 0 直接返回結果
- 或者數組的長度小於3 也直接返回
- 或者數組爲null
- 去重的話,主要就是每次left 和 right移動的時候,判斷是否移動之後的項和移動之前的項相同,這樣就可以達到去重的目的了
代碼
//排序+雙指針
public static List<List<Integer>> threeSumDemo(int[] nums) {
List<List<Integer>> result = new LinkedList<>();
int length = nums.length;
if(nums.length < 3)
return result;
Arrays.sort(nums);
if(nums[0] > 0){
return result;
}
for(int i = 0; i < length - 2; i++){
//去重,排序過後 相同的值都在一起
if(i > 0 && nums[i] == nums[ i - 1]){
continue;
}
int middle = i + 1;
int end = length - 1;
int sum = -nums[i];
while (middle < end){
if (nums[middle] + nums[end] + nums[i] < 0 || (middle > i + 1 && nums[middle] == nums[middle - 1])) {
middle++;
} else {
if (nums[middle] + nums[end] + nums[i] > 0 || (end < nums.length - 1 && nums[end] == nums[end + 1])) {
end--;
} else {
result.add(Arrays.asList(nums[i], nums[middle], nums[end]));
middle++;
end--;
}
}
}
}
return result;
}
//暴力法,超時
public static List<List<Integer>> threeSum(int[] nums) {
if(nums.length == 0 || nums == null)
return new LinkedList<>();
int len = nums.length;
Arrays.sort(nums);
Set<List<Integer>> ans = new HashSet<>();
List<List<Integer>> ans2 = new LinkedList<>();
for(int i = 0 ;i < len - 2; i++) {
if(nums[i] > 0)
break;
else {
int j = i + 1;
int tmp = nums[i] + nums[j];
while (j < len - 1 && tmp <= 0)
{
for(int k = j + 1; k < len; k++) {
List<Integer> list = new LinkedList<>();
if (tmp + nums[k] > 0)
break;
else if (tmp + nums[k] == 0) {
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
ans.add(list);
}
}
j++;
tmp = nums[i] + nums[j];
}
}
}
for(List<Integer> list : ans) {
ans2.add(list);
}
return ans2;
}