题目:三数之和

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

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

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

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

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

解题思路:双指针

首先题目要求结果不包含重复三元组,而在给出的nums中又可能出现重复的元素,因此可以先对nums进行一次排序,这样做的意义一是方便使用双指针,二是遍历中也方便跳过重复项

那么应该怎样找出三个相加为0的三元组呢?具体步骤如下:

  • 先找出一个基点,假设这个基点索引为inums长度为n,那么剩下2个与基点相加为0的元素就是在[i+1,i+2,.....,n-2,n-1]中寻找
  • 确定寻找范围后,就可以依靠双指针法,如下图: 0015-01.png 假设j=i+1,k=n-1,然后判断nums[i]+nums[j]+nums[k]的值是否为0,从上图可以看出-2-1+4>0,所以k值要往左移动,如下图: 0015-02.png k往左移动后nums[i]+nums[j]+nums[k]相加刚好为0,那么就可以把这个三元组放进结果数组中
  • 在找到基点的第一组三元数组后并不意味着开始前往下一个基点,相反当前基点nums[i]可能存在2个及2个以上三元数组,所以需要以当前基点寻找,如下图: 0015-03.png 在找出第一组三元数组后,j加一,k减一,这个时候nums[i]+nums[j]+nums[k]同样为0,如下图 0015-04.png
  • 最后只剩下一些边界条件的处理,比如:
    1.当基点大于0的时候,就代表不符合题目条件,直接返回结果
    2.当基点与前一个基点相同时,跳过本次循环
    3.如上面说到的基于当前基点继续寻找三元数组时,jk都要各自往左右移动,那么在左移或右移后有可能碰到相同项,那么就需要继续移动

代码实现

let threeSum = (nums) => {
    if (nums.length < 3) return []
    let sum,
        result = [];
    nums = nums.sort((a, b) => {
        return a - b
    });
    for (let i = 0; i < nums.length - 2; i++) {
        let a = nums[i]
        if (a > 0) return result;
        if (i > 0 && a === nums[i - 1]) continue;
        let j = i + 1, k = nums.length - 1;
        while (j < k) {
            let b = nums[j],
                c = nums[k],
                sum = a + b + c;
            if (sum === 0) {
                result.push([a, b, c]);
                j++;
                k--;
                while (nums[j] === b) {
                    j++
                }
                while (nums[k] === c) {
                    k--
                }
            } else if (sum > 0) {
                k--
            } else {
                j++
            }
        }
    }
    return result
};