算法(附思维导图 + 全部解法)300题之(18)四数之和

零 标题:算法(leetode,附思维导图 + 全部解法)300题之(18)四数之和

导读:

作者简介

1 作者简历

作者简历
2019年的微信小程序应用开发赛-全国三等奖

2 作者标签

1)“伪全栈工程师,主攻前端,偶尔写点后端”。

2)2019年的微信小程序应用开发赛 - 全国三等奖;
2019CODA比赛 - 前 17/211 强 且 荣获“优秀团队”称号 等。

3)“半自媒体人”,在校期间、个人公众号(IT三少。新自媒体(公众号)号: 码农三少 )在半年内实现了0到5.8K+的粉丝增长等。

一 题目描述

题目描述

二 解法总览(思维导图)

思维导图

三 全部解法

1 方案1

1)代码:

// 方案1 “暴力”。
// 先排序,再4层for循环、穷举所有的可能。
// 超时,通过 282 / 286 。
var fourSum = function(nums, target) {
    const l = nums.length;

    // 1)排序。
    nums = nums.sort((a, b) => a - b);

    // 2)状态初始化。
    let map = new Map(),
        resArr = [];

    // 3)核心:4层for循环、穷举所有的可能。
    for (let i = 0; i < l-3; i++) {
        for (let j = i + 1; j < l-2; j++) {
            for (let k = j + 1; k < l-1; k++) {
                for (let z = k + 1; z < l; z++) {
                    const tempSum = nums[i] + nums[j] + nums[k] + nums[z],
                        tempStr = [nums[i], nums[j], nums[k], nums[z]].join('#');

                    // 3.1)判断“当前4个数组合的和” 是否等于 target 且 之前没有存储过。
                    if (tempSum === target && !map.has(tempStr)) {
                        resArr.push([nums[i], nums[j], nums[k], nums[z]]);
                        map.set(tempStr, 1);
                    }
                }
            }
        }
    }

    // 4)返回结果 resArr 。
    return resArr;
};

2 方案2

1)代码:

// 方案2 “方案1的优化版”。
// 原先的4层for循环 --> 2层for循环 + 双指针 。
// 技巧:“有序胜过无序”。
// 通过sort方法(时间复杂度仅为 O(nlogn))将无序的数组变有序是一件很划算的事情。
// 要想使用双指针等,必须先排序、使其变有序。
var fourSum = function(nums, target) {
    const l = nums.length;

    // 1)排序。
    nums = nums.sort((a, b) => a - b);

    // 2)状态初始化。
    let map = new Map(),
        resArr = [];

    // 3)核心:2层for循环 + 双指针 。
    for (let i = 0; i < l-3; i++) {
        for (let j = i + 1; j < l-2; j++) {
            let left = j + 1,
                right = l-1;

            while (left < right) {
                const tempSum = nums[i] + nums[j] + nums[left] + nums[right],
                    tempStr = [nums[i], nums[j], nums[left], nums[right]].join('#');

                // 3.1)判断“当前4个数组合的和” 是否等于 target 且 之前没有存储过。
                if (tempSum === target) {
                    if (!map.has(tempStr)) {
                        resArr.push([nums[i], nums[j], nums[left], nums[right]]);
                        map.set(tempStr, 1);
                    }

                    // 3.2)核心:若 tempSum === target ,
                    // 则下一次的可能结果必须得让 left、right 各往中间至少走一步,不然组合必重复!
                    left++;
                    right--;
                } else if (tempSum < target){
                    left++;
                } else {
                    right--;
                }
            }
        }
    }

    // 4)返回结果 resArr 。
    return resArr;
};

3 方案3

1)代码:

