LeetCode 数据结构基础 - 三数之和

184 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

三数之和

原题地址

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

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

示例 1:

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

示例 2:

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

示例 3:

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

思路分析

方法一

  1. 看到题目想到的最粗暴的方法就是三层循环,各取一个数字进行相加,判断其和是否为 0
  2. 因为题目要求三元组不能重复,因此需要定义一个辅助数组 help
  3. 判断条件为三数的和为 0,以及在辅助数组 help 中不存在;
  4. 因为经历了三层循环,在执行到数组具有大量元素时,超出时间限制;
  5. 此方法行不通,看方法二。

方法二

  1. 现将数组排序,方便定义边界值和边界条件;
  2. 定义左右边界值,left = i + 1right = nums.length - 1;
  3. 遍历数组, 以 left < right 条件进行 while 循环 「 left > right 则三数相加不可能为零」;
  4. (nums[left] + nums[i] + nums[right]) === 0 则将 [nums[left] + nums[i] + nums[right]] 放入结果数组 res 中,同时给左右边界去重;
  5. (nums[left] + nums[i] + nums[right]) > 0,则证明右边界过大,需要左移,即 right-- ;
  6. 否则,则证明左边界过小,需要右移,即 left++

AC 代码

方法一

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    const res = []
    const help = []
    const len = nums.length
    for(let i = 0; i < len; i++) {
        for(let j = i+1;j < len;j++) {
            for(let k = j+1; k < len; k++) {
                if(nums[i] + nums[j] + nums[k] === 0 && !help.includes([nums[i], nums[j], nums[k]].sort().join())) {
                    res.push([nums[i], nums[j], nums[k]])
                    help.push([nums[i], nums[j], nums[k]].sort().join())
                }
            }
        }
    }
    return res
};

结果:

  • 315 / 318 个通过测试用例
  • 超出时间限制

方法二

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
const threeSum = function (nums) {
  nums.sort((a, b) => a - b)
  let res = []
  
  for (let i = 0; i < nums.length; i++) { 
    let left = i + 1
    let right = nums.length - 1

    if (nums[i] > 0) break

    if (i === 0 || nums[i] !== nums[i - 1]) { 
      while (left < right) {
        if ((nums[left] + nums[i] + nums[right]) === 0) {
          res.push([nums[left], nums[i], nums[right]])
          while (left < right && nums[left] === nums[left + 1]) left++ 
          while (left < right && nums[right] === nums[right - 1]) right-- 
          left++
          right--
        } else if ((nums[left] + nums[i] + nums[right]) > 0) {
          right--
        } else left++
      }
    }
  }
  return res
}

结果:

  • 执行结果: 通过
  • 执行用时:128 ms, 在所有 JavaScript 提交中击败了72.66%的用户
  • 内存消耗:50.5 MB, 在所有 JavaScript 提交中击败了64.53%的用户
  • 通过测试用例:318 / 318

END