【LeetCode】15 三數之和3Sum
阿新 • • 發佈:2018-11-21
給定一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0?
找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
例如, 給定陣列 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為: [ [-1, 0, 1], [-1, -1, 2] ]
彎路:
1 最先想到的應該是三層for迴圈暴力求解,可寫了也沒什麼意義。
2 之後又想到可不可以借鑑兩數之和的思路,通過HashMap進行O(n^ 3) 向 O(n^2)的簡化。事實證明,我想多了,經過兩個小時的自閉debug和各種複雜資料結構的推演,得出了一個結論,不要妄圖依賴Map的提供的api。也可能是對這道題來說,這種方法不好用。
附上使用HashMap的思路並在文章最後附上錯誤程式碼
(1)先用一個Map<Map<Integer, Integer>, Integer>將前兩個元素i,j和他們加在一起的需要的數字連線起來,構造這個結構的時間需要是n^2
(2)排序後對每個nums中的元素進行遍歷,對每個元素查詢是否在上述Map的value集中存在該元素,若存在,進行去重後即可新增相應元素到結果中。
3 發現了一個用Map減少一重遍歷的實現:https://blog.csdn.net/lnho2015/article/details/51314133
具體思路是隻把第一個元素用Map連線
正確思路:
需要解決的問題是:如何把三重遍歷變為雙重遍歷
解決思路也是重點:排序+雙指標
1 先對nums進行排序,利用排序和兩個“指標”,將三重遍歷變為雙重遍歷,思路類似快排
2 另外,一定要對資料進行去重,否則一些極端輸入如[0,0,0,0,0,0,0,0,0,0],速度會非常慢
具體思路:
1 對異常資料進行處理,如[1,2]和[0,0,0]
2 因為已經排過序了,所以如果當前的元素大於0,那麼後面的元素一定也大於0,必不符合條件,所以直接return
3 對起點進行去重之後,對當前元素之後的元素進行遍歷,設定兩個指標left和right,分別從左右向中間移動,小則left右移,大則right左移。此處必須保證去重,每次進行判定的必須是“新”的組合。
下面的演算法執行時間在55~59ms之間
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<>(); Arrays.sort(nums); if (nums.length < 3){ return result; } if(nums.length == 3){ if (nums[0] + nums[1] + nums[2] == 0){ result.add(Arrays.asList(nums[0], nums[1], nums[2])); return result; } } for (int base = 0;base < nums.length - 3;base++){ if(nums[base] > 0) return result; //起點去重 if(base > 0 && nums[base] == nums[base - 1]){ continue; } int left = base + 1; int right = nums.length - 1; List<Integer> temp = null; while (left < right){ if (nums[left] + nums[right] + nums[base] == 0){ temp = Arrays.asList(nums[base], nums[left], nums[right]); result.add(temp); //左右去重 while (left < right && nums[left + 1] == nums[left]) left++; while (left < right && nums[right - 1] == nums[right]) right--; left++; right--; } else if (nums[left] + nums[right] + nums[base] < 0) left++; else if (nums[left] + nums[right] + nums[base] > 0) right--; } } return result; } }
錯誤程式碼
class Solution {
public List<Map<Integer, Integer>> getKeys(Map map, Integer value){
List<Map<Integer, Integer>> keys = new ArrayList<>();
Set set = map.entrySet();
Iterator<Map.Entry<Map<Integer,Integer>, Integer>> iterator = set.iterator();
while(iterator.hasNext()){
Map.Entry<Map<Integer,Integer>, Integer> entry = iterator.next();
if(entry.getValue().equals(value)){
keys.add(entry.getKey());
}
}
return keys;
}
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
Map<Map<Integer, Integer>, Integer> map = new HashMap<>();
int need;
//建立前兩個元素與需求的第三個元素的HashMap
for (int i = 0;i < nums.length;i++){
if (i > 0 && nums[i] == nums[i-1]){
continue;
}
for (int j = i + 1;j < nums.length;j++){
if (j > i + 1 && nums[j] == nums[j-1]){
continue;
}
need = 0 - nums[i] - nums[j];
//System.out.println(need);
Map<Integer, Integer> ij = new HashMap<>();
ij.put(i,j);
map.put(ij, need);
}
}
System.out.println(map);
List <Integer> temp = null;
for (int i = 0;i < nums.length;i++){
if (i > 0 && nums[i] == nums[i-1]){
continue;
}
if (map.containsValue(nums[i])){
System.out.println(i);
for (Map<Integer, Integer> it : getKeys(map,nums[i])){
System.out.println(i + "..." + it);
Iterator<Integer> iter = it.keySet().iterator();
Integer key = iter.next();
Integer value = it.get(key);
if(i == key || i == value)
continue;
System.out.println(i +" "+ key +" "+ value);
temp = Arrays.asList(nums[i],nums[key],nums[value]);
result.add(temp);
}
}
}
return result;
}
}