题目描述
给你一个整数数组 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
分析解答
乍眼一看,求和问题。直接上哈希表来做。然后,去重?去重!百思不得其解,遂跟着代码随想录的解答学习了一番。
以下思路为代码随想录的解答。
思路拓展
1632108374-OljZRm-15.三数之和.gif (376×246) (leetcode-cn.com)
这道题主要使用双指针法。先将整个数组排好序(题干说明:输出的顺序和三元组的顺序并不重要,所以可以排序),i 为起始位置,l 左指针,r 右指针。目的是让 nums[i] + nums[left] + nums[right] = 0 ,i 是for 循环遍历的索引。
那么怎么满足这个条件呢?可以先分类讨论一下:
- 初始位置,如果和大于 0,那么 right--;如果小于 0, 那么 left++;直到和为 0
- 然而,因为我们是正序排列的数组,所以初始 i 如果大于0,那么直接返回 []
第一个问题解决了,然后我们开始去重。
首先抛出一个难点,此时我们遍历到第 i 个元素,想要去重,那是应该判断 i 和 i + 1 还是 i 和 i - 1 呢?(尽管他们乍眼一看没什么区别😥)
首先思考,[0, 0, 0]是满足条件的,他们内部相等是被允许的。也就是说 i 和 left 相等是允许的,也就是说 i 和 i + 1 是可以相等的,所以应该采用 i 和 i - 1 来去重。
开始 i -> 1位置, l -> 2位置,r -> 4位置 [-1, -1, 2] 合理。然后 i -> 2位置, l -> 3位置,r -> 4位置,[-1, -1, 2] 和之前的冲突,需要去重,这轮 i 循环直接 continue。
所以前面先对整个数组排序是多么重要,妙啊~~~
之后对 left 和 right 的去重都是同样的操作。
然后就是完整代码了,再理一遍思路:
- 数组排序,使用排序后的数组
- 第一次循环数组,i 是数组的每一个值,也是 arr 中可能的每个三元组的第一个值(最小值)的索引
- sortedNums[i](最小值) 如果大于 0 直接 return
- 去重 i 项,可能的三元组中的第一项
- 内部循环,主要用来找到 l 和 r 的值(三元组中剩下那两项的值),条件是 l < r
- 需要先判断大于和小于 因为不满足条件直接进行指针移动 而相等的情况下还需要 进行去重
- 当找到sortedNums[i] + sortedNums[l] + sortedNums[r] === 0 满足条件时,直接push
- push 的同时,去重,l和 l + 1,r 和 r -1 进行比较,进行指针移动
- 再同时,去重了之后,i 不动,移动 l 和 r , l++ , r--
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
let res = []
let sortedNums = nums.sort((a, b) => a - b)
for (let i = 0; i < sortedNums.length; i++) {
let l = i + 1, r = sortedNums.length - 1
// 最小值大于 0 直接 return
if (sortedNums[i] > 0) return res
// 去重 i 项
if (sortedNums[i] === sortedNums[i - 1]) continue
while (l < r) {
// 需要先判断大于和小于 因为不满足条件直接进行指针移动 而相等的情况下还需要 进行去重
if (sortedNums[i] + sortedNums[l] + sortedNums[r] > 0) {
r--
} else if (sortedNums[i] + sortedNums[l] + sortedNums[r] < 0) {
l++;
} else {
res.push([sortedNums[i], sortedNums[l], sortedNums[r]])
// 去重 l 和 r 项
while (l < r && nums[l] === nums[l + 1]) {
l++
}
while (l < r && nums[r] === nums[r - 1]) {
r--
}
// 去重后的指针移动
l++
r--
}
}
}
return res
};