BM54 三数之和

描述
给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。

数据范围:0 \le n \le 10000≤n≤1000,数组中各个元素值满足 |val | \le 100∣val∣≤100
空间复杂度:O(n^2)O(n
2
),时间复杂度 O(n^2)O(n
2
)

注意:
三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
解集中不能包含重复的三元组。
例如,给定的数组 S = {-10 0 10 20 -10 -40},解集为(-10, -10, 20),(-10, 0, 10)
分析:
根据题意求数组中任意三个数相加零的三元组。相对于寻找两数之和来说,空间复杂度可能会加一。
, 刚开始没有思路,用笨办法实现,能想到的就是三重遍历,本题性能要求不高也是可以通的

    num.sort((a, b) => a - b);
    const result = [];
    num.forEach((item1, index1) => {
        num.forEach((item2, index2)=> {
            num.forEach((item3, index3) => {
                if (
                    index1 !== index2
                    && index1 !== index3
                    && index2 !== index3
                    && (item1 + item2 + item3) === 0
                ) {
                   /*
                      追加数据
                   */
                }
            })
        })

这样判定肯定是有重复的, 先将其序列化之后用Set去重。

    result.push(
       JSON.stringify(
           [item1, item2, item3]
           .sort((a, b) => a - b)
          )
       );

最后再反序列化转为我们要的结果。

    return [...new Set(result)].map(item => {
        return JSON.parse(item);
    });

第一版全部代码


/**
 * 
 * @param num int整型一维数组 
 * @return int整型二维数组
 */
function threeSum( num ) {
    // write code here
    num.sort((a, b) => a - b);
    const result = [];
    num.forEach((item1, index1) => {
        num.forEach((item2, index2)=> {
            num.forEach((item3, index3) => {
                if (
                    index1 !== index2
                    && index1 !== index3
                    && index2 !== index3
                    && (item1 + item2 + item3) === 0
                ) {
                    result.push(
                        JSON.stringify(
                            [item1, item2, item3]
                            .sort((a, b) => a - b)
                        )
                    );
                };
            });
        });
    });
    return [...new Set(result)].map(item => {
        return JSON.parse(item);
    });
}
module.exports = {
    threeSum : threeSum
};

虽然本题这样也通过了题目的要求,但是不符合使用算法的初心。
,根据第一版的结果,考虑使用双指针和哈希表,通过减少一层循环。根据题意三数之和的结果为零,得出,其中任意两个数的和,等于第三个数的反数。

还是先使用了哈希表的解法,根据第50题《两数之和》思路,使用哈希表先保存了所有值,同时将在数组中的位置也存了下来,利用不同索引位和三数之和等于零这个条件来判断,但是这样输出后的结果和答案的位置不一样,卡在了13题

    num.sort((a, b) => a - b);
    const result = [];
    const negative = [];
    const just = [];
    const temp = {};
    const zero = num.filter(item => item === 0);
    if (num.length < 3) {
        return [];
    } else if (num.length >= 3 && !num.some(item => item > 0)) {
        return [[0, 0, 0]];
    }
    num.forEach((item, index) => {
        if (temp[item] !== undefined) {
            temp[item].indexList.push(index);
        } else {
            temp[item] = {
                item,
                indexList: [index]
            }
        }
    });
    num.forEach((item1, index1)=> {
        num.forEach((item2, index2) => {
           const value = - (item1 + item2);
            if (
                typeof temp[value] === 'object'
                && (value + item1 + item2 === 0)
                && temp[value].indexList.indexOf(index1) === -1
                && temp[value].indexList.indexOf(index2) === -1
                && index1 !== index2
            ) {
                result.push(
                    JSON.stringify(
                        [temp[value].item, item1, item2]
                        .sort((a, b) => a - b)
                    )
                );
           }
        });
    })

想要的数据是拿到了,但是和答案的位置不同判定失败,、
在这里插入图片描述
如果没有重复的数,应该是可以过的。
然后使用双指针解法。使用双指针提前一定是要排序的。(从左向右/从小到大)遍历,取当前遍历的值作为基准值,然后扫描当前值以后的值。一个向左一个向右。不断缩小范围。在找的过程中如果扫描的值之和取反后大于当前的值,那么需要将左指针向右移动,因为我们的数据是升序排序的,越往右越大,反之越往左越小。在找的过程中如果扫描的值之和取反后小于当前的值,那么需要将右指针向左移动。如果扫描的值之和取反不大于当前值,也不小于当前值。那么就是等于当前值。找到之后需要将左右指针同时移动一位。还需要跳过重复。需要注意的是跳过重复必须是在同时移动左右指针位置之前,否则将不达预期。数据的处理方式和之前一样不变。然后就可以通过了。


/**
 * 
 * @param num int整型一维数组 
 * @return int整型二维数组
 */
function threeSum( num ) {
    // write code here
    num.sort((a, b) => a - b);
    const result = [];
    num.forEach((item, index) => {
        let left = index + 1;
        let right = num.length - 1;
        while(left < right) {
            if (item < -(num[left] + num[right])) {
                left += 1;
            }else if (item > -(num[left] + num[right])) {
                right -= 1;
            } else {
                result.push(
                    JSON.stringify([item, num[left], num[right]])
                )
                while (num[left] === num[left + 1]) {
                    left += 1;
                }
                while (num[right] === num[right - 1]) {
                    right -= 1;
                } 
                right -= 1;
                left += 1;
            }
        }
    });
    return [...new Set(result)].map(item => {
        return JSON.parse(item);
    });
}
module.exports = {
    threeSum : threeSum
};

总结:虽然这题过了,但是感觉还没解到点子上。第50题是两数之和,这一题是三数之和,那如果是五数之和,十数之和呢,总不能用五个指针十个指针,最好的方法应是,先找到有多少种组合,计算每种组合相加的结果得到我们想要的答案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值