leetcode不定期刷题---15.三数之和

比特启航
• 阅读 861

15.三数之和

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/probl...

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

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

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:

[
  [-1, 0, 1],
  [-1, -1, 2]
]

题解:

看到这一题,一般的第一反应都是暴力法,三层循环嵌套,二话不说就是玩命给他干出来。

  let nums =  [-1, 0, 1, 2, -1, -4];
  var threeSum = function(nums) {
    let arr = [];
    for(let i = 0; i < nums.length-1;i++){
      for(let j = i+1;j<nums.length;j++){
        for(let m = j+1;m<nums.length;m++){
          if(nums[i]+nums[j]+nums[m] === 0){
            arr.push([nums[i],nums[j],nums[m]])
          }
        }
      }
    }
    return arr;
  };
  
  console.log(threeSum(nums));

但是呢,这个方法虽然简单粗暴,但是存在着很大的问题,第一点就是当数据量特别大的时候,会导致超时。第二点就是存在着重复的问题,得出来的结果需要去重。第三点,时间复杂度O(n^3)。

所以呢,必须要找一个更简单的方法。

我的思路跟题解中的第一个大佬是差不多的,只不过没有那么打篮球聚会什么的修饰。

首先把这条题简化成在一个数组中寻找两个数字和为某个值的所有可能。那么大家第一时间也会用暴力法来做,如下:

let nums =  [-1, 0, 1, 2, -1, -4];
  var twoSum = function (nums, sum) {
    let arr = [];
    for (let i = 0; i < nums.length - 1; i++) {
      for (let j = i + 1; j < nums.length; j++) {
        if (nums[i]+nums[j] === sum) {
          arr.push([nums[i], nums[j]])
        }
      }
    }
    return arr;
  };
  console.log(twoSum(nums,1));//[[-1,2],[0,1],[2,-1]]

这里就会出现重复的问题,那么我们就可以结合前一次我们学习的双指针的方法来算这一题。在数组首尾放上一个指针,进行相加,然后与结果比较,移动相应的指针。如下:

let nums =  [-1, 0, 1, 2, -1, -4];
  var twoSum = function (nums, sum) {
    let arr = [];
    let left = 0,right = nums.length-1;
    while(left < right){
      if(nums[left]+nums[right]>sum){
        nums[left]>nums[right] ? left++ : right--;
      }else if(nums[left]+nums[right]<sum){
        nums[left]<nums[right] ? left++ : right--;
      }else{
        arr.push([nums[left],nums[right]]);
        nums[left]<nums[right] ? left++ : right--;
      }
    }
    return arr;
  };
  console.log(twoSum(nums,1));//[[-1,2],[0,1]]

这种情况就算是基本完成了,不过这里面会有一个重复比较的问题,只要存在相同的值,那么就会有这个问题。解决办法呢就是在最开始就将数组排序,从小到大排列,在比较完一次之后,移动指针的时候进行一个判断,如果移动完之后的值跟之前的值是一样的,那么继续移动指针,直到不能移动为止。数组从小到大排序之后,比较起来也更好判断移动哪根指针;

  let nums =  [-1, 0, 1, 2, -1, -4];
  var twoSum = function (nums, sum) {
    nums = nums.sort((a,b)=>{return a-b});
    console.log(nums);//[-4, -1, -1, 0, 1, 2]
    let arr = [];
    let left = 0,right = nums.length-1;
    while(left < right){
      if(nums[left]+nums[right]>sum){
        //大于和值的时候,右边right值为大的数,移动一格,寻找更小的数
        do{--right}while(nums[right]===nums[right+1])
      }else if(nums[left]+nums[right] < sum){
        //小于和值的时候,左边left值为小的数,移动一格寻找更大的数
        do{++left}while(nums[left]===nums[left-1]);
      }else{
        //等于和值的时候,左右都移动一格,省去重复的值
        arr.push([nums[left],nums[right]]);
        do{--right}while(nums[right]===nums[right+1]);
        do{++left}while(nums[left]===nums[left-1])
      }
    }
    return arr;
  };
  console.log(twoSum(nums,1));//[[-1,2],[0,1]]

