题目描述:
给你一个整数数组 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 。
代码
/**
* 函数threeSum,接受一个数字数组nums作为参数,返回所有和为0的三元组数组。
*
* @param {number[]} nums 数字数组
* @return {number[][]} 所有和为0的三元组数组
*/
var threeSum = function (nums) {
// 首先对数组进行排序,使用升序
let arr = nums.sort((a, b) => a - b);
// 初始化结果数组
let result = [];
// 临时变量,用于存储中间结果
let tmp, left, right;
// 循环遍历数组中的所有数
for (let i = 0; i < arr.length; i++) {
// 如果当前元素大于0,由于数组是升序排列的,后面的元素都会更大,不可能找到和为0的三元组
if (arr[i] > 0) break;
// 如果当前元素与前一个元素相同,跳过当前循环,避免找到重复的三元组
while (arr[i] == arr[i - 1]) i++;
// 计算当前元素的补数,即需要找到两个数,使得它们与当前元素相加等于0
tmp = 0 - arr[i];
// 初始化左指针,从当前元素的下一个位置开始
left = i + 1;
// 初始化右指针,从数组的最后一个元素开始
right = arr.length - 1;
// 使用左右指针寻找和为0的三元组
while (left < right) {
// 如果左右指针所指的元素之和等于补数,说明找到了一个三元组
if (arr[left] + arr[right] == tmp) {
let tmparr = [];
// 将当前的三元组添加到临时数组中
tmparr.push(arr[i], arr[left], arr[right]);
// 将临时数组添加到结果数组中
result.push(tmparr);
// 跳过重复的left指针元素
while (left < right && arr[left] == arr[left + 1]) left++;
// 跳过重复的right指针元素
while (left < right && arr[right] == arr[right - 1]) right--;
// 移动指针,继续寻找下一个可能的三元组
left++;
right--;
}
// 如果左右指针所指的元素之和大于补数,说明需要减小和,移动right指针向左
else if (arr[left] + arr[right] > tmp) {
right--;
}
// 如果左右指针所指的元素之和小于补数,说明需要增大和,移动left指针向右
else if (arr[left] + arr[right] < tmp) {
left++;
}
}
}
// 返回结果数组
return result;
};
代码主要逻辑
-
排序:首先对输入数组
nums
进行排序,这样可以通过比较当前元素与0的关系来确定是否需要继续搜索。 -
初始化:定义一个空数组
result
来存储找到的三元组。 -
遍历数组:使用一个
for
循环遍历数组,其中i
是当前考虑的元素的索引。 -
终止条件:如果当前元素
nums[i]
大于0,由于数组已排序,后面的元素也会大于0,因此不可能找到和为0的三元组,直接返回结果。 -
跳过重复元素:如果当前元素与前一个元素相同,跳过当前循环,避免找到重复的三元组。
-
左右指针:使用两个指针
left
和right
,分别指向当前元素的下一个位置和数组的最后一个元素。 -
寻找三元组:在
while
循环中,通过移动left
和right
指针来寻找和为0的三元组。 -
添加三元组:如果找到和为0的三元组,将其添加到结果数组中,并更新指针位置,同时跳过重复的元素。
-
移动指针:如果三数之和不等于0,根据三数之和与0的比较结果来决定是移动
left
指针还是right
指针。 -
返回结果:最后返回包含所有和为0的三元组的数组。
这个算法的时间复杂度是O(n^2)
,其中n
是数组的长度,因为对于每个元素,我们最多进行一次O(n)
的双指针搜索。