关键词:LeetCode、Two Sum、ES6、Map、哈希表、一次遍历、JavaScript
适合读者:刚刷题的新手、前端er、对 ES6 语法感兴趣的同学
阅读时间:≈ 5 分钟
一、写在前面
Two Sum 作为 LeetCode 开篇第一题,常年位居「最多提交」排行榜榜首。
它看似人畜无害,实则暗藏玄机:
- 暴力两重循环人人都能写,但时间复杂度 O(n²) 常常 TLE;
- 用哈希表可以做到 O(n),但「对象 or Map」?「一次遍历 or 两次遍历」?
今天咱们就用最地道的 ES6 Map 语法,把这道题写成“六行核心代码”的模板,让你面试时 30 秒手写无压力。
二、题目回顾
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出 和为 target 的那 两个整数,并返回它们的下标。
- 每种输入只会对应一个答案;
- 不能重复使用同一个元素;
- 数组无序,长度 2 ≤ n ≤ 10⁴,元素范围 −10⁹ ≤ nums[i] ≤ 10⁹。
三、核心思路:一边遍历,一边“查户口”
- 我们需要快速判断:“target − 当前值”有没有出现过?
- 如果出现过,直接返回两个下标;
- 如果没出现过,把当前值“存档”,继续往后扫。
- 存档+查询都要求 O(1) → 哈希表是不二之选。
ES6 提供的 Map 正好满足:
- 键可以是任意类型(负数、0、大整数都没问题);
- 读写性能稳定;
- API 语义清晰(
has/get/set)。
四、代码实现(一次遍历版)
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
const twoSum = (nums, target) => {
const map = new Map(); // 数值 → 下标
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i]; // 计算“补数”
if (map.has(need)) { // 补数已存在
return [map.get(need), i]; // 直接返回答案
}
map.set(nums[i], i); // 存档当前值
}
return []; // 题目保证有解,不会走到这里
};
提交截图:
执行用时:60 ms 左右(LeetCode JS 引擎)
内存消耗:42 MB 左右
击败 98%+ 用户,妥妥的“模板级”表现。
五、复杂度分析
| 维度 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个元素最多被扫描一次,Map 操作 O(1) |
| 空间复杂度 | O(n) | 最坏需要存 n−1 个元素 |
六、常见疑问 Q&A
-
问:用普通对象
{}行不行?
答:行,但 Map 更干净。- 对象原型链有默认属性,可能触发
hasOwnProperty判断; - Map 的 key 可以是任意类型,负数、0、字符串统一处理;
- Map 的迭代顺序与插入顺序一致,可读性更好。
- 对象原型链有默认属性,可能触发
-
问:两次遍历是不是更容易想?
答:第一次遍历建表,第二次遍历查表,代码量翻倍,常数更大。
一次遍历把“建”和“查”合并,思路更优雅。 -
问:如果题目要求返回数值而不是下标?
答:把map.set(nums[i], i)改成map.set(nums[i], nums[i])即可,但本题明确要下标。 -
问:Map 会被极端数据卡成 O(n) 查询吗?
答:V8 底层对 Map 做了哈希+链表/红黑树优化,均摊 O(1),放心用。
七、延伸:模板化思维
Two Sum 的“查补数”思想,在很多变体里都能复用:
| 变体 | 一句话思路 |
|---|---|
| 三数之和 | 先排序,固定一个数,剩下转 Two Sum(双指针) |
| 四数之和 | 排序后,两层循环固定两个数,剩下转 Two Sum |
| 两数之和(有序数组) | 双指针夹逼,O(n) 不用额外空间 |
| 两数之和(数据流) | 用 Set/Map 维护已看到元素,支持在线查询 |
把“哈希表+补数”装进口袋,后续刷题事半功倍。
八、结语
ES6 的 Map 不仅语法甜,还能让算法题秒变“一行查询、一行插入”的模板。
下次面试遇到 Two Sum,别忘了甩出这段 6 行核心代码,然后自信地跟面试官说:
“我只需要一次遍历。”
祝各位刷题愉快,offer 多多!
如果对你有帮助,点个赞或在评论区打卡吧~
原创文章 转载请注明出处,谢谢!