就这样,数组中两数和为某个值的问题就迎刃而解了。那么我们现在是三个值的和,其实这个反而没有那么复杂。

对数组进行一次循环,每个值为nums[i],然后在剩下的值里面寻找和为0-nums[i]的所有可能。同时为了防止重复操作,在循环的时候,前后值相等时候,直接跳过。

而且在最外层循环的时候,因为数组已经从小到大排序过了,所以当循环到大于0的值的时候,直接进行下一次循环。最终的代码如下:

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    nums = nums.sort((a,b)=>{return a-b});
    let arr = [];
    for(let i = 0; i < nums.length;i++){
      if(nums[i]>0 || nums[i] === nums[i-1])continue;
      let sum = 0 - nums[i];
      let left = i+1,right = nums.length-1;
      while(left < right){
       if(nums[left]+nums[right]>sum){
          //大于和值的时候,右边right值为大的数,移动一格,寻找更小的数
          do{--right}while(nums[right]===nums[right+1])
        }else if(nums[left]+nums[right] < sum){
          //小于和值的时候,左边left值为小的数,移动一格寻找更大的数
          do{++left}while(nums[left]===nums[left-1]);
        }else{
          // console.log(nums[i], nums[left], nums[right]);
          //等于和值的时候,左右都移动一格,省去重复的值
          arr.push([nums[i],nums[left],nums[right]]);
          do{--right}while(nums[right]===nums[right+1]);
          do{++left}while(nums[left]===nums[left-1])
        }
      }
    }
    return arr;
  };

执行结果:

执行用时 :192 ms, 在所有 JavaScript 提交中击败了95.77%的用户

内存消耗 :46.3 MB, 在所有 JavaScript 提交中击败了86.32%的用户

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
PPDB:今晚老齐直播
【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。
Stella981 Stella981
3年前
Leetcode Index
序:  用于记录刷题过程中难度较大或思路怪异的题目,对于常规难度一般的题就不写入我的博客了,关于效率仅是提交时击败的百分比,可能会随时间波动,仅供参考,算法优劣以时间复杂度和空间复杂度为基准。欢迎留言讨论。题目序号难度效率Leetcode42.TrappingRainWater(https://www.oschina.
Stella981 Stella981
3年前
2021年全球公有云终端用户支出将增长18% ;EMNLP 2020最佳论文:无声语音的数字发声
!(https://static001.geekbang.org/infoq/af/af9f6637b50b09be60b00a42f3812d5e.png)开发者社区技术周刊又和大家见面
可莉 可莉
3年前
2021年全球公有云终端用户支出将增长18% ;EMNLP 2020最佳论文:无声语音的数字发声
!(https://static001.geekbang.org/infoq/af/af9f6637b50b09be60b00a42f3812d5e.png)开发者社区技术周刊又和大家见面
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
贾蔷 贾蔷
3个月前
力扣1137题 解题思路和步骤 C++代码实现,力扣一共多少题
一、题目分析力扣1137题要求我们找到第N个泰波那契数。泰波那契数的定义是:T00,T11,T21,且在n0的条件下Tn3TnTn1Tn2。,当n4时,T4T3T2T14。这道题主要考查我们对递归或动态规划的理解和运用。在思考解题方法时,我们
贾蔷 贾蔷
4星期前
力扣15题三数之和解法(C++双指针算法详解)
一、题目解读15题()要求在一个包含n个整数的中,找出所有三个数之和为0的组合,且每个组合的元素不能重复。题目考察数组遍历、与技巧的结合,是经典的多问题,对时间复杂度优化有较高要求。二、解题思路采用“双指针”策略:首先对原数组排序,然后固定第一个数,通过左