题目描述
在数组里找两个数,使它们相加等于 target,返回这两个数的下标。
思路解析
先看最直接的笨办法:拿一个数,去和后面每个数挨个相加试试看。
笨办法 · 固定 5 挨个试:固定第一个数 5,先和 2 相加 = 7,不对。指针继续往后挪。
笨办法 · 还在试:5 和后面 7、11、15 全试完都不对,只好再固定 2,从头再来一遍。每一对都要算。
数组有 1 万个数,就要算约 1 亿 次。问题出在「每次都要回头把后面全找一遍」。
一边扫数组,一边用一个哈希表记下「见过的数 → 它的下标」。每到一个数,只要查一下「我的搭档之前记过没有」,一步就能找到。
准备开始:哈希表一开始是空的。我们从 0 号位开始,从左往右扫。
i = 0 · 看 5:当前是 5。要凑成 13,就还差一个 8。去哈希表里找 8——现在表是空的,没有。
i = 0 · 没找到:没找到搭档,就把自己记进哈希表:5 在 0 号位,写成 5 → 0。继续下一个。
i = 1 · 看 2:当前是 2,还差 11。查哈希表,只有 5,没有 11。
i = 1 · 没找到:把 2 也记进表:2 → 1。
i = 2 · 看 7:当前是 7,还差 6。表里没有 6。
i = 2 · 没找到:把 7 记进表:7 → 2。注意表越来越「全」了。
i = 3 · 看 11 · 重点:当前是 11,还差 2。查哈希表——2 之前记过!
i = 3 · 找到了!:哈希表里 2 → 1,说明 2 在 1 号位。再加上现在的 3 号位,搭档凑齐,返回 [1, 3],结束。
笨办法每个数都要回头找一遍,是 n×n 次;哈希表只扫一遍,一万个数从一亿次降到一万次。
「回头找」改成「记下来、秒查到」。这是算法里最常用的一招,后面很多题都会用到哈希表。
参考代码(Python)
1class Solution:
2 def twoSum(self, nums, target):
3 map = dict() # 哈希表:值 → 下标
4 for i, num in enumerate(nums):
5 anotherNum = target - num # 还差的搭档
6 if anotherNum in map: # 之前记过吗?
7 return [map[anotherNum], i]
8 map[num] = i # 没有就记下自己
9 return []复杂度分析
- 时间复杂度:O(n) —— 数组只扫一遍
- 空间复杂度:O(n) —— 哈希表最多存 n 个数
套路模板
记住这个骨架:先算 need、先查、再存自己。三数之和、四数之和,就是在它外面再套一层循环。
1# 凡是「找一对 / 找另一半」的题都能套
2seen = {}
3for i, x in enumerate(nums):
4 need = target - x # 想找的另一半
5 if need in seen: # 先查
6 return [seen[need], i]
7 seen[x] = i # 再存自己易错点
- ❌ 先 seen[x]=i 再查 need → ✅ 先查 need,再存自己(顺序反了,nums=[3,3] 会把自己当搭档)
- ❌ seen 里存 下标 → 值 → ✅ 存 值 → 下标(要返回下标)(查的是 need 这个「值」,所以键必须是值)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。