【前端面试常见算法题系列】15. 三数之和

129 阅读1分钟

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

一、题目描述

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

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

二、思路分析

这道题是两数之和的进阶版,做过那道题的小伙伴一定知道,使用双指针技巧可以完美解决,这道题当然也是如此。

我们想找和为 target 的三个数字,那么借鉴两数之和的题解,我们可以想到:先找第一个元素,然后再找和为 target-第一个元素 的两个数字(其实这就是找两数之和)。
那么,第一个元素该怎么确定呢,只要确定了第一个元素,剩下的就好办了,直接套用两数之和的题解即可。其实答案很简单,nums 中的每一个元素 nums[i] 都有可能是第一个元素。

想到这里,可以敲代码了,思路如下:

  • 先对 nums 排序,方便使用双指针。
  • 遍历 nums 数组,当前元素作为三元组的第一个元素,运用双指针技巧,左指针 L 为当前元素的下一个元素,右指针 Rnums 数组的最后一个元素。
  • L 小于 R 的前提下,在 while 循环中判断 sum = nums[i] + nums[L] + nums[R] 是否等于 0,如果小于 0L++ ,如果大于 0R-- ,如果等于 0 则将这三个数压进答案数组,并且 L++R-- (为了不让三元组重复,可以循环判断 nums[L] == nums[L+1] 或者 nums[R] == nums[R-1] ,符合的话就 L++R-- ,以此提高效率)。
  • 需要注意的是,如果 nums 数组长度小于 3 ,直接返回空数组即可。

三、AC 代码

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    let ans = [];
    const len = nums.length;
    if(nums == null || len < 3) return ans;
    nums.sort((a, b) => a - b); // 排序
    for (let i = 0; i < len ; i++) {
        if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
        if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
        let L = i + 1;
        let R = len - 1;
        while(L < R){
            const sum = nums[i] + nums[L] + nums[R];
            if(sum == 0){
                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--;
            }
            else if (sum < 0) L++;
            else if (sum > 0) R--;
        }
    }        
    return ans;
};