携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组。
来源:力扣(LeetCode) 链接:leetcode.cn/problems/3s…
分析
提炼题目大意
- 三个元素和为0的三元组
- 三元组不重复
实现方法探索
- 首先是找到符合题目描述的3元组
- 看上去,要找到a+b+c=0的三个数,暴力一点,3重循环就可以找到,但是这样足够慢,时间复杂度是O(n^3)
- 一般通过降低循环的次数来降低时间复杂度,但是要怎么降低循环次数?
- 变换公式,a+b = -c,这样只需要循环两遍,一遍找a,一遍找b,加起来等于-c就找到了三元组[a,b,c]
- 另一个要求是三元组不重复,[-3,1,2][2,-3,1][1,2,-3]被认是一样的,[-3,1,2]是有序的,所以一组相同的三元组中,总有一组是有序的,我们可以把这个有序的那个当初一组相同的三元组的代表,所以要找出所有不重复的三元组,先对数据进行排序之后再遍历,得到的结果就是不重复的三元组了
细节优化
- 第一重循环是必需且必要的,第二重循环可以稍微优化
- 一个一个的遍历和两个指针一起遍历,显然双指针会更快一些
- 三元组元素就是指针的指向和一重循环的数组的值
- 数组有序的前提下
- 当左右指针的值与一重循环的值加起来小于0,此时左指针的值足够小,需要右移
- 当左右指针的值与一重循环的值加起来大于0,此时右指针的值足够大,需要左移
- 当指针移动后的值和移动之前的值一样时,为了实现三元组不重复这个条件,此时不需要计算,继续移动指针即可
代码
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
let res = []
nums.sort((a,b) => a-b)
for(let i=0;i<nums.length - 2;i++) {
let n1 = nums[i]
if(n1 > 0) break
if(n1 == nums[i-1] && i > 0) continue
let left = i +1
let right = nums.length - 1
while(left < right){
let n2 = nums[left]
let n3 = nums[right]
if(n1 + n2 + n3 === 0) {
res.push([n1,n2,n3])
while(left < right && nums[left] === n2) left++
while(left < right && nums[right] === n3) right--
}else if(n1 + n2 + n3 < 0){
left ++
}else {
right --
}
}
}
return res
};
总结
- 当遇到值=值这种公式时,左右不平衡的时候,可以通过移项,使左右尽可能平衡,就会打开新世界的大门,算法会更优雅
- 在获得答案的时候,最好在算法中考虑细节,得到准确的答案,以这个题作为例子,可以得到符合条件的所有三元组,最后去重,这样就不用考虑什么时候是重复的,同时也做了多余的操作,浪费了时间和空间。
- 今天也是有收获的一天