LeetCode 15 3sum 三数之和

题目链接

https://leetcode-cn.com/problems/3sum/

题意

        很简单,就是给出一个数组,3个数一组,找到所有和为0的组。并且要求不能重复。或者说找其中3个数其和为0,找出所有的组合。

题解

        很好的一个题了,博主做了3次,前两次全部TLE,最后才AC了,但是整体思路差不多,就看能不能想到双指针的操作。前两次就是太菜了。

        先说一个比较简单的思路,双指针的思路也有很大程度的重合,也是博主前两次TLE的思路:直接爆搜+剪枝。首先数组是要排序的,那么我们从最小的数开始暴搜,作为第一个数。然后搜第二个、第三个。如果不剪枝的话绝壁是超时的。比较容易想到的剪枝就是和如果大于0的时候可以直接剪掉了。我们可以当nums[i] > 0、nums[i]+nums[j] > 0分别剪去。然后就是第三个数k的范围。直接O(N^3)的搜索肯定不行了,那么k应该至少从0开始。因为i与j两者的和必须要<=0才有意义,否则就不存在第三个数了。所以可以预先搜索0或者第一个大于0的数,作为k的开始。预先搜索也略坑,我们直接把k当最大值反向搜就可以了。

        其次,一个坑就是不能要求重复。如果我们直接搜而不考虑去重的情况会这样,例如数据:

-1,-1,0,1

那么会出现两组[-1,0,1]这就是重复了。因为我们已经排序,所以去重的方法也非常简单,例如i,当i > 0时,如果nums[i] = nums[i-1],我们可以continue,跳过这次搜索,直接i++。这样就完成了去重。不能利用set去重,这样就把重复的数变成一个数了,对于:

-1,-1,2

这种类似的数据而言是非常致命的,至少少算了一组。

        上面就是博主TLE两次的算法了,很暴力。实际上这样的算法时间复杂度可以说是O(N^3)了,虽然看起来剪枝减去了很多,但是TLE还是妥妥的。而用双指针的话就能将复杂度减为O(n^2),并且仍然能使用上面的剪枝。仍然是遍历i,但是j与k设定为两个指针,j = i+1,k = len-1。这样,如果三数的和为0,那么就不多说了,j++以及k--,同时加入解集。当三数的和大于0,说明k应该减小,小于0则是j增大。这样就能在O(n^2)内实现找到所有解。

        当然,对于i、j仍然可以判断大于0来进行一次剪枝。同时也应该考虑去重的情况。

Java 代码

import java.util.*;

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        int len = nums.length;
        if(len < 2) return ans;
        Arrays.sort(nums);
        for(int i = 0;i < len-2;i++){
            if(nums[i] > 0) break;
            if(i > 0){
                if(nums[i] == nums[i-1]) continue;
            }
            int j = i+1;
            int k = len-1;
            while(j < k){
                if(nums[i] + nums[j] + nums[k] == 0){
                    List<Integer> ad = new ArrayList<>();
                    ad.add(nums[i]);
                    ad.add(nums[j]);
                    ad.add(nums[k]);
                    ans.add(ad);
                    k--;
                    while(k > j && nums[k] == nums[k+1]) k--;
                    j++;
                    while(j < k && nums[j] == nums[j-1]) j++;
                }else{
                    if(nums[i] + nums[j] + nums[k] > 0){
                        k--;
                    }else{
                        j++;
                    }
                }
            }
        }
        return ans;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值