个人记录-LeetCode 15. 3Sum

来源:互联网 发布:mmd r18动作数据 编辑:程序博客网 时间:2024/05/16 07:01

问题:
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)。

2、类似于求最大水位的问题
个人记录-LeetCode 11. Container With Most Water

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)。

0 0