题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
力扣原题目地址:https://leetcode.cn/problems/...

我们来看一下解决方案...

四种方案,层层递进,主要还是思路...

方案一,直接两层循环判断值是否相等

var twoSum = function (nums, target) {
                    for (let i = 0; i < nums.length; i++) {
                        // j=i+1即:下一项==当前项+1 因为要不断和下一项相加看看是否等于target
                        for (let j = i + 1; j < nums.length; j++) { 
                            if (nums[i] + nums[j] === target) {
                                return [i, j]
                            }
                        }
                    }
                };
这种方式可以理解为暴力破解的方式,要循环两次时间复杂度为O(n) * O(n)即为O(n²),太慢了,咱们看看有没有优化方式

方案二,使用数组判断另一个值是否存在

思路:我们已经知道两数之和的target了,同时在遍历的时候,也能拿到一个数nums[i],至于另外一个数,咱们可以总和减去一个数即:otherVal = target - nums[i],然后再看看另外一个数otherVal是否在这个数组里面,当然不能直接看,直接看又得循环一遍了,所以咱们可以新定义一个数组,把数组中的树一次追加进去,直到发现新数组中的某一项中有另外一个数,也就是说新数组中的这一项加上nums[i]刚好是等于target,这样的话,就完成任务了,代码如下:

var twoSum = function (nums, target) {
                    let arr = [] // 1. 定义一个数组用来另存nums数组中的数据
                    for (let i = 0; i < nums.length; i++) { // 2. 循环得到每一项每个 一个数
                        let otherVal = target - nums[i] // 3. 通过一个数 计算出 另一个数
                        if (arr.includes(otherVal)) { // 4. 一开始数组中肯定不包含另一个数
                            return [arr.indexOf(otherVal), i] // 6. 直到找到 
                        } else {
                            arr.push(nums[i]) // 5. 不包含没事,那就存一份吧,方便后续匹配
                        }
                    }
                };

注意arr中的数组中的每一项,从左往右,是和nums数组中的每一项值以及索引是保持一致的,所以使用arr.indexOf(otherVal)找到的索引就是在nums中的索引

如果大家在力扣中按照这种方式提交的话,会发现这种方式的性能甚至还不如暴力破解的性能好呢!为何呢?原因是:arr.includesarr.indexOf这两个方法都会遍历数组的,所以使用数组不是太好。那能不能使用对象呢?可以的,我们来看看对象的写法

方案三,使用对象判断另外一个值是否存在

var twoSum = function (nums, target) {
                let obj = {} // 1. 定义一个对象用来另存nums数组中的数据
                for (let i = 0; i < nums.length; i++) { // 2. 循环得到每一项每个 一个数
                    let otherVal = target - nums[i] // 3. 通过一个数 计算出 另一个数
                    if (Object.values(obj).includes(otherVal)) { // 4. 一开始对象中的value数组肯定不包含另一个数
                        // 6. values数组值中包含这个数的话,就查找这个值所对应的索引即可
                        let k = (Object.values(obj)).findIndex((item) => { return item == otherVal })
                        return [k, i]
                    } else {
                        obj[i] = nums[i] // 5. 不包含没事,就用对象存一份,对象的key是索引i,对象的value是索引项i对应的值nums[i],如此方便后续匹配
                    }
                }
            };

思路和方案二使用数组的方式基本一样,都是通过差值,找找有没有另外一项,虽然能解决问题,但是这种写法是目前三种方案中最差的写法,因为使用了Object.values、includes、findIndex方法,所以效率非常低。提交力扣勉强能够通过,此处放上一张图,大家就知晓了

击败百分之五,基本上是倒数了,尴尬,不过没事,咱们要得主要是思路

方案四,使用Map集合判断另外一个值是否存在

首先,此方案是最优解,时间空间复杂度都处理的不错,再说Map集合的解决方案之前,我们先来复习一下Map集合的基本功能,以便于更好的理解后续代码

Map知识的简单复习

let map = new Map() // new实例化一个Map对象
// 添加key value
map.set('name', '孙悟空')
map.set('age', '500')
map.set('home', '花果山水帘洞')
console.log(map.has('name'));// 是否存在key等于name的这个属性名 // true
console.log(map.get('name'));// 通过key去取其value
console.log(map.has('score'));// 是否存在key等于score的这个属性名 // true
console.log('map集合', map);

我们来看看打印出来的'map集合'的结果:


仔细一下,呦,这map集合和对象长得差不多啊。我们可以简单的理解为Map集合是特殊的对象存储形式,详细的理解Map集合可以看一下mdn官方 https://developer.mozilla.org...

使用Map集合存储数组数据

假设我们有一个数组是:let nums = [19,24,25,28,30]

  • 我们来看一下使用对象是如何模拟存储这个数据的
let obj = {
    0:19,
    1:24,
    2:25,
    3:28,
    4:30
}
// 使用对象存储,对象的key键就是数组的下标索引,对象的value值就是数组的索引值。当然数组也可以理解为是一种特殊的对象
  • 我们再来看一下使用Map集合来存储这个数组
let map = new Map()
map.set(0, 19)
map.set(1, 24)
map.set(2, 25)
map.set(3, 28)
map.set(4, 30)
console.log('map集合', map);

看看打印'map集合':

这里一定要反过来哦,数组的第0项是19,可以map.set(0, 19)也可以map.set(19, 0)具体使用那种方式,看实际需求,本题目中使用map.set(19, 0)方式更加合适

好了,看到这里大家再看底下的具体代码,结合注释应该就更好理解了

Map集合解决两数之和问题代码

var twoSum = function (nums, target) {
    let map = new Map() // 1. 创建一个map集合用来存储数据
    for (let i = 0; i < nums.length; i++) { // 2. 循环得到每一项每个 一个数
        let otherVal = target - nums[i] // 3. 得到另外一个值
        if (map.has(otherVal)) { // 4. 一开始肯定是不存在的
            return [map.get(otherVal), i] // 6. 存在的话,就得到对应索引值,并返回
        } else {
            map.set(nums[i], i) // 5. 不存在就在map集合存一份如:map.set(19, 0)
        }
    }
};

这种方式还不错,毕竟Map集合的效率要高一些。注意区分:数组方式、对象方式、以及Map集合方式它们的判断是否存在、找索引的区别方式,这一点很重要!

总结

关于两数之和的解法,其实还有别的方式,本文这四种方式,也只是抛砖引玉,给大家捋一下思路,毕竟工作中,有思路了,问题基本上就解决80%了。

好记性不如烂笔头,记录一下吧^_^


水冗水孚
1.1k 声望589 粉丝

每一个不曾起舞的日子,都是对生命的辜负