[JavaScript / leetcode] 15. 三数之和

132 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第26天,点击查看活动详情

每日刷题 2022.10.27

题目

  • 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
  • 你返回所有和为 0 且不重复的三元组。
  • 注意:答案中不可以包含重复的三元组。

示例

  • 示例1
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1][-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
  • 示例2
输入: nums = [0,1,1]
输出: []
解释: 唯一可能的三元组和不为 0 。
  • 示例3
输入: nums = [0,0,0]
输出: [[0,0,0]]
解释: 唯一可能的三元组和为 0

提示

  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^5

解题思路

  • 三数之和,因为题目中要求了:不能包含重复的三元组,因此需要有序。
  • 双指针+排序

总结提炼

  • 从数组中取n个数,和为m。(数组的长度一定是大于n的)
  • 这个时候就不太能使用固定一个数,找剩下的数的方法了;因为现在是找n个数。
  • 遇到类似的n个,就可以枚举,如果选上当前的元素 或者 不选当前的元素的情况,对最终的结果的影响是什么?由此可以想到:使用dfs来解决问题。

dfs解法

  • dfs的深度就是:数组的长度lensum表示:选中的数值之和,用于判断最终的和是否等于mselect表示:当前被选中的值的个数(优化:当选中的值的个数 > n,那么就可以直接淘汰掉这种情况,不符合);tar表示:找到的符合要求的数组组成的集合。
  • dfs的每一层都需要先判断:当前是否已经将数组中的每一个值的情况都考虑到了,如果都考虑到了,即:idx === len,那么就判断当前是否符合n和m的要求,将符合要求的放入到最终的答案结果中。
  • 紧接着就需要判断当前层的值,选 or 不选,不选的时候,直接dfs下一层;选的时候需要将当前选中的值select放入到tar中,接着dfs下一层,返回回来的时候,需要将其还原回原来的样子(也就是将selecttar中取出来)。

AC代码

  // 选出n个数,和为m, 找到符合的就返回
  let arr = nums, m = 0, n = 3, len = arr.length, ans = [], flag = false;
  // 参数表示:select选择的个数, sum当前被选中的总和, idx下标, tar符合要求的数组成的集合 
  dfs(0, 0, 0, []);
  return ans;

  function dfs(select, sum, idx, tar) {
    if(idx === len) {
      // 此时需要比较是否符合n个,且和为m
      if(select === n && sum === m) {
        // 记录下来
        ans.push([...tar])
      }
      return;
    }
    // 如果当前选中的数,已经超过了n, 就可以直接返回
    if(select > n) return;
    // 不选
    dfs(select, sum, idx + 1, tar, flag);
    if(flag) return;
    // 选
    let cur = arr[idx];
    // 将当前的值插入到符合要求的数组中
    tar.push(cur);
    dfs(select + 1, sum + cur, idx + 1, tar, flag);
    if(flag) return;
    // 回溯,还原回原本的样子
    tar.pop();
  }
};