【LeetCode】15、三数之和为0

题目等级:3Sum(Medium)

题目描述:

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:

The 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]
]

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


解题思路:

  看到这个题目,我们首先会联想到LeetCode的第一个题:两数之和:【LeetCode】1、Two Sum

  两数之和比较简单,解法是利用了HashMap的查找优势,空间换时间将时间复杂度降到了O(n),这里是三数之和,当然可以类比,我们很容易想到可以先固定一个元素a,由于a+b+c=0,那么b+c=-a,所以就可以将三数之和转化为两数之和的问题

  而两数之和的问题我们已经解决,这样就得到本题的解答,然而,在实现这种思路的过程中,发现一个稍微有些棘手的问题是如何去重复,另外这样做的时间复杂度当然为O(n^2),额外空间复杂度是O(n)。

  然后又在网上参考了一些其他人的解法,发现HashMap似乎并不是最好的解法,所以找到了另外一种解法。

  思路如下:同样是先固定一个元素a,找b+c=-a,而最优美的地方就在于如何找着另外两个数b和c,这里不用HashMap,而是先将整个数组进行排序(排序时间复杂度O(nlogn)),然后可以利用前后双指针解法去找这两个元素。

  这样做的好处有两个:一是不需要额外的空间,二是由于已经有序,去重简单。

  代码实现的过程中,重点还是要关注一下如何去掉重复的情况,具体看代码。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res=new ArrayList<>();
        if(nums==null||nums.length==0)
            return res;
        int len=nums.length;
        Arrays.sort(nums);  //第一步:先排序
        
        for(int i=0;i<len-2;i++){ //依次遍历每一个元素,把它作为a,在后续的元素里找b+c=-a;
            if(i>0 && nums[i]==nums[i-1])
                continue;     //重复元素直接跳过
            //找两数之和为-a,双指针法
            int low=i+1,high=len-1;
            while(low<high){
                if(nums[low]+nums[high]==-nums[i]){ //找到了一个解
                    res.add(Arrays.asList(nums[i],nums[low],nums[high]));
                    //已经找到了,重复的去掉
                    while(low<high && nums[low+1]==nums[low])
                        low++;
                    while(low<high && nums[high-1]==nums[high])
                        high--;
                    low++;
                    high--;
                }else if(nums[low]+nums[high]<-nums[i])
                    low++;
                else
                    high--;
            }
        } 
        return res;
    }
}

  时间复杂度:O(n^2),没有使用额外空间

总结

  总结来说,本题的一个巧妙之处就是前后向两个指针同时遍历,实际上回顾一下,可以发现这种思想之前已经用过了:在《剑指Offer》第42题找和为S的两个数字使用的正是这样的解法:【剑指Offer】42、和为S的两个数字,但是这种思想只有在有序的情况下才能使用。

猜你喜欢

转载自www.cnblogs.com/gzshan/p/11127130.html