Keep Learning

永远年轻,永远热泪盈眶

LeetCode 15. 3Sum

题目描述

  • Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
  • Note:
    he solution set must not contain duplicate triplets.
  • Example:

    Given array nums = [-1, 0, 1, 2, -1, -4],
    A solution set is:
    [
    [-1, 0, 1],
    [-1, -1, 2]
    ]

  • 地址

问题分析

  • 该题是 Leetcode-1. Two Sum 以及 Leetcode-264. Two Sum II - Input array is sorted 进阶题目。做法也类似。
  • 该题因为是求三个数累加和为 0的所有三元组,那么我们先对数组进行排序。然后先固定一个元素 nums[i] ,然后在 nums[i + 1...nums.length - 1] 范围内利用 Two Sum 中的双指针做法,来查找两数之和等于 0 - nums[i]的两个数字即可。
  • 但是,这题真正的难点在于,数组若含有重复元素,那么可能存在多个三元组满足条件题目要求找到的三元组必须不能重复。例如对于排序后的数组 nums = [-4,-1,-1,-1,0,1,1,2],最终只能是一个三元组 [-1,0,1]
  • 对于防止三元组重复的方法具体如下:
    • 若当前nums[i] 等于上一个 nums[i - 1] ,因为符合结果的三元组肯定都已经被 nums[i - 1] 时找到,所以对于nums[i] ,便可以直接跳过。
    • 若当前已经找到一个三元组,比如是nums[i], nums[left], nums[right]那么在当前固定 nums[i] 的情况下,便应该不再寻找与nums[left],nums[right]相同的数,所以应该利用循环跳过两头所有与nums[left],nums[right]相同的数字,这样才能不重复。
  • 优化
    若当前nums[i] > 0,那么因为该数组已经排序,那么他与[i + 1...length-1]内任意两个数字都不可能加出 0,所以直接 break。
  • 复杂度: O(N^2)

经验教训

  • 如何从 Two Sum 到 3Sum
  • 如何防止重复。

代码实现

    public List<List<Integer>> threeSum(int[] nums) {
        if (nums == null || nums.length == 0) {
            return new ArrayList<List<Integer>>();
        }
        List<List<Integer>> res = new ArrayList<>();
        int length = nums.length;
        //排序
        Arrays.sort(nums);
        for (int i = 0; i < nums.length && nums[i] <= 0; i++) { //因为已排序,所以当nums[i] > 0 之后,和后序元素相加一定 > 0
            //如果当前元素与上一元素相同,那么跳过该元素
            if (i != 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            //固定好 nums[i] 之后,利用双指针在[i + 1...nums.length - 1]中寻找和为 0-nums[i] 的两个元素
            int left = i + 1;
            int right = nums.length - 1;
            while (left < right) {
                if (nums[left] + nums[right] == 0 - nums[i]) {//找到一组
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[left++]);
                    list.add(nums[right--]);
                    res.add(list);
                    //左右两边都跳过与当前重复的数字
                    while ( left < right && nums[left] == nums[left - 1]) {
                        ++left;
                    }
                    while (left < right && nums[right] == nums[right + 1]) {
                        --right;
                    }
                }else if (nums[left] + nums[right] < 0 - nums[i]){
                    ++left;
                    //下一句也是跳过重复数字,但是当该数组没有大量重复时,相反循环中的比较操作会拖慢速度
                    /*
                    while (left < right && nums[left] == nums[left - 1]) {
                        ++left;
                    }
                    */
                }else {
                    --right;
                    /*
                    while (left < right && nums[right] == nums[right + 1]) {
                        --right;
                    }
                    */
                }
            }
        }
        return res;
    }
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zjxxyz123/article/details/79947507
个人分类: LeetCode
上一篇LeetCode 451. Sort Characters By Frequency
下一篇今日头条2018春招编程题-第二场
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