算法笔记--三数之和

96 阅读3分钟

leetcode原题目:

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

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

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:
输入:nums = []
输出:[]

示例 3:
输入:nums = [0]
输出:[]

看到题目的第一眼,首先排除O(n^3)复杂度的暴力解除法,我能想到的降低复杂度提高效率的方式,是通过之前做的一道两数之和的题目,首先对数组元素进行大小排序,然后用双指针去从数组首尾向中间0位置遍历,复杂度为0(n);

基于当时这个思路,我在这题里用到了三个指针,依旧将数组从小到大排序,然后用一个指针去从数组头部开始遍历,直到当前数大于0时结束遍历,因为数组已经排序过了,如果最小的数已经大于0,那么三数之和必不可能等于0,然后剩下的两个指针就依旧用我之前两数之和的思路,这样复杂度就是O(n^2);

有了思路后,开始写代码;

首先就是在数组长度小于3的情况下是不存在三数之和的,这种情况下我们就可以直接返回空数组了;

然后将数组进行从小到大排序,用到了数组排序 sort 方法, nums.sort((a,b) => a-b);

raycast-untitled.png

接下来就是需要去找两数之和加上 nums[one] 等于0的两个数了;所以 nums[one] 必须小于等于0;

raycast-untitled.png

这样我们就基本上写完了,运行代码后我发现自己没有去重,想到了Set数据结构,但是Set只能对简单数据类型去重,现在我们数组里存的是数组对象,所以就只能在指针移动的时候手动的去重;

我们已经对数组进行排序了,那么相同的值一定是相邻的,基于这个特点,我们只需要在指针移动的时候去对比一下,如果相邻的数值相同我们就直接在下一次循环进行之前过滤掉;

然后我再次运行了代码,还是没有去重,这里我以为是我上面去重的思路有问题想了很久,然后突然就意识到其实我们的第一个指针也是同样需要去重打所以我加上了:

raycast-untitled.png

这样在相同的情况下就直接 continue 进入下一次循环,nice;至此这道题就算解答完了


完整代码:

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    if (nums.length<3) return []
    nums = nums.sort((a,b) => a-b)
    let one=0
    let result = []
    while(nums[one]<=0 && one<nums.length){
        if(one > 0 && nums[one] == nums[one-1]) {
            one++
            continue
        }
        let two=one+1
        let three=nums.length-1
        while(two<three){
            const r = nums[one]+nums[two]+nums[three]
            if(r==0){
                result.push([nums[one],nums[two],nums[three]])
                while(two<three && (nums[three] == nums[three-1])){
                  three--
                }
                while(two<three && (nums[two] == nums[two+1])){
                  two++
                }
                three--
                two++
            }else if(r<0){
                two++
            }else{
                three--
            }
        }
        one++
    }
    return result
};

加油啊⛽️

image.png