想系统提升编程能力、查看更完整的学习路线,欢迎访问 AI Compass:github.com/tingaicompa… 仓库持续更新刷题题解、Python 基础和 AI 实战内容,适合想高效进阶的你。
📖 第1课:两数之和
模块:哈希表入门 | 难度:Easy ⭐ LeetCode 链接:leetcode.cn/problems/tw… 前置知识:无(本课是起点课) 预计学习时间:20分钟
🎯 题目描述
给你一个整数数组和一个目标值,请你在数组中找出两个数,使它们的和恰好等于目标值。返回这两个数的下标(不是值本身)。
题目保证:每组输入恰好有唯一解,同一个元素不能用两次。
示例:
输入:nums = [2, 7, 11, 15], target = 9
输出:[0, 1]
解释:nums[0] + nums[1] = 2 + 7 = 9,所以返回下标 [0, 1]
输入:nums = [3, 2, 4], target = 6
输出:[1, 2]
解释:nums[1] + nums[2] = 2 + 4 = 6
约束条件:
2 <= nums.length <= 10^4(数组最少2个元素,最多1万个)-10^9 <= nums[i] <= 10^9(元素可以为负数或零)-10^9 <= target <= 10^9- 只会存在一个有效答案
🧪 边界用例(面试必考)
| 用例类型 | 输入 | 期望输出 | 考察点 |
|---|---|---|---|
| 最小输入 | nums=[1,2], target=3 | [0,1] | 数组只有2个元素,直接配对 |
| 含负数 | nums=[-3,4,3,90], target=0 | [0,2] | 负数 + 正数 = 0,别忘了负数 |
| 含零 | nums=[0,4,3,0], target=0 | [0,3] | 0+0=0,两个不同下标的0 |
| 含重复值 | nums=[3,3], target=6 | [0,1] | 值相同但下标不同,不能用同一元素 |
| 目标为负 | nums=[-1,-2,-3], target=-4 | [0,2] | target本身可以是负数 |
| 大规模 | n=10^4 | — | 暴力法约 5×10^7 次,刚好卡边界 |
💡 思路引导
生活化比喻
想象你在一个热闹的跳蚤市场买东西,手里有 9 块钱(target=9),你要找到两件商品的价格加起来正好等于 9 块。
🐌 笨办法:你拿起第一件商品(比如标价 2 元),然后把市场里所有其他商品都翻一遍,看看有没有标价 7 元的。没找到?放下,再拿第二件,又从头翻一遍……这样逛下去,你可能累断腿才找到。每拿一件商品,都要扫一遍整个市场,效率极低。
🚀 聪明办法:你带了一个小本本(哈希表)。每走到一个摊位,先不急着全市场找配对——而是低头翻翻小本本:"之前有没有见过能和这件凑成 9 块的?" 没有?就把当前商品的价格和摊位号记到小本本里,然后继续逛。因为小本本翻一下只需要一瞬间,所以你只需要逛市场一圈,就能找到答案。这就是"空间换时间"的威力——多带个小本本,省下无数条腿。
关键洞察
与其对每个数都去数组里"搜索"它的互补数,不如一边遍历一边"记住"见过的数,让互补数自己来找你。
🧠 解题思维链
这一节模拟你在面试中"从零开始思考"的过程。
Step 1:理解题目 → 锁定输入输出
- 输入:一个整数数组
nums(长度 2~10^4),一个整数target - 输出:两个下标
[i, j](不是值!面试中经常有人搞混) - 限制:恰好一个解;同一元素不能用两次(
i != j);元素可以重复、可以为负
Step 2:先想笨办法(暴力法)
最直觉的做法——两两配对试一试。用两层循环,外层选第一个数、内层选第二个数,看它们的和是否等于 target。
- 时间复杂度:O(n^2)
- 瓶颈在哪:内层循环在做"查找某个特定值是否存在",每次查找都要线性扫描
Step 3:瓶颈分析 → 优化方向
暴力法的核心问题:对于外层遍历的每一个 nums[i],我们需要在剩余元素中查找 target - nums[i]。这个"查找"操作是 O(n),导致总体 O(n^2)。
- 核心问题:"查找互补数"这一步太慢了——每次都要遍历一遍
- 优化思路:能不能把"查找"从 O(n) 降到 O(1)?→ 能!用哈希表存储已经见过的数
Step 4:选择武器
- 选用:哈希表(Python 字典 dict)
- 理由:字典支持 O(1) 的键查找,正好把"查找互补数"这个瓶颈从 O(n) 压缩到 O(1),总体从 O(n^2) 降为 O(n)
🔑 模式识别提示:当题目出现"查找配对"、"两数之和/差/积"、"是否存在某个互补值",优先考虑"一遍哈希表"模式
🔑 解法一:暴力双循环(直觉法)
思路
最朴素的想法:把所有两两组合都试一遍。外层循环选第一个数 nums[i],内层循环选第二个数 nums[j](j > i 避免重复配对),检查 nums[i] + nums[j] 是否等于 target。
图解过程
示例:nums = [2, 7, 11, 15], target = 9
Step 1: i=0, nums[i]=2
j=1: 2+7=9 ✅ 命中!返回 [0, 1]
+-----+-----+------+------+
| [2] | [7] | [11] | [15] |
+-----+-----+------+------+
i=0 j=1
↓
2+7=9 == target ✅ → return [0, 1]
示例:nums = [3, 2, 4], target = 6
Step 1: i=0, nums[i]=3
j=1: 3+2=5 ❌
j=2: 3+4=7 ❌
+-----+-----+-----+
| [3] | [2] | [4] |
+-----+-----+-----+
i=0 j=1 j=2
3+2=5 ❌ 3+4=7 ❌
Step 2: i=1, nums[i]=2
j=2: 2+4=6 ✅ 命中!返回 [1, 2]
+-----+-----+-----+
| [3] | [2] | [4] |
+-----+-----+-----+
i=1 j=2
↓
2+4=6 == target ✅ → return [1, 2]
Python代码
from typing import List
def two_sum_brute(nums: List[int], target: int) -> List[int]:
"""
解法一:暴力双循环
思路:枚举所有 (i,j) 组合,检查 nums[i]+nums[j] 是否等于 target
"""
n = len(nums) # 数组长度
for i in range(n): # 外层:选第一个数
for j in range(i + 1, n): # 内层:选第二个数(j>i 避免重复)
if nums[i] + nums[j] == target: # 检查是否命中
return [i, j] # 题目保证有解,直接返回
return [] # 理论上不会到这里(保险写法)
# ✅ 测试
print(two_sum_brute([2, 7, 11, 15], 9)) # 期望输出:[0, 1]
print(two_sum_brute([3, 2, 4], 6)) # 期望输出:[1, 2]
print(two_sum_brute([3, 3], 6)) # 期望输出:[0, 1] (重复值)
print(two_sum_brute([-3, 4, 3, 90], 0)) # 期望输出:[0, 2] (含负数)
print(two_sum_brute([0, 4, 3, 0], 0)) # 期望输出:[0, 3] (0+0=0)
复杂度分析
- 时间复杂度:O(n^2) — 两层循环,每层最多 n 次
- 具体地说:如果 n=10000,最坏需要约 10000×9999/2 ≈ 5×10^7 次操作,勉强能过但很慢
- 空间复杂度:O(1) — 只用了几个变量,没有额外数据结构
优缺点
- ✅ 思路直观,代码简单,不容易出bug
- ✅ 不需要额外空间
- ❌ 时间复杂度 O(n^2),数据量大时太慢——内层循环的"查找"是瓶颈,能不能用更快的方式查找?
⚡ 解法二:一遍哈希表(最优解)
优化思路
暴力法的痛点在于"查找互补数"太慢。我们用一个字典(哈希表)来记住已经遍历过的数:键是元素值,值是下标。这样每到一个新元素,只需在字典里 O(1) 查找它的互补数 target - nums[i] 是否已经出现过。
💡 关键想法:一边遍历一边建字典,对当前元素先查再存——如果先存再查,会错误地把自己和自己配对。
图解过程
示例:nums = [2, 7, 11, 15], target = 9
字典 seen = {}(一开始是空的)
╔══════════════════════════════════════════════════════════════╗
║ i=0, num=2 ║
║ complement = 9 - 2 = 7 ║
║ 7 在 seen 里吗?seen = {} → ❌ 不在 ║
║ → 把 2 记进去:seen = {2: 0} ║
╠══════════════════════════════════════════════════════════════╣
║ i=1, num=7 ║
║ complement = 9 - 7 = 2 ║
║ 2 在 seen 里吗?seen = {2: 0} → ✅ 在!下标是 0 ║
║ → 返回 [0, 1] ║
╚══════════════════════════════════════════════════════════════╝
结果:[0, 1] ✅
示例:nums = [3, 2, 4], target = 6
字典 seen = {}
╔══════════════════════════════════════════════════════════════╗
║ i=0, num=3 ║
║ complement = 6 - 3 = 3 ║
║ 3 在 seen 里吗?seen = {} → ❌ ║
║ → seen = {3: 0} ║
╠══════════════════════════════════════════════════════════════╣
║ i=1, num=2 ║
║ complement = 6 - 2 = 4 ║
║ 4 在 seen 里吗?seen = {3: 0} → ❌ ║
║ → seen = {3: 0, 2: 1} ║
╠══════════════════════════════════════════════════════════════╣
║ i=2, num=4 ║
║ complement = 6 - 4 = 2 ║
║ 2 在 seen 里吗?seen = {3:0, 2:1} → ✅ 在!下标是 1 ║
║ → 返回 [1, 2] ║
╚══════════════════════════════════════════════════════════════╝
结果:[1, 2] ✅
边界用例:nums = [3, 3], target = 6
╔══════════════════════════════════════════════════════════════╗
║ i=0, num=3 ║
║ complement = 6 - 3 = 3 ║
║ 3 在 seen 里吗?seen = {} → ❌(还没放进去呢!) ║
║ → seen = {3: 0} ║
╠══════════════════════════════════════════════════════════════╣
║ i=1, num=3 ║
║ complement = 6 - 3 = 3 ║
║ 3 在 seen 里吗?seen = {3: 0} → ✅ 在!下标是 0 ║
║ → 返回 [0, 1] ║
╚══════════════════════════════════════════════════════════════╝
关键:先查再存,所以 i=0 时不会把自己和自己配对 ✅
Python代码
from typing import List
def two_sum_hash(nums: List[int], target: int) -> List[int]:
"""
解法二:一遍哈希表
思路:遍历数组,对每个元素查字典里有没有它的互补数
"""
seen = {} # 字典:值 → 下标
for i, num in enumerate(nums): # 同时获取下标和值
complement = target - num # 计算互补数
if complement in seen: # O(1) 查找:互补数见过吗?
return [seen[complement], i] # 见过!返回两个下标
seen[num] = i # 没见过,记住当前数和下标
return [] # 保险写法
# ✅ 测试
print(two_sum_hash([2, 7, 11, 15], 9)) # 期望输出:[0, 1]
print(two_sum_hash([3, 2, 4], 6)) # 期望输出:[1, 2]
print(two_sum_hash([3, 3], 6)) # 期望输出:[0, 1] (重复值)
print(two_sum_hash([-3, 4, 3, 90], 0)) # 期望输出:[0, 2] (含负数)
print(two_sum_hash([0, 4, 3, 0], 0)) # 期望输出:[0, 3] (0+0=0)
复杂度分析
- 时间复杂度:O(n) — 只遍历一次数组,每次字典查找 O(1)
- 具体地说:如果 n=10000,只需要约 10000 次操作,比暴力法快 5000 倍!
- 空间复杂度:O(n) — 字典最多存 n 个键值对
- 典型的"空间换时间"策略
🐍 Pythonic 写法
利用 Python 字典推导式和 enumerate 的简洁写法:
from typing import List
def two_sum_pythonic(nums: List[int], target: int) -> List[int]:
"""
Pythonic 写法:利用字典推导 + enumerate
注意:这里先建完整字典再查找,适用于无重复值或需要后出现的下标
"""
# 一行建立 {值: 下标} 的映射(重复值会保留最后出现的下标)
index_map = {val: idx for idx, val in enumerate(nums)}
# 遍历查找互补数
for i, num in enumerate(nums):
complement = target - num
# complement 存在且不是自己(用下标判断,避免自己配自己)
if complement in index_map and index_map[complement] != i:
return [i, index_map[complement]]
return []
# ✅ 测试
print(two_sum_pythonic([2, 7, 11, 15], 9)) # 期望输出:[0, 1]
print(two_sum_pythonic([3, 2, 4], 6)) # 期望输出:[1, 2]
print(two_sum_pythonic([3, 3], 6)) # 期望输出:[0, 1]
这个写法用到了:
- 字典推导式
{val: idx for idx, val in enumerate(nums)}:一行代码把列表变成值→下标的映射 enumerate():同时获取下标和值,比手动维护计数器更 Pythonic- 注意判断
index_map[complement] != i,防止同一个元素被用两次
⚠️ 面试建议:先写解法二(一遍哈希表)展示清晰的思路和"先查再存"的技巧,再提一嘴"也可以用字典推导式先建完映射再查"来展示语言功底。面试官更看重你的思考过程,而非代码行数。
📊 解法对比
| 维度 | 解法一:暴力双循环 | 解法二:一遍哈希表 | Pythonic写法 |
|---|---|---|---|
| 时间复杂度 | O(n^2) | O(n) | O(n) |
| 空间复杂度 | O(1) | O(n) | O(n) |
| 代码难度 | 简单 | 简单 | 简单 |
| 面试推荐 | ⭐ | ⭐⭐⭐ | ⭐⭐ |
| 适用场景 | 只适合小规模数据或说明思路 | 面试首选,通用性强 | 快速编码、代码竞赛 |
面试建议:先用 30 秒口述暴力法思路和复杂度(表明你能想到基本解法),然后直接优化到一遍哈希表(展示优化能力)。不需要把暴力法代码完整写出来,把时间留给最优解的编码和测试。
🎤 面试现场
模拟面试中的完整对话流程,帮你练习"边想边说"。
面试官:来看一道经典题——给你一个整数数组和一个目标值,找出两个数使它们的和等于目标值,返回它们的下标。
你:(审题 30 秒)好的,让我确认一下——输入是一个整数数组和一个 target,输出是两个不同的下标,对吧?每组输入保证有唯一解?
面试官:没错。
你:好的。我先想一个最直接的办法:两层 for 循环,把所有二元组都试一遍,时间 O(n^2),空间 O(1)。不过这个显然不够快。
让我想想怎么优化……瓶颈在于:对于每个数 nums[i],我需要查找 target - nums[i] 是否存在。查找操作是 O(n),如果我能把查找降到 O(1) 就好了——用哈希表!
具体做法是:一边遍历数组,一边维护一个字典,键是值、值是下标。对每个元素,先看字典里有没有它的互补数。有就直接返回两个下标;没有就把当前数存进字典,继续。这样整体时间 O(n),空间 O(n)。
面试官:思路清晰,请写代码吧。
你:好的。(边写边说)
def twoSum(self, nums, target):
seen = {} # 值 → 下标
for i, num in enumerate(nums):
complement = target - num
if complement in seen: # O(1) 查找
return [seen[complement], i]
seen[num] = i
我用 enumerate 同时拿到下标和值。关键是"先查再存"——这样可以避免一个元素和自己配对。
面试官:能手动跑一个例子验证一下吗?
你:好的,用 nums=[2, 7, 11, 15], target=9:
i=0, num=2:complement=7,seen={} 里没有 7 → 存入 seen={2:0}i=1, num=7:complement=2,seen={2:0} 里有 2 → 返回 [0, 1]
正确!再测个边界:nums=[3,3], target=6:
i=0, num=3:complement=3,seen={} 里没有 3 → 存入 seen={3:0}i=1, num=3:complement=3,seen={3:0} 里有 3 → 返回 [0, 1]
没有出现"自己配自己"的问题,因为 i=0 时先查(空字典,查不到)再存。✅
面试官:很好。如果我不让你用额外空间呢?
你:如果要求 O(1) 空间,可以先排序(O(n log n)),再用双指针从两端向中间逼近。但要注意排序会打乱原始下标,所以需要在排序前记录下标(比如排序 (value, index) 元组)。时间变成 O(n log n),空间 O(n) 或 O(1) 取决于排序实现。对这道题来说,哈希表解法的 O(n) 时间其实更优。
高频追问
| 追问 | 应答策略 |
|---|---|
| "还有更优解吗?" | 时间已经是 O(n) 最优(至少要看一遍所有元素),空间 O(n) 换来的速度提升很值得。如果题目允许改原数组,可以用排序+双指针做到 O(n log n) 时间、O(1) 额外空间,但时间反而更慢 |
| "如果数据量是 10 亿级别呢?" | 单机内存放不下哈希表,需要分桶(partition):按 nums[i] % K 分成 K 个桶,互补的两个数一定在"对称桶"里,可以分布式处理 |
| "如果有多组解怎么办?" | 把 return 改成 append 收集所有解;字典的值改为下标列表 defaultdict(list) 处理重复值 |
| "实际工程中什么场景会用到?" | 任何"查找配对"场景:交易撮合系统匹配买卖单、推荐系统找互补用户、网络协议中匹配请求和响应 |
🎓 知识点总结
Python技巧卡片 🐍
# enumerate() — 同时拿到下标和值,告别手动 i += 1
for i, val in enumerate(["a", "b", "c"]):
print(i, val) # 0 a / 1 b / 2 c
# dict 的 in 操作 — O(1) 判断键是否存在
seen = {2: 0, 7: 1}
print(7 in seen) # True ← 查的是键,不是值!
print(0 in seen) # False ← 0 是值不是键
# 字典推导式 — 一行创建字典
nums = [10, 20, 30]
index_map = {v: i for i, v in enumerate(nums)}
# 结果:{10: 0, 20: 1, 30: 2}
# dict.get(key, default) — 安全取值,键不存在时返回默认值
seen = {2: 0}
print(seen.get(7, -1)) # -1(不存在时返回 -1,不会报错)
💡 底层原理(选读)
哈希表为什么能 O(1) 查找?
你可以把哈希表想象成一个超大的储物柜。每个柜子有编号(0, 1, 2, ...)。当你要存一个东西(比如数字 7),哈希表会用一个哈希函数算出一个编号——比如
hash(7) % 柜子总数 = 3——然后直接放到 3 号柜子里。下次要找 7 的时候,不需要一个一个柜子翻过去,而是再算一次哈希,直接走到 3 号柜子打开看。一步到位,这就是 O(1) 的秘密。
那什么时候不是 O(1)? 当两个不同的值算出相同的编号("哈希冲突"),它们会被塞到同一个柜子里(用链表或其他方式串起来)。如果冲突非常多,最坏情况下一个柜子里全是东西,查找就退化为 O(n)。但 Python 的 dict 使用了开放寻址法 + 精心设计的哈希函数,在绝大多数实际场景下冲突很少,平均查找时间稳定在 O(1)。
Python 中 dict 和 set 的区别:
dict:存键值对(key → value),适合你需要"记住额外信息"的场景(如本题中记住下标)set:只存键(key),适合你只需要判断"有没有见过"的场景- 两者底层都是哈希表,查找都是 O(1)
算法模式卡片 📐
- 模式名称:一遍哈希表(One-pass Hash Map)
- 适用条件:在数组/序列中查找满足某种"配对关系"的两个(或多个)元素
- 识别关键词:两数之和/差/积、查找配对、是否存在互补值、频次统计
- 模板代码:
def one_pass_hash(nums, target):
"""
一遍哈希表模板:
- 遍历数组,对每个元素计算"需要的配对值"
- 先查字典(看配对值是否已存在),再存当前元素
- 关键:先查再存,避免自己配自己
"""
seen = {} # {值: 下标} 或 {值: 出现次数}
for i, num in enumerate(nums):
need = target - num # 计算配对值(根据题意调整运算)
if need in seen: # O(1) 查找
return [seen[need], i] # 找到配对,返回结果
seen[num] = i # 未找到,存入当前元素
return [] # 无解
易错点 ⚠️
-
返回下标 vs 返回值:题目要求返回的是下标,不是值本身。很多人匆忙写代码时直接
return [num, complement],面试直接扣分。解决:审题时在草稿纸上写清楚"输入/输出"再动手。 -
同一元素用两次:比如
nums=[3,2,4], target=6,如果你先把所有数存进字典再查找,可能会把nums[0]=3和它自己配对得到3+3=6(但实际上只有一个 3)。解决:先查再存,或者查找时检查index_map[complement] != i。 -
重复值覆盖:如果先建完字典再查找(Pythonic 写法),
nums=[3,3]中第二个 3 会覆盖第一个 3 的下标。需要注意这不影响正确性——因为我们从前往后遍历查找时,i=0的 complement=3 在字典里的下标是 1(被覆盖后的),返回[0, 1]正好正确。但如果对覆盖行为不放心,推荐使用"一遍哈希"的先查再存写法,逻辑更清晰可控。 -
忘记处理负数:题目的 nums 和 target 都可以是负数。
target - num在负数情况下依然成立,不需要特殊处理,但测试时要覆盖负数用例。
🏗️ 工程实战(选读)
这个算法思想在真实项目中的应用,让你知道"学了有什么用"。
-
数据库索引:数据库对某一列建立索引(本质是哈希表或B树),使得
SELECT * FROM users WHERE id = 12345不需要全表扫描,而是 O(1) 或 O(log n) 直接定位到目标行。两数之和的核心思想——"用空间换时间,把查找从线性变为常数"——正是数据库索引的灵魂。 -
交易撮合系统:证券/加密货币交易所的核心模块是"撮合引擎"。当一笔买单进来(比如"想以 100 元买 A 股"),系统需要快速找到是否有匹配的卖单("有人以 100 元卖 A 股吗?")。这就是一个"查找配对"问题,通常用哈希表按价格索引所有挂单,实现 O(1) 撮合。
-
网络协议中的请求-响应匹配:HTTP/2 和 gRPC 等协议中,客户端发送的请求带有唯一 ID,服务端返回响应时也带同一个 ID。客户端用一个哈希表
{请求ID: 回调函数}来存储等待中的请求,收到响应时 O(1) 找到对应的回调并执行。 -
缓存系统:Redis、Memcached 等缓存本质上就是一个大哈希表。
cache.get("user:1001")的 O(1) 查找能力让热数据访问延迟从毫秒级(数据库查询)降到微秒级。
🏋️ 举一反三
完成本课后,试试这些同类题目来巩固知识:
| 题目 | 难度 | 相关知识点 | 提示 |
|---|---|---|---|
| LeetCode 167. 两数之和 II - 输入有序数组 | Medium | 双指针 | 数组已排序,不需要哈希表,用左右双指针更优 |
| LeetCode 15. 三数之和 | Medium | 排序 + 双指针 | 固定一个数,内层变成两数之和;注意去重 |
| LeetCode 18. 四数之和 | Medium | 排序 + 双指针 | 三数之和再套一层循环,关键是剪枝和去重 |
| LeetCode 454. 四数相加 II | Medium | 哈希表 | 四个独立数组,两两分组用哈希表,思路和两数之和一模一样 |
| LeetCode 560. 和为 K 的子数组 | Medium | 前缀和 + 哈希表 | "前缀和之差=K" 转化为两数之和问题,非常经典 |
| LeetCode 217. 存在重复元素 | Easy | 哈希表(set) | 最简单的哈希表应用:见过就返回 True |
📝 课后小测
试试这道变体题,不要看答案,自己先想 5 分钟!
题目:给定一个整数数组 nums 和一个目标值 target,判断数组中是否存在两个数的差恰好等于 target(target > 0)。如果存在,返回 True,否则返回 False。
示例:nums = [5, 2, 8, 1], target = 3 → True(因为 5 - 2 = 3)
💡 提示 1(实在想不出来再点开)
"两数之差等于 target" 可以改写为 a - b = target,也就是 a = b + target。是不是有点像两数之和?
💡 提示 2(再给你一个线索)
遍历数组时,对于当前的数 num,它的"配对数"有两种情况:num + target(当前数是较小的那个)和 num - target(当前数是较大的那个)。在哈希表里查找这两个值就行了。
✅ 参考答案
def two_diff(nums: list[int], target: int) -> bool:
"""
两数之差:a - b = target
等价于查找:对每个 num,是否存在 num - target 或 num + target
"""
seen = set() # 只需判断存在性,用 set 足够
for num in nums:
# num 作为较大值:num - x = target → x = num - target
# num 作为较小值:x - num = target → x = num + target
if (num - target) in seen or (num + target) in seen:
return True
seen.add(num)
return False
# 测试
print(two_diff([5, 2, 8, 1], 3)) # True (5-2=3)
print(two_diff([1, 2, 3, 4], 10)) # False (没有差为10的配对)
print(two_diff([1, 5, 3, 4, 2], 0)) # False (target>0,不需要考虑0)
核心思路:把"两数之差"问题转化为和两数之和一样的"哈希查找配对"模式。区别在于配对值有两个方向(num ± target),而两数之和只有一个方向(target - num)。这里只需要判断存在性不需要返回下标,所以用 set 比 dict 更合适。
如果这篇内容对你有帮助,推荐收藏 AI Compass:github.com/tingaicompa… 更多系统化题解、编程基础和 AI 学习资料都在这里,后续复习和拓展会更省时间。