题目描述
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为 0
且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例
scss
输入: 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] 。
注意,输出的顺序和三元组的顺序并不重要。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
题解
js
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function (nums) {
let ans = [];
const len = nums.length;
if (nums == null || len < 3) return ans;
nums.sort((a, b) => a - b);
for (let i = 0; i < len; i++) {
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
let L = i + 1;
let R = len - 1;
while (L < R) {
const sum = nums[i] + nums[L] + nums[R];
if (sum == 0) {
ans.push([nums[i], nums[L], nums[R]]);
while (L < R && nums[L] == nums[L + 1]) L++;
while (L < R && nums[R] == nums[R - 1]) R--;
L++;
R--;
}
else if (sum < 0) L++;
else if (sum > 0) R--;
}
}
return ans;
};
首先通过sort()
对数组进行排序,从而方便后续的双指针移动和去重
最外层通过一个for
循环遍历数组来获取三元组的第一个数。 for循环里面设定两个指针L
,R
以及变量sum
.L
指针从i的右侧开始(较小),R
指针从数组末尾开始(最大),sum
用来计算当前的和。
内层while循环,条件为L<R以免去重复,寻找能满足nums[i]+nums[L]=nums[R]
的组合
- 若满足sum=0条件,存储当前三元组,然后
L
向右移动,R
同时向左移动以寻找下一个满足条件的。(只移动一个必定不会满足sum为0) - 若sum<0,当前和较小,
L
向右移动以获得较大的值 - 若sum>0,当前和较大,
R
向左移动以获得较小的值
优化
- 最开始先检查数组元素数量,如果<3必然不存在三元组满足条件,直接返回
- while循环前先检查当前的nums[i],如果>0,因为L,R指针都大于i,数组按大小排序,则必然不存在满足条件的三元组,跳过当前循环。
- while循环前再检查当i不指向第一个元素时,i是否和前一个i-1指向的值相等,如果相等,结束当前循环避免重复计算。
- 若当前sum==0时,通过while循环里的条件判断: 若L<R且L和L+1/R和R-1指向的值相等时,执行L++/R--操作以跳过L+1/R-1。此处使用while循环代替if判断来实现连续去重
时间,空间复杂度
时间复杂度O(N^2)
排序的复杂度为 O(NlogN)。
外层if
循环固定 i
运行 N 次。
内层 while
循环的双指针 L 和 R 共同遍历的次数为 O(N)。
总复杂度为 O(NlogN)+O(N×N)=O(N^2)。
空间复杂度:O(logN) 或 O(N)
取决于 JavaScript 内部排序算法所需的额外空间。如果是快速排序,通常为 O(logN);如果是归并排序,则为 O(N)。
参考题解:LeetCode题解 画手大鹏