三数之和

描述

给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。
注意:
三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
解集中不能包含重复的三元组。
例如,给定的数组 S = {-10 0 10 20 -10 -40},解集为(-10, -10, 20),(-10, 0, 10)
0 <= S.length <= 1000

示例1

输入:
[0]

返回值:
[]

示例2

输入:
[-2,0,1,1,2]

返回值:
[[-2,0,2],[-2,1,1]]

示例3

输入:
[-10,0,10,20,-10,-40]

返回值:
[[-10,-10,20],[-10,0,10]]

思路

三数之和,最简单最暴力的解法就是 三重循环遍历,但是这样的时间复杂度太高了为O(n^3)。

大家看上图,其实咱们可以使用 三指针 的方式来解决这道题。

  • 将数组按照从小到大排序。
  • 先定义I、J、K 三指针,I 位于最左侧,J 位于 I 的右侧,K 位于 数组最右侧。
  • JK 分别向对方移动,当 I + J + K > 0 时,K 向左移动,反之 J 向右移动,沿途碰到符合 I + J + K = 0 记录下来。
  • JK 遍历完之后,I 向右移动,J 位于 I 的右侧,K 位于 数组最右侧。在重复上面的逻辑即可。
  • 这里面有个问题,那就是会有重复的组合,咱们遍历的时候如果碰到相邻元素相等时,可以直接跳过,这样能减少重复次数。
  • 还有一个跳出等其他细节,大家可以看下方代码注释。

AC 代码

public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<>();
            if (num == null || num.length < 3) {
                return res;
            }
            // 对数组从小到大排序
            Arrays.sort(num);
            // 如果 排序后第一个(最小的)的元素 大于 0, 那说明这个数组不可能有等于 0 的三个元素。
            if (num[0] > 0) {
                return res;
            }
            int length = num.length;
            for (int i = 0; i < length; i ++) {

                // 如果相邻的两个元素相等,则直接跳过,防止重复
                if (i > 0 && num[i] == num[i - 1]) {
                    continue;
                }
                // k 是右侧指针
                int k = length - 1;
                // 这个等价于 int temp = 0 - num[i], 确定第一个元素后,去剩下的队列中寻找另外两个元素
                int temp = - num[i];
                // 从第一个元素后边开始遍历
                for (int j = i + 1; j < length; j ++) {
                    // 这个和上面一样,碰到相邻元素相等就跳过
                    if (j > i + 1 && num[j] == num[j - 1]) {
                        continue;
                    }
                    // 当右侧指针小于左侧指针,并两者之和大于 temp 时,右侧指针要左移,因为是从小到大排序的
                    while (k > j && num[k] + num[j] > temp) {
                        k --;
                    }
                    // 当 两个指针重合时,说明 b + c > -a ==> a + b + c > 0
                    // 就算是在往后遍历也只是 a + b + c 越来越大,永远不会 等于 0,因为数组是从小到大有序的
                    if (k == j) {
                        break;
                    }
                    if (num[k] + num[j] == temp) {
                        ArrayList<Integer> list = new ArrayList<Integer>();
                        list.add(num[i]);
                        list.add(num[j]);
                        list.add(num[k]);
                        res.add(list);
                    }

                }
            }
        return res;
    }
  • 时间复杂度:时间复杂度:O(N^2),N 为数组长度
  • 空间复杂度:O(log^N),这个事排序的空间复杂度。

最后

大家可以去 【牛客网-题库-在线编程】去练习一下。
可以去微信搜索:【蘑菇睡不着】交个朋友~
也可以扫描下方二维码。

图片说明

#Java开发##学习路径#
全部评论
emm用hash map就可以把暴力的时间复杂度降到n^2。遍历每两个数,用hash map判断这两数和的相反数是不是存在就好了。
点赞 回复 分享
发布于 2021-09-16 21:29

相关推荐

今天老师给大家分享推荐算法3轮面经,供各位同学参考。1️⃣第一轮1、先自我介绍,我的习惯是经历简单介绍一下,然后自然转向准备最充分的一个项目开始详细讲,面试官感兴趣的话最好,不感兴趣的话会直接打断的。主要介绍了项目的背景,难点和解决方案,面试官关心的点主要集中在问题抽象和损失函数,讲清楚为什么这么做,项目大概聊了半小时左右2、机器学习基础:推导&nbsp;lr,写出loss和梯度(比起推导svm来说简直就是送分题,要是写不出来的话估计会直接挂,基础还是要好好准备)3、算法&nbsp;链表对折&nbsp;1&nbsp;2&nbsp;3&nbsp;4&nbsp;5&nbsp;变成&nbsp;1&nbsp;5&nbsp;2&nbsp;4&nbsp;3拆解一下题目,(灵活)找到链表的中点&nbsp;牛客题霸:&nbsp;链表中倒数第k个节点&nbsp;是找中点的复杂版,都是双指针解法翻转后半段链表&nbsp;牛客题霸:&nbsp;翻转链表合并两个链表&nbsp;牛客题霸:&nbsp;合并两个有序链表&nbsp;是复杂版2️⃣第二轮1、先介绍项目,主要聊了项目背景和收益,收益具体怎么衡量,项目如何上线生效2、算法题&nbsp;m*n的二维数组,只能往右或者往下,找最短路径,n空间&nbsp;牛客题霸:&nbsp;矩阵的最小路径和3、有了解过设计模式吗?(答了常见的工厂模式和单例模式,对应的应用场景,简单扯了一下装饰器模式,也是看xgb源码看到的,其实不会用)4、系统设计需要注意什么,如何设计一个系统,系统性能如何评估,需要考虑哪些指标(考察点应该是线上的系统了,指标比如内存使用率,qps,99&nbsp;39&nbsp;49时间之类的)5、之前帮阿里云录制过一些深度学习的入门课程,简单聊了一下相关的内容3️⃣第三轮1、先介绍项目,主要聊了项目背景和收益,收益具体怎么衡量,项目如何上线生效2、介绍xgbgbdt和xgb的区别(居然没有问lgb)怎么选最优分裂节点,怎么加速,预排序有什么作用,怎么分箱,等宽还是等深怎么处理缺失值的,预测时候缺失值怎么办3、为什么离职,希望一份什么样的工作4、有没有什么问题想要了解的(问了业务场景&nbsp;工作内容)📳对于想求职算法岗的同学,如果想参加高质量项目辅导,提升面试能力,欢迎后台联系。&nbsp;&nbsp;&nbsp;&nbsp;
查看9道真题和解析 简历中的项目经历要怎么写
点赞 评论 收藏
分享
评论
5
4
分享

创作者周榜

更多
牛客网
牛客企业服务