力扣15题:三数之和Java解法 逐行精讲 【排序+双指针】

题目描述:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

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

示例2
输入:nums = []
输出:[]

示例3
输入:nums = [0]
输出:[]

解决方案:排序+双指针法

难点

本题目的难点,在于如何去重,利用排序+双指针可以实现一个巧妙地去重方式,比暴力穷举法易于理解

解题思路

举个生动形象中的例子,假设一场3v3的游戏,要求是三个人,并且平均战斗力是1000才可以参赛,我现在假设自己的战斗力刚好是1000,假设要找的队友分别是A和B,那样子A和B的平均战力就要在1000,否则无法参赛。
那只有两种情况。

  1. 队友A战力低于1000,队友B的战力高于1000。
  2. 队友A和B的战斗力都是1000

画个图说明一下
在这里插入图片描述
如图所示,我们从1开始寻找队友 找到5结束 所以结束循环的条件 应该是左指针小于右指针 否则就代表当前这一遍 都循环完了。

整理一下思路:
第一步:可以对已有的数组作一个排序,保证数组升序排序,由左至右逐渐增大。为了后续操作做准备。
第二步:创建左右两个指针,循环数组,将i的位置的值,当作是我们自己,左右指针分别指向两个队友(两个数)。
第三步:每一次都做验证,三个值相加,看一下三个值相加的结果到底是大了还是小了。
第四步:移动指针,由于目前数组已经有序了,如果值大于0 则大的数小一些即可,右指针左移,反之小于0左指针右移。
注意:由题可知该题答案不唯一,所以三数之和等于零,一定不能结束循环,左右指针都移动一下,继续寻找,说不定还可以找到剩下的正确的答案!

上代码:

 // i代表的是我们当前值 leftPoint代表的是我们小的值 rightPoint代表的是我们比较大的值
 public List<List<Integer>> threeSum2(int[] nums) {
		// 数组长度
        int n = nums.length;
		// 保存答案 每个值都是一个Integer的集合
        List<List<Integer>> result = new ArrayList<>();

        // 排序数组
        Arrays.sort(nums);

        for (int i = 0; i < n; i++) {
            // 由于数组已经排序了 i指针被定义为最小的数字 如果nums当前的值大于0 则可以退出循环了
            // 举个例子:i = 3 则代表当前数组nums : [3,x,x,x,x] 所有值都大于3 所以不可能存在正确答案
            if (nums[i] > 0) break;

            // 如果i指针的数据 和上一个重复 则无需遍历当前次 没有任何意义
            if (i > 0 && nums[i] == nums[i - 1]) continue;

            // 定义左右指针 左指针为i指针的下一个值 右指针为数组的数量-1 即最后一个数字
            int leftPoint = i + 1;  // 左指针为i+1的原因是 由题意可知 和自己做运算没有意义
            int rightPoint = n - 1; // 右指针为n-1 就是循环完全部数组

            // 左指针不覆盖右指针 并且不等于右指针 即左指针 < 右指针才是有效情况
            while (leftPoint < rightPoint) {
                // 取三指针之和
                int num = nums[i] + nums[leftPoint] + nums[rightPoint];
                // 如果值大于0 则代表需要小一些 即右指针前移
                // 如果值小于0 则代表需要大一些 即左指针后移
                // 如果值等于0 则代表找到正确答案 双指针分别移动一位
                if (num == 0) {
                    // 找到了正确的答案 将值添加至结果中
                    result.add(Arrays.asList(nums[i], nums[leftPoint], nums[rightPoint]));
                    // 移动双指针
                    leftPoint++;
                    rightPoint--;
                    // 如果移动前后的值是相同的 则跳过 
                    // 这里是为了提高效率 相同的数据在这里没有任何意义 所以直接移动就可以了
                    while (leftPoint < rightPoint && nums[leftPoint] == nums[leftPoint - 1]) leftPoint++;
                    while (leftPoint < rightPoint && nums[rightPoint] == nums[rightPoint + 1]) rightPoint--;
                } else if (num < 0)
                    leftPoint++;
                else
                    rightPoint--;
            }
        }
        // 返回结果
        return result;
    }

作者:cgadmin544
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Leetcode上 cgadmin544 的就是我自己 所以标注原创了哈 如有版权问题,请及时联系我处理哈

复杂度分析
我们程序中使用了一个for循环加若干个while,但是我们会发现while循环的整体次数 其实就是n的次数,所以时间复杂度应该是O(N)。
空间复杂度:只使用了几个固定的值,如n,左指针,右指针等 所以空间复杂度是O(1)


作者的话

开本专栏的目的其实是为了自己的大厂梦吧! 也算为了让大家见证我这个小菜鸡的逐渐成长! 如果本文章可以帮助到您一点点,本人万分荣幸!! 如有任何问题请给我留言,我会逐一回复的哈。

全面发展,一专多能!!! 冲冲冲

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值