『力扣题解』三数之和系列

87 阅读1分钟

15. 三数之和

核心的思路是遍历一轮数组,下标为i,然后使用双指针法,l从i的右侧出发,r从数组最右侧出发,根据此时三数之和,移动l和r。本题的关键问题在于去除重复的结果。

先对数组排序,遍历数组时当 nums[i] === nums[i - 1]时跳过此轮循环,这种方式常常用于避免计算重复的结果。同理,在双指针移动时也可能遇到重复结果。

比如说[-4, -1, -1, 0, 1, 2],i=1时,l=3,r=4,-1+0+1=0;紧接着i=2时,如果不做去重,还会有[-1, 0, 1]。

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
  const ans = [];
  const len = nums.length;
  if (len < 3) return ans;
  nums.sort((a, b) => a - b);

  for (let i = 0; i < len - 2; i++) {
    // 本题的target=0,如果最左边的数>0,直接剪枝
    if (nums[i] > 0) return ans;
    
    // 去重
    if (i > 0 && nums[i] === nums[i - 1]) continue;

    let l = i + 1,
      r = len - 1;
    while (l < r) {
      const sum = nums[i] + nums[l] + nums[r];

      if (sum > 0) r--;
      else if (sum < 0) l++;
      else {
        ans.push([nums[i], nums[l], nums[r]]);
        // 去重
        while (l < r && nums[l] === nums[l + 1]) l++;
        while (l < r && nums[r] === nums[r - 1]) r--;
        l++, r--;
      }
    }
  }
  return ans;
};

18. 四数之和

基本上和三数之和一样,就是需要多一层循环去枚举j,而且还需要考虑j的去重问题。

比如 [-2, -1, -1, -1, 0, 1, 2], target = -2。

如果i=0,j=1,l=4,r=5,是可以的。但是j=2也可以,这样就会造成重复,所以还是要用上面提到的方式去重,即nums[j] = nums[j - 1]就continue。

还需要注意,j-1可能等于i,此时不应该continue,比如[2, 2, 2, 2],target = 8,显然四个数都用到了,不能因为i=0,j=1,且nums[i]=nums[j]=2,就让j=3。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function (nums, target) {
  const len = nums.length;
  const ans = [];
  if (len < 4) return ans;

  nums.sort((a, b) => a - b);
  for (let i = 0; i < len - 3; i++) {
    if (nums[i] > target / 4) return ans;
    if (i > 0 && nums[i] === nums[i - 1]) continue;

    for (let j = i + 1; j < len - 2; j++) {
      if (j !== i + 1 && nums[j] === nums[j - 1]) continue;
      let l = j + 1,
        r = len - 1;
      while (l < r) {
        const sum = nums[i] + nums[j] + nums[l] + nums[r];
        if (sum > target) r--;
        else if (sum < target) l++;
        else {
          ans.push([nums[i], nums[j], nums[l], nums[r]]);
          while (l < r && nums[l] === nums[l + 1]) l++;
          while (l < r && nums[r] === nums[r - 1]) r--;
          l++, r--;
        }
      }
    }
  }
  return ans;
};