From 06fafb84512b653b0ad086caedf051ff633561ef Mon Sep 17 00:00:00 2001 From: wonder Date: Wed, 13 May 2026 10:56:20 +0800 Subject: [PATCH] vault backup: 2026-05-13 10:56:20 --- 哈希/01-两数之和.md | 146 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 哈希/01-两数之和.md diff --git a/哈希/01-两数之和.md b/哈希/01-两数之和.md new file mode 100644 index 0000000..49fbaf9 --- /dev/null +++ b/哈希/01-两数之和.md @@ -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」「四数之和」都能迎刃而解。