1. 程式人生 > >【LeetCode】15 三數之和3Sum

【LeetCode】15 三數之和3Sum

給定一個包含 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;
    }
}