每日算法题-leetcode第1题-两数之和

186 阅读3分钟

Offer 驾到,掘友接招!我正在参与 2022 春招打卡活动,点击查看活动详情

leetcode 今日题目第 1 题-两数之和

一、题目描述

给定一个整数数组 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]

二、思路分析

一个“淳朴”的解法

这道题相信很多同学看一眼就很快能得出一个最基本的思路:两层循环来遍历同一个数组;第一层循环遍历的值记为 a,第二层循环时遍历的值记为 b;若 a+b = 目标值,那么 a 和 b 对应的数组下标就是我们想要的答案。

对“淳朴”解法的反思

大家以后做算法题的时候,要有这样的一种本能:当发现自己的代码里有两层循环时,先反思一下,能不能用空间换时间,把它优化成一层循环。

因为两层循环很多情况下都意味着 O(n^2) 的复杂度,这个复杂度非常容易导致你的算法超时。即便没有超时,在明明有一层遍历解法的情况下,你写了两层遍历,面试官对你的印象分会大打折扣。

空间换时间,Map 来帮忙

拿我们这道题来说,其实二层遍历是完全不必要的。

大家记住一个结论:几乎所有的求和问题,都可以转化为求差问题。 这道题就是一个典型的例子,通过把求和问题转化为求差问题,事情会变得更加简单。

我们可以在遍历数组的过程中,增加一个 Map 来记录已经遍历过的数字及其对应的索引值。然后每遍历到一个新数字的时候,都回到 Map 里去查询 targetNum 与该数的差值是否已经在前面的数字中出现过了。若出现过,那么答案已然显现,我们就不必再往下走了。

我们以 nums = [2, 7, 11, 15] 这个数组为例,来模拟一下这个思路:

  • 第一次遍历到 2,此时 Map 为空;
  • 以 2 为 key,索引 0 为 value 作存储,继续往下走;遇到了 7;
  • 计算 targetNum 和 7 的差值为 2,去 Map 中检索 2 这个 key,发现是之前出现过的值;
  • 那么 2 和 7 的索引组合就是这道题的答案啦。
  • 键值对存储我们可以用 ES6 里的 Map 来做,如果图省事,直接用对象字面量来定义也没什么问题。

三、编码实现

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
const twoSum = function (nums, target) {
  // 这里我用对象来模拟 map 的能力
  const diffs = {};
  // 缓存数组长度
  const len = nums.length;
  // 遍历数组
  for (let i = 0; i < len; i++) {
    // 判断当前值对应的 target 差值是否存在(是否已遍历过)
    if (diffs[target - nums[i]] !== undefined) {
      // 若有对应差值,那么答案get!
      return [diffs[target - nums[i]], i];
    }
    // 若没有对应差值,则记录当前值
    diffs[nums[i]] = i;
  }
};

tips:这道题也可以用 ES6 中的 Map 来做,你试试呢?

四、总结

本题采用了 map 的解决方案,其实还可以用双指针法来解决。

答案参考:www.yuque.com/xiumubai/gp…

五、最后

我是朽木白,一个热爱分享的程序猿。如果觉得本文还不错,记得点赞+关注 ❤️