leetcode-三数之和-15-排序+双指针

原题链接

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

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

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

输入:nums = []
输出:[]


思考点1:如何做到不重复

a. 因为三元组要求不重复,我们第一感觉就是要对数组进行升序排序,否则分析会产生困难。

比如 -4...-1...-4...-1...5。不排序的话,会有多种方式选择到-4,-1,而且去重也不好想办法。

b. 基于a,数组排序后,因为可能存在连续的相同数值,比如[-1,-1,-1,-1,2]。很明显,只有一个三元组[-1,-1,2]符合要求。四个-1元素选取哪两个呢?这引发出第二个思考点。

思考点2:连续相同的数值,如何选择
数组[-1, -1, -1, -1, 2]的符合条件的三元组是[-1, -1, 2], 那两个-1是四个-1中的哪两个呢?按照方便起见我们选取最前面的那两个-1。这其实就等价于下述要求:

假如我们约定枚举第一个选中元素的循环叫做第一层循环,枚举第二个选中元素的循环叫做第二层循环,枚举第三个选中元素的循环叫做第三层循环。

a. 当前循环层开始的数组下标是上一循环层选中元素的下标+1
b. 当前循环层遍历到的元素与前一元素相同时,舍弃。

拿上面的case来讲,假设第一个选中的元素selected1 = nums[0] = -1时,第二个选中的元素selected2则从nums[1]开始算起。如果第二个元素选择nums[1], 即selected2 = nums[1]=-1,则第三个元素就确定了,必须是2。从nums[2]开始找,直至找到元素2。

第二个元素能不能不选择nums[1]这个-1呢?而是nums[2]这个-1呢? 当然可以,但是算法实现就没有了逻辑了,我们基于b的假设来实现算法的。

另外要注意到,从左向右遍历selected2时,由于要求三数之和为0,所以第三个数自然也会从右向左递减,这个特点可以使用双指针来实现。

public List<List<Integer>> threeSum(int[] nums) {
        if (nums.length < 3) {
            return new ArrayList<>();
        }
        List<List<Integer>> ans = new ArrayList<>();
        Arrays.sort(nums);
        // first表示第一个选中的元素对应的位置
        for (int first = 0; first < nums.length - 2; first++) {
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            int target = -nums[first];
            // second表示第二个选中的元素对应的位置
            for (int second = first + 1; second < nums.length - 1; second++) {
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // third表示第三个选中的元素对应的位置
                // 这里可以优化,注意到每次枚举selected2时,selected3都从最右侧开始遍历
                // 这是没必要的。可以将third的定义移动到当前for循环的外面,原因就是双指针
                int third = nums.length - 1;
                if (nums[second] + nums[third] < target) {
                    continue;
                }

                while (nums[second] + nums[third] > target && third > second) {
                    third--;
                }

                if (nums[second] + nums[third] == target && third > second) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[first]);
                    list.add(nums[second]);
                    list.add(nums[third]);
                    ans.add(list);
                }
            }
        }
        return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值