1. 程式人生 > >個人記錄-LeetCode 15. 3Sum

個人記錄-LeetCode 15. 3Sum

問題:
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.

For example, given array S = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1
, 0, 1], [-1, -1, 2] ]

題目的要求是:
從給定的陣列中找到不重複的組合。
每個組合包含3個元素,其和為0。

程式碼示例:
1、暴力解法

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();

        if (nums == null || nums.length < 3 ) {
            return
result; } int len = nums.length; //現將給定陣列從小到大排序 Arrays.sort(nums); //最小值大於0或最大值小於0,顯然是無法找到需要的組合 if (nums[0] > 0 || nums[len-1] < 0) { return result; } //考慮到組合的和為0,因此整個組合不可能全部為負數或正數 //用於儲存負數,及負數出現的次數 HashMap<Integer, Integer> negativeMap = new
HashMap<>(); ArrayList<Integer> negativeList = new ArrayList<>(); //用於儲存正數,及正數出現的次數 HashMap<Integer, Integer> positiveMap = new HashMap<>(); ArrayList<Integer> positiveList = new ArrayList<>(); //儲存是否出現0 int numOfZero = 0; for (int num : nums) { if (num < 0) { addNumToCollections(num, negativeMap, negativeList); } else if (num > 0) { addNumToCollections(num, positiveMap, positiveList); } else { ++numOfZero; } } //出現3個以上的0,顯然0 0 0的組合滿足條件 if (numOfZero >= 3) { List<Integer> answer = new ArrayList<>(); for (int i = 0; i < 3; ++i) { answer.add(0); } result.add(answer); } //每次先取一個負數,再取一個正數 for (int i = 0; i < negativeList.size(); ++i) { for (int j = 0; j < positiveList.size(); ++j) { int negative = negativeList.get(i); int positive = positiveList.get(j); //求出需要的第3個數 int complete = 0 - negative - positive; boolean addFlag = false; //需要的數小於0,並且存在 if (complete < 0 && negativeMap.containsKey(complete)) { //需要的數與主動取出的負數不一致時,它的下標必須大於主動取得負數的下標,否則會出現重複的組合 if ((complete != negative && negativeList.indexOf(complete) > i)|| //需要的數與主動取出的負數一致,那麼要求這個負數至少出現兩次以上 (complete == negative && negativeMap.get(negative) > 1)) { addFlag = true; } //所需的數為正數,類似 } else if (complete > 0 && positiveMap.containsKey(complete)){ if ((complete != positive && positiveList.indexOf(complete) > j) || (complete == positive && positiveMap.get(positive) > 1)) { addFlag = true; } } else if (complete == 0 && numOfZero > 0){ addFlag = true; } if (addFlag) { addAnswerToResult(negative, positive, complete, result); } } } return result; } private void addNumToCollections(int num, Map<Integer, Integer> map, List<Integer> list) { if (map.containsKey(num)) { map.replace(num, map.get(num), map.get(num)+1); } else { map.put(num, 1); list.add(num); } } private void addAnswerToResult(int negative, int positive, int complement, List<List<Integer>> result) { List<Integer> answer = new ArrayList<>(); answer.add(negative); answer.add(positive); answer.add(complement); result.add(answer); } }

這個方法可以得到正確結果,但是會超時。整個演算法僅看for迴圈似乎複雜度是O(N2),但算上Map的查詢,複雜度可能接近O(N3)。

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();

        if (nums == null || nums.length < 3) {
            return result;
        }

        int len = nums.length;
        Arrays.sort(nums);

        if (nums[0] > 0 || nums[len-1] < 0) {
            return result;
        }

        //輪詢陣列中的每一個數字,為了避免重複
        //找到在這個數字之後,出現的另外兩個數字,構成組合
        for (int i = 0; i < len-2;) {
            int firstIndex = i + 1;
            int lastIndex = len - 1;
            while (firstIndex < lastIndex) {
                int curr = nums[firstIndex] + nums[lastIndex] + nums[i];
                //類似於求水位問題,當前的和大於0,降低高位的下標
                if (curr > 0) {
                    do {
                        --lastIndex;
                    //跳過重複的值
                    } while (firstIndex < lastIndex && nums[lastIndex] == nums[lastIndex+1]);
                //當前的和小於0,增加低位的下標
                } else if (curr < 0){
                    do {
                        ++firstIndex;
                    } while(firstIndex < lastIndex && nums[firstIndex] == nums[firstIndex-1]);
                } else {
                    addAnswerToResult(nums[firstIndex], nums[lastIndex], nums[i], result);
                    //等於0時,找出新的兩個數,與當前數字構成組合
                    //低位增加、高位減小才有可能滿足條件
                    do {
                        ++firstIndex;
                    }while(firstIndex < lastIndex && nums[firstIndex] == nums[firstIndex-1]);
                    do {
                        --lastIndex;
                    }while (firstIndex < lastIndex && nums[lastIndex] == nums[lastIndex+1]);
                }
            }
            //跳過重複的組合
            do{
                ++i;
            }while(i < nums.length && nums[i] == nums[i-1]);
        }

        return result;
    }

    private static void addAnswerToResult(int first, int last, int complement, List<List<Integer>> result) {
        List<Integer> answer = new ArrayList<>();
        answer.add(first);
        answer.add(last);
        answer.add(complement);

        result.add(answer);
    }
}

這個方法的複雜度是O(N2)。