算法 | 三数之和-题号15

70 阅读2分钟

跳转至LeetCode

题目描述

给你一个整数数组 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
  • -105 <= nums[i] <= 105

思路分析

回顾一下两数之和:

在两数之和里,我们的目标是找出数组中两个数相加之和等于target的两个数,顺着这个思路,其实三数和为0可以转换为:target = 0 - num3num1 + num2 = target

区别就是我们的target是可变的,数组中的每一项都是-target

所以是在两数之和的基础上在外层套一轮循环。

代码实现

先实现两数之和(数组有序,有且只有一个结果)

/**
 * @param {number[]} numbers
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (numbers, target) {
    const len = numbers.length
    let p1 = 0
    let p2 = len - 1
    while (p1 < p2) {
        const sum = numbers[p1] + numbers[p2]
        if (sum === target) {
            return [p1+1, p2+1]
        } else if (sum < target) {
            p1++
        } else if (sum > target) {
            p2--
        }
    }
};

实现三数之和需要注意的:

  • 不止有一个结果,所以找到一组匹配的之后,还需要继续遍历剩余的数
let res = []
while (p1 < p2) {
    const sum = numbers[p1] + numbers[p2]
    if (sum === target) {
        res.push([p1+1, p2+1])
        p1++
        p2--
    } else if (sum < target) {
        p1++
    } else if (sum > target) {
        p2--
    }
}
return res
  • target是可变的,是通过数组遍历而来,改造后结果
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    let sortNums = nums.sort((a,b) => a-b)
    let res = []
    for (let i = 0; i < sortNums.length - 2; i++) {
        // 如果当前值大于0,那后面不用执行了,因为不会有匹配的结果
        if (sortNums[i] > 0) break
        let matchRes = isMatch(sortNums, i)
        if (matchRes && matchRes.length) {
            res = res.concat(matchRes)
        } 
    }
    return res
};
var isMatch = function (arr, i) {
    let p1 = i+1
    let p2 = arr.length - 1
    let res = []
    while (p1 < p2) {
        let sum = arr[p1] + arr[p2]
        if (sum === -arr[i]) {
            res.push([arr[i], arr[p1], arr[p2]])
            p1++
            p2--
        } else if (sum < -arr[i]) {
            p1++
        } else {
            p2--
        }
    }
    return res
}

运行之后,发现有部分没有通过测试

image.png

分析原因,是没有做去重,如果连续两个数是一样的,那么就会再次执行,所以我们需要去重

 for (let i = 0; i < sortNums.length - 2; i++) {
    if (sortNums[i] > 0) break
    // 去重,因为已经是有序的,所以重复的肯定是连续的
    if (i > 0 && sortNums[i] === sortNums[i-1]) {
        continue
    } else {
        let matchRes = isMatch(sortNums, i)
        if (matchRes && matchRes.length) {
            res = res.concat(matchRes)
        } 
    }
    
}

再次运行是通过的,不过提交后还是有部分没有通过

image.png

分析发现,还是会有重复的情况,需要再次去重

if (sum === -arr[i]) {
    res.push([arr[i], arr[p1], arr[p2]])
    // 去重2 [-2,0,0,2,2]
    while (p1 < p2 && arr[p1] === arr[p1+1]) p1++
    while (p1 < p2 && arr[p2] === arr[p2-1]) p2--
    p1++
    p2--
} else if (sum < -arr[i]) {
    p1++
} else {
    p2--
}

提交后通过了。

完整代码

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    let sortNums = nums.sort((a,b) => a-b)
    let res = []
    for (let i = 0; i < sortNums.length - 2; i++) {
        if (sortNums[i] > 0) break
        if (i > 0 && sortNums[i] === sortNums[i-1]) {
            continue
        } else {
            let matchRes = isMatch(sortNums, i)
            if (matchRes && matchRes.length) {
                res = res.concat(matchRes)
            }
        }  
    }
    return res
};
var isMatch = function (arr, i) {
    let p1 = i+1
    let p2 = arr.length - 1
    let res = []
    while (p1 < p2) {
        let sum = arr[p1] + arr[p2]
        if (sum === -arr[i]) {
            res.push([arr[i], arr[p1], arr[p2]])
            // 去重2 [-2,0,0,2,2]
            while (p1 < p2 && arr[p1] === arr[p1+1]) p1++
            while (p1 < p2 && arr[p2] === arr[p2-1]) p2--
            p1++
            p2--
        } else if (sum < -arr[i]) {
            p1++
        } else {
            p2--
        }
    }
    return res
}