演算法之陣列——三數之和

it_was發表於2020-10-03

難度中等:confused:

給你一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。

示例:
給定陣列 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[[-1, 0, 1],[-1, -1, 2]]

在看這個問題之前可以先看下 兩數之和

當碰到這個三數之和的時候,首先想到的思路就是既然 a + b = c ,那麼 a + b = -c,我在想,是不是可以以這個 -ctarget ,然後迭代陣列,重用這個兩數之和的方法?程式碼如下:

public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        HashSet<List<Integer>> set = new HashSet<>();
        for(int i = 0;i<nums.length;i++){
            List<List<Integer>> res = twoSum(nums,-nums[i],i);
            if(res != null && res.size() != 0){
                for(List<Integer>  l : res) {
                    //將適合的數對與當前陣列合起來然後去重!!!
                    int[] arr = new int[]{nums[i], l.get(0), l.get(1)};
                    Arrays.sort(arr); //通過排序進行去重!!!
                    List<Integer> list1 = new ArrayList<>();
                    list1.add(arr[0]);
                    list1.add(arr[1]);
                    list1.add(arr[2]);
                    if (!set.contains(list1)) {
                        set.add(list1);
                    }
                }
            }
        }
        List list2 = new ArrayList(set);
        System.out.println(list2.toString());
        return list2;
    }
    public List<List<Integer>> twoSum(int[] nums, int target ,int j){
        //兩數之和,返回所有和為目標和的數對!!!注意 傳入的本位 j 應該跳過!!! 
        HashSet<Integer> hashSet = new HashSet<>();
        List<List<Integer>> list = new ArrayList<>();
        for(int i = 0;i<nums.length;i++){
            if(i == j){
                continue;
            }
            if(hashSet.contains(target - nums[i])){
                List<Integer> l = new ArrayList<>();
                l.add(nums[i]);
                l.add(target - nums[i]);
                list.add(new ArrayList<>(l));
            }
            hashSet.add(nums[i]);
        }
        return list;
    }

結果:sob:

演算法之陣列——三數之和

超時了。。。。。。。找出合適的數對的時間複雜度就 O(n ^ 2),然後又通過排序進行去重,整體的時間複雜度固然很高

排序加雙指標:squirrel:

首先因為所求為三個數之和為0,不需要求下標,所以可以對陣列進行排序,使得整體具有規律性!其實兩數之和也可以通過排序加雙指標,但是那個題所求為下標,不能打亂陣列,所以最好的就是用雜湊表。一旦排好序之後,就可以用上面的思路了,即將每個數作為 target,然後遍歷剩下的數,進行判斷!
其實這裡邊有很多技巧,可以仔細通過程式碼體會:

public List<List<Integer>> threeSum1(int[] nums) {
        if(nums == null || nums.length <= 2){
            return new ArrayList<>();
        }
        List<List<Integer>> res_list = new ArrayList<>();
        int len = nums.length;
        int left = -1;
        int right = -1;
        Arrays.sort(nums); //排序是前提!!!使其有序之後,才可以通過一些條件加速過程
        for(int i = 0;i < len;i++){
            if(nums[i] > 0){
                //如果當前位置大於零,說明之後的都大於零,一定不可能湊出三個數使得 a + b + c = 0!
                break;
            }
            if(i >= 1 && nums[i] == nums[i -1]){
                //如果當前數字等於前一個數字,說明是重複的,沒必要在進行之後判斷!
                continue;
            }
            left = i + 1; //從後面一個數字開始即可!
            right = len - 1;
            while(left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    res_list.add(list);
                    left++;
                    right--;
                    //以下兩個while迴圈為去重過程
                    while (left < right &&nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while(left < right && nums[right] == nums[right + 1]){
                        right --;
                    }
                }else if(sum > 0){
                    right --;
                }else{
                    left ++;
                }
            }
        }
        return res_list;
    }

演算法之陣列——三數之和

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章