vault backup: 2026-05-13 10:56:20
This commit is contained in:
+146
@@ -0,0 +1,146 @@
|
||||
---
|
||||
tags: ["LeetCode", "哈希表", "简单"]
|
||||
create time: 2026-05-13 14:30
|
||||
---
|
||||
|
||||
# 01-两数之和
|
||||
|
||||
## 题面
|
||||
|
||||
给定一个整数数组 `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]
|
||||
```
|
||||
|
||||
**约束:**
|
||||
|
||||
- `2 <= nums.length <= 10^4`
|
||||
- `-10^9 <= nums[i] <= 10^9`
|
||||
- `-10^9 <= target <= 10^9`
|
||||
- 只会存在一个有效答案
|
||||
|
||||
---
|
||||
|
||||
## 思路
|
||||
|
||||
> [!question] 💡 思考
|
||||
> 给定一个数字 x,如何快速判断 `target - x` 是否已经出现过?这就是本题的核心问题——"补数查找"。
|
||||
|
||||
### 方法一:暴力枚举(O(n²))
|
||||
|
||||
最直接的想法:两层循环枚举所有数对,检查和是否等于 target。
|
||||
|
||||
这个方案虽然正确,但不符合进阶要求。我们直接进入最优解法。
|
||||
|
||||
### 方法二:哈希表一次扫描 ⭐(O(n))
|
||||
|
||||
核心思想:**用空间换时间**。在遍历数组的同时,把已经走过的数字记录下来。每遇到一个新数字,先看看它的补数是否已经在记录里——找到了就返回,没找到就把当前数字放入记录,继续往后走。
|
||||
|
||||
为什么可以一遍完成?因为我们不需要「自己加自己」——题目明确说不让使用同一个元素两次,而我们是先查后放,当前元素还没入库时查到的肯定是之前其他位置的值。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["开始"] --> B{"遍历完数组?"}
|
||||
B -->|"是"| C["无解, 返回空"]
|
||||
B -->|"否"| D["取出当前值 num"]
|
||||
D --> E["计算 complement = target - num"]
|
||||
E --> F{"complement 在哈希表中?"}
|
||||
F -->|"是"| G["返回 [[hash[complement]], 当前位置]"]
|
||||
F -->|"否"| H["将 num → 索引 存入哈希表"]
|
||||
H --> B
|
||||
```
|
||||
|
||||
以 `nums = [2, 7, 11, 15], target = 9` 为例:
|
||||
|
||||
| 步骤 | 当前值 | 补数 | 哈希表状态 | 结果 |
|
||||
|------|--------|------|------------|------|
|
||||
| 1 | 2 | 7 | `{}` | 未找到,存入 `2→0` |
|
||||
| 2 | 7 | 2 | `{2:0}` | **找到!** 返回 `[0, 1]` |
|
||||
|
||||
**关键细节:**
|
||||
|
||||
- Go 中使用 `map[int]int` 实现哈希表,键为数值、值为索引
|
||||
- `val, ok := mp[key]` 的 ok 语义天然适合「判断 key 是否存在」
|
||||
- 插入操作直接 `mp[num] = i`,简洁高效
|
||||
|
||||
---
|
||||
|
||||
## 代码提示
|
||||
|
||||
```
|
||||
// 伪代码模板
|
||||
初始化空哈希表 mp
|
||||
for i 从 0 到 n-1:
|
||||
complement = target - nums[i]
|
||||
if complement 在 mp 中:
|
||||
return [mp[complement], i]
|
||||
mp[nums[i]] = i
|
||||
```
|
||||
|
||||
Go 语言中使用多返回值判断 key 是否存在:
|
||||
|
||||
```go
|
||||
idx, exists := mp[complement]
|
||||
if exists {
|
||||
// found it!
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技巧
|
||||
|
||||
> [!tip] 🔑 核心模式:补数查找
|
||||
> 「两数之和」是补数查找的经典范式。任何「寻找两个元素满足某种和/差关系」的问题,都可以尝试这个模式:一边遍历一边维护已访问元素的哈希表。
|
||||
|
||||
> [!note] 🐹 Go 中的 HashMap 陷阱
|
||||
> - Go 的 `map` 查找不存在的 key 会返回零值,所以必须用双返回值 `(val, ok)` 来区分「key 不存在」和「key 对应零值」
|
||||
> - 本题不会出现此冲突(nums[i] ≥ 0 时),但处理负数或需要判断 nil 时要格外注意
|
||||
|
||||
> [!info] 📊 复杂度分析
|
||||
> - 时间:O(n),只扫描一次数组,每次哈希表操作均摊 O(1)
|
||||
> - 空间:O(n),最坏情况下需要存储 n-1 个元素
|
||||
|
||||
---
|
||||
|
||||
## 代码
|
||||
|
||||
```go
|
||||
func twoSum(nums []int, target int) []int {
|
||||
mp := make(map[int]int) // 数值 -> 索引的映射
|
||||
|
||||
for i, num := range nums {
|
||||
complement := target - num
|
||||
if idx, ok := mp[complement]; ok {
|
||||
return []int{idx, i}
|
||||
}
|
||||
mp[num] = i
|
||||
}
|
||||
|
||||
return nil // 理论上不会执行到此处(题目保证有解)
|
||||
}
|
||||
```
|
||||
|
||||
> [!success] ✅ 运行验证
|
||||
> 这是 LeetCode 第 1 题,通过率约 50%,属于入门必会的经典题目。掌握补数查找模式后,类似变体如「两数之和 II」「四数之和」都能迎刃而解。
|
||||
Reference in New Issue
Block a user