【算法】三数之和

206 阅读2分钟

难度:中等

题目

给你一个整数数组 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实现方法。

解题思路:

  1. 排序数组:首先,将输入数组nums进行排序。排序后的数组有助于我们在之后的步骤中避免重复解,并简化查找过程。
  2. 双指针法:使用三层循环理论上可以解决这个问题,但效率较低。更高效的方法是使用双指针技术。在外层循环中遍历排序后的数组,对于每个元素,使用两个指针(一个从当前元素的下一个元素开始,另一个从数组的末尾开始)来寻找另外两个数,使三数之和为0。
  3. 去重逻辑:为了避免重复解,我们需要在遍历和移动指针的过程中加入去重的逻辑。具体来说,如果当前遍历到的元素与前一个元素相同,则跳过此次循环,避免重复计算。同样,两个指针在移动时也要检查是否遇到重复元素,并适当跳过。
  4. 收集结果:当找到符合条件的三元组时,将其添加到结果集中。注意,由于题目要求不重复的三元组,所以在添加时也需要检查是否与已有的解重复。

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]]

这段代码首先对数组进行排序,然后使用外层循环遍历数组元素,配合双指针策略在内层循环中寻找符合条件的三元组。在搜索过程中,通过判断和的大小来决定移动左指针还是右指针,并在找到一个解后进行必要的去重处理,确保结果中不包含重复的三元组。