题目描述
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != 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 - num3,num1 + 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
}
运行之后,发现有部分没有通过测试
分析原因,是没有做去重,如果连续两个数是一样的,那么就会再次执行,所以我们需要去重
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)
}
}
}
再次运行是通过的,不过提交后还是有部分没有通过
分析发现,还是会有重复的情况,需要再次去重
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
}