// 方案3 回溯(说白了、说穿了,就是递归。因为一般用递归实现回溯)。
// 本质:跟4层for循环差不多,可能的区别会体现在代码的可读性上。
// 超时,通过 268 / 286 。
var fourSum = function(nums, target) {
    // 技巧:永远记住,递归 = 递归出口(为了不陷入无线递归的死循环) + 递归主体(一般会变更一些参数后,在调用函数本身)。
    // 一般 递归出口 放前面, 递归主体 放后面。
    // 1)递归
    const dfs = (index, l, curArr, resArr, nums, target) => {
        const curLength = curArr.length;
        // 1.1)递归出口。
        // 边界:是 index > l ,而不是 index >= l(这样会遗漏答案,case:[1,0,-1,0,-2,2] 0 )。
        if (index > l || curLength > 4) {
            return;
        }

        // 1.2)递归主体
        if (curLength === 4) {
            const tempSum = curArr.reduce((acc, cur) => {
                acc += cur;
                return acc;
            }, 0),
                tempStr = curArr.join('#');

            // 判断“当前4个数组合的和” 是否等于 target 且 之前没有存储过。
            if (tempSum === target) {
                if (!map.has(tempStr)) {
                    resArr.push(curArr.slice());
                    map.set(tempStr, 1);
                }
            }
        } else if (curLength < 4) {
            // 技巧:回溯的核心 = 选 + 不选 。
            // 1.2.1)选
            curArr.push(nums[index]);
            dfs(index + 1, l, curArr, resArr, nums, target);
            // 1.2.1)不选
            curArr.pop();
            dfs(index + 1, l, curArr, resArr, nums, target);
        }
    };

    // 2)排序。
    nums = nums.sort((a, b) => a - b);

    // 3)状态初始化。
    const l = nums.length;
    let index = 0,
        map = new Map(),
        curArr = [],
        resArr = [];

    // 4)调用 回溯函数 —— dfs递归。
    dfs(index, l, curArr, resArr, nums, target);

    // 5)返回结果 resArr 。
    return resArr;
}

4 方案4

1)代码:

// 方案4 “方案3(回溯)的优化版” —— 剪枝。
// TODO:暂时想不出,跳过。
// case:[-477,-476,-471,-462,-440,-400,-398,-394,-394,-393,-389,-386,-350,-346,-338,-315,-273,-249,-182,-172,-166,-161,-149,-116,-112,-109,-100,-73,-33,-26,-22,-11,6,8,13,19,56,78,101,102,111,140,155,158,181,205,211,225,232,242,254,265,281,308,310,320,320,364,366,381,385,387,443,496,496]
// 1236
var fourSum = function(nums, target) {
    // TODO:暂时想不出,跳过。
}
#面经##学习路径#
全部评论

相关推荐

手撕没做出来是不是一定挂
Chrispp3:不会,写出来也不一定过
点赞 评论 收藏
分享
头像
11-21 11:39
四川大学 Java
是红鸢啊:忘了还没结束,还有字节的5k 违约金
点赞 评论 收藏
分享
一名愚蠢的人类:多少games小鬼留下了羡慕的泪水
投递荣耀等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
1
分享
正在热议
# 25届秋招总结 #
443000次浏览 4514人参与
# 春招别灰心,我们一人来一句鼓励 #
42077次浏览 535人参与
# 北方华创开奖 #
107460次浏览 600人参与
# 地方国企笔面经互助 #
7969次浏览 18人参与
# 同bg的你秋招战况如何? #
77008次浏览 566人参与
# 实习必须要去大厂吗? #
55793次浏览 961人参与
# 阿里云管培生offer #
120380次浏览 2220人参与
# 虾皮求职进展汇总 #
116056次浏览 886人参与
# 如果你有一天可以担任公司的CEO,你会做哪三件事? #
11650次浏览 289人参与
# 实习,投递多份简历没人回复怎么办 #
2454867次浏览 34858人参与
# 提前批简历挂麻了怎么办 #
149922次浏览 1978人参与
# 在找工作求抱抱 #
906075次浏览 9421人参与
# 如果公司给你放一天假,你会怎么度过? #
4762次浏览 55人参与
# 你投递的公司有几家约面了? #
33209次浏览 188人参与
# 投递实习岗位前的准备 #
1196011次浏览 18550人参与
# 机械人春招想让哪家公司来捞你? #
157641次浏览 2267人参与
# 双非本科求职如何逆袭 #
662333次浏览 7397人参与
# 发工资后,你做的第一件事是什么 #
12793次浏览 62人参与
# 工作中,努力重要还是选择重要? #
35884次浏览 384人参与
# 简历中的项目经历要怎么写? #
86934次浏览 1516人参与
# 参加完秋招的机械人,还参加春招吗? #
20145次浏览 240人参与
# 我的上岸简历长这样 #
452046次浏览 8089人参与
牛客网
牛客企业服务