思路:
其实最简单的思路就是暴力查找,三数之和,用三层循环去找,简单,但是时间复杂度为 n³ ,跑不通,故需要降低时间复杂度,于是乎便有了,排序+双指针解法
方法一:暴力查找
for(let i=0;...){
for(let j=i+1;...){
for(let t=j+1;...){
if(nums[i]+nums[j]+nums[t] == 0){
a.push(nums[i],nums[j],nums[t])
res.push(a)
}
}
}
}
return res
时间复杂度:O(n³)
很明显,这个算法确实不算最优解,故需要优化成方法二
方法二:排序+双指针(我觉得也可以说是三指针?)
思路:
- 特殊判断:if (nums.length) < 3,直接返回空数组(不符合题意)
- 将数组进行升序排序
- 如果数组的第一个数大于0,后面的数经过排序后也会大于0,故三数之和不可能等于0,直接返回空数组(不符合题意)
- 设置三个指针,第一个为数组最左的元素 i ,第二个为 i 指针右边的 left ,第三个为指向数组末端的 right
- 当 i>0 且 nums[i]==nums[i-1] 跳过此次循环,因为在上一次的循环中,已经把含有 nums[i] 这个数字的情况给包含进答案了,故不跳过的话相当于再来一次,答案会有重复元素
- 当 left《right 时循环计算 sum=nums[i]+nums[left]+nums[right] 并且按照如下规则将 left 和 right 指针进行移动
- 当 sum == 0 时,将这个组合给记录在答案中,并且将 left 指针向后移动,跳过所有重复的 nums[left] ,right 指针同理向前移动,跳过所有重复的 nums[right]
- 当 sum < 0 时,证明 nums[left] 不够大,故将 left 向后移动,并跳过重复元素
- 当 sum > 0 时,证明 nums[right] 太大了,故将 right 向前移动,并跳过重复元素
var threeSum = function (nums) {
let len = nums.length
let res = []
if (len < 3) return []
nums = nums.sort((a, b) => a - b)
if (nums[0] > 0) return []
let left, right
for (let i = 0; i < len - 1; i++) {
left = i + 1
right = len - 1
if (i > 0 && nums[i] == nums[i-1]) continue
while (left < right) {
let a = []
if (nums[i] + nums[left] + nums[right] == 0) {
a.push(nums[i],nums[left],nums[right])
res.push(a)
while (left < right && nums[left] == nums[left + 1]) {
left++
}
while (left < right && nums[right] == nums[right - 1]) {
right--
}
left++
right--
} else if (nums[i] + nums[left] + nums[right] < 0) {
left++
while(left < right && nums[left] == nums[left-1]) left++
} else {
right--
while(left < right && nums[right] == nums[right-1]) right--
}
}
}
return res
};
时间复杂度: O(N²)