难度:中等
题目
给你一个整数数组 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 。
提示:
-
3 <= nums.length <= 3000
-
-105 <= nums[i] <= 105
当然,解决这道题目的关键在于理解如何有效地找出数组中三个数的组合,使得它们的和为0。这是一道经典的算法问题,通常称为“三数之和”。下面是详细的解题思路和JavaScript实现方法。
解题思路:
- 排序数组:首先,将输入数组
nums进行排序。排序后的数组有助于我们在之后的步骤中避免重复解,并简化查找过程。 - 双指针法:使用三层循环理论上可以解决这个问题,但效率较低。更高效的方法是使用双指针技术。在外层循环中遍历排序后的数组,对于每个元素,使用两个指针(一个从当前元素的下一个元素开始,另一个从数组的末尾开始)来寻找另外两个数,使三数之和为0。
- 去重逻辑:为了避免重复解,我们需要在遍历和移动指针的过程中加入去重的逻辑。具体来说,如果当前遍历到的元素与前一个元素相同,则跳过此次循环,避免重复计算。同样,两个指针在移动时也要检查是否遇到重复元素,并适当跳过。
- 收集结果:当找到符合条件的三元组时,将其添加到结果集中。注意,由于题目要求不重复的三元组,所以在添加时也需要检查是否与已有的解重复。
JavaScript实现:
function threeSum(nums) {
nums.sort((a, b) => a - b); // 先排序
const result = [];
for (let i = 0; i < nums.length - 2; i++) {
// 跳过相同的元素以去重
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1, right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum < 0) {
left++; // 和太小,左指针右移
} else if (sum > 0) {
right--; // 和太大,右指针左移
} else {
// 找到了符合条件的三元组
result.push([nums[i], nums[left], nums[right]]);
// 移动指针并去重
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++;
right--;
}
}
}
return result;
}
// 示例
//console.log(threeSum([-1, 0, 1, 2, -1, -4])); // 应该输出:[[-1, -1, 2], [-1, 0, 1]]
这段代码首先对数组进行排序,然后使用外层循环遍历数组元素,配合双指针策略在内层循环中寻找符合条件的三元组。在搜索过程中,通过判断和的大小来决定移动左指针还是右指针,并在找到一个解后进行必要的去重处理,确保结果中不包含重复的三元组。