JavaScript实现三数之和--力扣(leetcode)

目录

1 题目描述

2 输入输出

3 解答

1)暴力法(三层for嵌套循环)(超时)

2)一层for循环+双指针求twoSum(有代码)

4 代码

ac代码

嗯,超时代码

1 题目描述

https://leetcode-cn.com/problems/3sum/

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

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

2 输入输出

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4]

满足要求的三元组集合为:

[

  [-1, 0, 1],

  [-1, -1, 2]

]

3 解答

1)暴力法(三层for嵌套循环)(超时)(没有代码)

2)一层for循环+双指针求twoSum(有代码)

对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum,使用双指针

难点:结果去重

去重方法:

a)对结果二维数组去重(超时):数组的项(内层数组)转为字符串,然后一维数组去重,然后再转为二维数组----原因,因为对于数组长度为n的数组arr,随机选三个数,有n(n-1)(n-2)种,如果都符合情况,那么结果数组长度为n(n-1)(n-2),去重时间复杂度为n3

b)一维数组保存结果(超时),如果数组中有该字符串(每一个结果通过,连接的字符串),最后转为二维数组

c)使用set数据结构保存结果ac,每次add的时,会自动去重,去重时间比数组短,然后set变成二维数组(时间优化还是比较差,但是ac

d)跳过去重(ac

对于外层for循环,如果该数与前面一个相同,跳过这个数(去重),因为相同的数已经做过一遍啦。

对于内层while循环,如果左边arr[left]与前一个相同(这里不包括i项),跳过;右边类似

4 代码

ac代码

/**
 * @param {number[]} arr
 * @return {number[][]}
 * 功能:三数之和
 * 方法:对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum(三重for嵌套超时),使用双指针
 * 难点:结果去重
 * 难点解决方法:
   3 使用set保存结果,每次add的时,会自动去重,去重时间比数组短,然后set变成二维数组(时间优化还是比较差,但是ac啦)
 */
var threeSum_1 = function(arr) {
    if(arr.length <= 2){
        return [];//长度小于等于2
    }
    arr.sort((a, b) => {  //数组排序
        return a - b
    });
    let len = arr.length;
    if(arr[len - 1] < 0 || arr[0] > 0){  //排序后第一个数大于0或者最后一个小于0,返回空
        return [];
    }
    let result = new Set();  //使用Set数据结构保存结果,操作比数组快
    for(let i = 0; i < len - 2; i++){ //变量一遍数组,到倒数第三个,对于每一个数,求twoSum
        let left = i + 1;  //左指针
        let right = len - 1;  //右指针
        let twoSum = 0 - arr[i];  //twoSum
        while(left < right){ //左指针往右移,右指针往左移
            if(arr[left] + arr[right] === twoSum){  //相等,把结果放进Set里面。
                //注意,如果放进的是两个相同项的数组,如[1, 2], [1, 2];
                //那么结果里面,Set有两项,因为这两个数组的地址(指向堆里面值的指针)不一样
                let str = arr[i] + "," + arr[left] + "," + arr[right];
                result.add(str);  //自动去重,去重时间比数组快
                left++;
                right--;
            }else if(arr[left] + arr[right] < twoSum){
                left++;
            }else {
                right--;
            }
        }
    }
    result = [...result]; //set变成数组
    for(let i = 0; i < result.length; i++){ //一维数组(项为结果组成的字符串)转为二维数组
        result[i] = result[i].split(",");
        for(let j = 0; j < result[i].length; j++){
            result[i][j] = Number(result[i][j]);
        }
    }
    return result;
};

/**
 * @param {number[]} arr
 * @return {number[][]}
 * 功能:三数之和
 * 方法:对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum(三重for嵌套超时),使用双指针
 * 难点:结果去重(通过)
 * 难点解决方法:
 4 对于外层for循环,如果该数与前面一个相同,跳过这个数(去重),因为相同的数已经做过一遍啦。
  对于内层while循环,如果左边arr[left]与前一个相同,跳过;右边类似
 */
var threeSum_2 = function(arr) {
    if(arr.length <= 2){
        return [];//长度小于等于2
    }
    arr.sort((a, b) => {  //数组排序
        return a - b
    });
    let len = arr.length;
    if(arr[len - 1] < 0 || arr[0] > 0){  //排序后第一个数大于0或者最后一个小于0,返回空
        return [];
    }
    let result = [];  //保存结果
    for(let i = 0; i < len - 2; i++){ //遍历一遍数组,到倒数第三个,对于每一个数,求twoSum

        if( i- 1 >= 0 && arr[i] === arr[i - 1]){ //如果该项与前面一项一样,那么跳过该项,去重
            //这里不能与后一项比较,这样不能保证跳过相同的数字,比如123334, 和为10,
            //如果与后一项比较(即使这个判断放在代码最后不行,原因有三个或者三个以上相同数字时),
            // 那么会跳过前面两个三,得不到结果
            //i++;  //这里注意啦,不能写i++,因为for循环里面已经写了,再写就重复啦。
            continue;
        }
        let left = i + 1;  //左指针
        let right = len - 1;  //右指针
        let twoSum = 0 - arr[i];  //twoSum
        //双指针求和部分
        while(left < right){ //左指针往右移,右指针往左移
            if(left - 1 > i && arr[left] === arr[left - 1]){ //去重,与前一个相同时,跳过相同的项,注意边界
                left++;
                continue;
            }
            if(right + 1 < len && arr[right] === arr[right + 1]){ //去重,与后一个相同时,跳过相同的项
                right--;
                continue;
            }
            if(arr[left] + arr[right] === twoSum){  //相等
                result.push([arr[i], arr[left], arr[right]]);
                left++;
                right--;
            }else if(arr[left] + arr[right] < twoSum){
                left++;
            }else {
                right--;
            }
        }
    }
    return result;
};

let set = new Set([[1, 2], [1, 2], 2]);
console.log(set);//Set { [ 1, 2 ], [ 1, 2 ], 2 }
console.log(threeSum_2([-4,-2,1,-5,-4,-4,4,-2,0,4,0,-2,3,1,-5,0]));

嗯,超时代码

/**
 * @param {number[]} arr
 * @return {number[][]}
 * 三数之和
 * 方法:对数组进行排序,然后遍历数组,对于每一个数,转为求twoSum(三重for嵌套超时),使用双指针
 * 难点:结果去重
 * 难点解决方法:
 1 对结果二维数组去重(超时):数组的项(内层数组)转为字符串,然后一维数组去重,然后再转为二维数组
 2 一维数组保存结果(超时),如果数组中有该字符串(每一个结果通过,连接的字符串),最后转为二维数组
 */
var threeSum = function(arr) {
    if(arr.length <= 2){
        return [];//长度小于等于2
    }
    arr.sort((a, b) => {
        return a - b
    });
    let len = arr.length;
    if(arr[len - 1] < 0 || arr[0] > 0){
        return [];
    }
    let result = [];
    for(let i = 0; i < len - 2; i++){
        let left = i + 1;
        let right = len - 1;
        let twoSum = 0 - arr[i];
        while(left < right){
            if(arr[left] + arr[right] === twoSum){
                let str = arr[i] + "," + arr[left] + "," + arr[right];
                if(result.indexOf(str) === -1){
                    result.push(str);
                } // 2这个去重也不行
                left++;
                right--;
            }else if(arr[left] + arr[right] < twoSum){
                left++;
            }else {
                right--;
            }
        }
    }
    for(let i = 0; i < result.length; i++){
        result[i] = result[i].split(",");
        for(let j = 0; j < result[i].length; j++){
            result[i][j] = Number(result[i][j]);
        }
    }// 2这个去重也不行
    return result;
};

参考:力扣

百里于2020年5月24日

如果有错,请您指出!如有侵权,请联系我删除!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值