开篇
当初第一次面试前端,上来就甩这道题,我傻乎乎写了双层循环,面试官笑着问我有没有更优解,当场卡壳。后来才明白,这题看着简单,实则是哈希表入门天花板,能不能想出 O (n) 解法直接区分刷题新手和老手。
题目简述
给一个数字数组和目标和 target,找出数组里相加等于 target 的两个数字,返回它们的下标。限制条件很关键:数组只有唯一一组答案,同一个元素不能用两次,下标顺序随便返回。
第一种思路:暴力双层循环(新手第一反应)
拿到题第一眼,我脑子里第一个念头就是挨个配对。说白了就是固定第一个数,再遍历后面所有数求和判断。拿示例 [2,7,11,15] target=9 举例:先拿 2,往后依次加 7、11、15,2+7 刚好等于 9,直接返回 0 和 1。
JS 暴力代码
javascript
var twoSum = function(nums, target) {
// 外层固定第一个数字
for(let i = 0; i < nums.length; i++) {
// j从i+1开始,避免同一个元素重复使用
for(let j = i + 1; j < nums.length; j++) {
if(nums[i] + nums[j] === target) {
return [i, j]
}
}
}
};
复杂度:两层循环嵌套,时间 O (n²),空间 O (1)。数组长度上限是 10⁴,极端情况会循环上亿次,直接超时,面试写这个大概率扣分。
第二种思路:哈希表,一次遍历搞定(最优解)
面试官想要的就是这个方法,我当时没转过弯,后来想通逻辑后直呼巧妙。核心逻辑:我们要找 a + b = target,等价于 b = target - a。遍历数组的时候,用 map 存已经走过的数字和它的下标。每拿到当前数字 a,先去 map 里查有没有 target - a 这个 b:
- 存在:直接取出 b 的下标,和当前 i 一起返回
- 不存在:把当前数字 a 和下标存入 map,继续往下走
还是用示例 [2,7,11,15] target=9 走一遍流程:
- i=0,数字 = 2,需要找 9-2=7,map 是空的,存入 {2:0}
- i=1,数字 = 7,需要找 9-7=2,map 里刚好有 2,对应下标 0,返回 0,1,遍历直接结束
这里有个坑我踩过:不要先把全部数字放进 map 再遍历,遇到 [3,3] target=6 这种用例,会直接拿到同一个下标 0。边遍历边存就能完美避开,因为只会匹配前面已经存入的元素,不会匹配自己。
JS 哈希表完整代码
javascript
var twoSum = function(nums, target) {
// key:数组数字,value:数字对应的下标
const map = new Map();
for(let i = 0; i < nums.length; i++) {
const cur = nums[i];
// 计算我们需要配对的另一个数字
const need = target - cur;
// 如果map里存在这个数字,直接返回下标
if(map.has(need)) {
return [map.get(need), i]
}
// 找不到就把当前数字存进map,留给后面的数字配对
map.set(cur, i)
}
};
复杂度:只遍历一次数组,时间 O (n),空间 O (n),用哈希空间换时间,进阶要求直接满足。
两种解法简单对比
暴力循环不用额外空间,但数据量大就超时,适合刚入门理解题意;哈希表牺牲一点内存,换来线性时间,是面试标准答案,也是日常开发推荐写法。
总结 & 互动
这道题看着简单,其实是在考察你会不会用哈希表优化查询,很多人卡就卡在没想到转换等式 b = target - a。我当初踩的大坑:双层循环写得飞起,完全没往哈希表上想,面试当场尴尬住。另外注意题目限制,数组里会有重复数字,边遍历边存 map 的写法才能处理 [3,3] 这种案例。
你面试有没有被问到过这道两数之和?最开始写的是暴力解法还是哈希?评论区聊聊,每条我都会回。如果这篇题解帮你理清了思路,点个赞让更多刷题的小伙伴看到~