题目描述
给你一个包含 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
解题思路
看到这题最简单的就是暴力解法,直接三重for循环就完事了。但是这样的空间复杂度和时间复杂度都很高,那么有没有什么办法能够优化了?这时候我就想到了用双指针(对撞指针)的方法。
如果喜欢看代码,可直接跳过文字,代码也有注释。
- 首先,根据示例可知,如果数组为空或者数组长度不足3,则直接返回原数组;
- 那么先对给定数组nums进行排序,这里的话就用sort()升序排序(倒序也行,不过后面代码就是变了,不过思路是一样的)
- 题目既然是要求三数之和,那么我们就定义三个数出来:第一个数为 nums[i],i 从 0 开始遍历数组;第二个数为nums[left],即左指针为 left , 第三个数为nums[right],即右指针为 right。
- 题目又说三数之和要为 0 ,那么我们就定义一个空数组res,如果满足条件就把满足条件的三数插入到数组中;
- 因为三个数中第一个数是最小的,所以先说第一个数,如果nums[i] 大于0 ,那么直接break结束循环,因为如果三个数中的第一个数都比0大,那么后面的数肯定都比 0 大,所以直接结束循环;
- 反之如果nums[i] 小于0,就让left = i + 1,right = nums.length - 1;
- 当左指针比右指针小的时候进行while循环, 判断三数之和是否满足条件;
- 如果满足条件就以数组的形式放进空数组中,并且对左指针和右指针做去重的操作,如果指针本次的值和上一次的值相同,那么就跳过本次循环,这样就避免结果出现重复数组;
- 如果三数之和不满足条件,即三数之和小于0,左指针右移;三数之和大于0,右指针左移;
- 最后第一个数循环结束,返回 res 数组即为数组中所有满足条件的集合。
AC代码
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let res = [] // 定义一个空数组
if(nums == null || nums.length < 3) return res // 如果数组为空或者数组元素不足3个,则返回原数组
nums.sort((a,b) => a - b) // 对给定数组进行升序排序,如果想降序排序只需把 a - b 改成 b - a即可
for(let i = 0; i < nums.length; i++) { // 第一重循环,给定第一个值
if(nums[i] > 0) break // 如果第一个值都大于0,说明后面的值都比0大,所以直接结束循环
if(nums[i] == nums[i-1]) continue // 如果当前值和前一个值相同,则结束本次循环并进入下次循环,这样做的目的是保证ans中不会出现重复的数组
let l = i + 1 // 第二个值,也可以说是左指针
let r = nums.length - 1 // 第三个值,即右指针
while(l < r) { // 只有当左指针小于右指针才进行循环;如果左指针大于或等于右指针的话,说明从 i 之后的所有数都已经遍历完了,本次循环已经结束
let sum = nums[i] + nums[l] + nums[r] // 定义三数之和
if(sum == 0) { // 如果三数之和满足条件
res.push([nums[i],nums[l],nums[r]]) // 就将这三个数以数组的形式 push 到 ans 数组中
while(l < r && nums[l] == nums[l+1]){ // 去重,如果不去重的话就可能会出现重复的数组被 push 到 ans 数组中
l++
}
while(l < r && nums[r] == nums[r-1]){ // 去重,同上
r--
}
l++ // 左指针右移
r-- // 右指针左移
} else if(sum < 0){ // 小于 0 说明左指针的值小了,所以左指针右移,因为排序之后是升序数组
l++
} else if(sum > 0) { // 大于 0 说明右指针的值大了,所以右指针向左移
r--
}
}
}
return res
};
总结
如果这份题解能够帮助到您的话,希望动动各位发财的金手指点个赞哟。在对撞指针的基础上还可以再进行优化,就比如去重操作那里可以用hashMap,欢迎各位大佬在评论区评论。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情