Leetcode 15. 三数之和 Java & Python

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

这道题的思路和两数之和类似,只不过难度更胜一筹,还是同样的配方,没有思路的时候不妨尝试暴力解法

public List<List<Integer>> threeSum(int[] nums) {

        List<List<Integer>> res = new ArrayList<>();

        for (int i = 0; i < nums.length; i++) {
            int first = -nums[i];
            for (int j = 0; j < nums.length; j++) {
                if (i != j) {
                    int third = first - nums[j];
                    for (int k = 0; k < nums.length; k++) {
                        if (k != i && k != j && nums[k] == third) {
                            List<Integer> list = new ArrayList<>();
                            list.add(nums[i]);
                            list.add(nums[j]);
                            list.add(third);
                            Collections.sort(list);
                            if (!res.contains(list)) {
                                res.add(list);
                            }
                        }
                    }
                }
            }
        }

        return res;
    }

这样在我们自己的IDE里和数据量小的时候,可以得到正确解答,但是数据量一大便会timeout,因为这是一个O(n3)的算法,时间开销非常大。在有了这个方法之后,我们怎么来优化呢?从里面的第二层循环开始,发现和两数之和有着出奇的相似,但是两数之和是有序数组,这里是无序的,我们不妨先将数组排序,在采用二分查找O(nlogn + n2logn)或者对撞指针O(nlogn  + n2)来解决问题,下面是对撞指针的解决方案,同时,这道题目里,一定注意注释给出的几个优化点,避免重复!!!

public List<List<Integer>> threeSum(int[] nums) {

        List<List<Integer>> res = new ArrayList<>();

        if (nums == null || nums.length < 3)
            return res;

        Arrays.sort(nums);

        for (int i = 0; i < nums.length; i++) {
            //如果num[i] > 0 再加两个比其大的数 不可能等于0 至此结束循环
            if (nums[i] > 0) {
                break;
            }

            //避免重复
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }

            int target = -nums[i];
            int left = i + 1;
            int right = nums.length - 1;

            //说明第一个数选小了 重新选第一个数 不必在循环
            if (nums[i] + nums[right] + nums[right - 1] < 0) {
                continue;
            }

            while (left < right) {
                if (nums[left] + nums[right] == target) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    res.add(list);
                    left++;
                    right--;
                    //避免重复
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }
                } else if (nums[left] + nums[right] < target) {
                    left++;
                    //避免重复
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                } else {
                    right--;
                    //避免重复
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }
                }
            }
        }

        return res;
    }

上面的代码实际上是一个O(n2)级别的算法,提交给Leetcode后便可以AC了,但是笔者将其改写成Python代码后,发现这样一个O(n2)的算法还是会timeout,如果有小伙伴可以优化下面的Python代码或者其他思路,欢迎一起交流分享

    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res = []

        if len(nums) < 3:
            return res

        nums.sort()

        for i in range(len(nums)):

            if nums[i] > 0:
                break

            if i > 0 and nums[i] == nums[i - 1]:
                continue

            target = -nums[i]
            left = i + 1
            right = len(nums) - 1

            if nums[i] + nums[right] + nums[right - 1] < 0:
                continue

            while left < right:
                if nums[left] + nums[right] == target:
                    res.append([nums[i], nums[left], nums[right]])

                    left += 1
                    right -= 1

                    while left < right and nums[left] == nums[left - 1]:
                        left += 1
                    while left < right and nums[right] == nums[right + 1]:
                        right -= 1

                elif nums[left] + nums[right] < target:
                    left += 1
                    while left < right and nums[left] == nums[left - 1]:
                        left += 1

                else:
                    right -= 1
                    while left < right and nums[right] == nums[right + 1]:
                        right -= 1

        return res

猜你喜欢

转载自blog.csdn.net/sinat_33150417/article/details/83016997