[路飞]_每天刷leetcode_78(三数之和 3 Sum)

230 阅读1分钟

这是我参与2022首次更文挑战的第39天,活动详情查看:2022首次更文挑战

三数之和 3 Sum

LeetCode传送门15. 三数之和

题目

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

Example:

Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]

Input: nums = []
Output: []

Input: nums = [0]
Output: []

Constraints:

  • 0 <= nums.length <= 3000
  • 105<=nums[i]<=105-10^5 <= nums[i] <= 10^5

思考线


解题思路

这道题最能想到的就是使用三层for循环,然后把所有的值都找出来,本着先找出解再优化的原则,我写了如下代码

function threeSum(nums: number[]): number[][] {
    const res: number[][] = [];
    const resObj = {}
    const len = nums.length;
    for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            for (let k = j + 1; k < len; k++) {
                if (nums[i] + nums[j] + nums[k] === 0) {
                    const item = [nums[i], nums[j], nums[k]]
                    const ind = item.sort().join()
                    if (!resObj[ind]) {
                        res.push(item);
                        resObj[ind] = true;
                    }
                }
            }
        }
    }
    return res
};

然而很明显这样的暴力解是会明显的超时的,那么我们该如何做这道题呢?

由于题目要求我们找出所有和为0且不能重复的三元组。我们首先思考一下如何能保证不重复呢?

经过简单的思考,保证不重复最好做的是把数组进行升序排序,我们只要保证first <= second <= third,这样我们再找三元组的时候就可以避免重复。

然后就是找到三数和为0如何优化的过程。

  • 我们可以先遍历一遍排序后的数组,把第一个元素确定下来

    • 如果第一个元素都大于0,后面的肯定也大于0,所以直接break掉循环即可

    • 如果当前元素i和上一个元素i-1的值相等,说明这和上一个内容重复了,我们直接执行continue来提前结束掉当前循环

  • 除去上面两种情况,我们再确定后面两个数,后面两个数,我们可以用双指针法来找到sum===0的情况, 我们假设第二个元素为L,第三个为R

    • L<R的情况下,我们用双指针不断逼近结果,找到所有可能性,具体细节请看下面代码

有了以上的思路我的代码实现如下

function threeSum(nums: number[]): number[][] {
    const sorted = nums.sort((a, b) => a - b); // 排序
    const len = nums.length;
    const res = []
    for (let i = 0; i < len; i++) {
        if (sorted[i] > 0) break; // 如果第一个数都大于零,肯定没结果
        if (i > 0 && sorted[i] === sorted[i - 1]) continue; // 如果和前面的重复了,去重。注意是i和i-1

        // 下面是双指针来比较twoSum的做法
        let L = i + 1;
        let R = len - 1;
        while (L < R) {
            const sum = sorted[i] + sorted[L] + sorted[R];

            if (sum === 0) {
                res.push([sorted[i], sorted[L], sorted[R]])
                // 注意下面两个while去重
                while (L < R && sorted[L] === sorted[L + 1]) L++;
                while (L < R && sorted[R] === sorted[R - 1]) R--;
                L++
                R--
                continue;
            }
            if (sum < 0) L++
            if (sum > 0) R--
        }

    }
    return res;
};

时间复杂度

O(n^2): 数组排序O(nlogn), 遍历数组O(n), 双指针遍历O(n),整体为O(nlogn) + O(n) * O(n) = O(n^2)

这就是我对本题的解法,如果有疑问或者更好的解答方式,欢迎留言互动。