Files
leetcode-go/哈希/01-两数之和.md
T

4.2 KiB
Raw Blame History

tags, create time
tags create time
LeetCode
哈希表
简单
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))

核心思想:用空间换时间。在遍历数组的同时,把已经走过的数字记录下来。每遇到一个新数字,先看看它的补数是否已经在记录里——找到了就返回,没找到就把当前数字放入记录,继续往后走。

为什么可以一遍完成?因为我们不需要「自己加自己」——题目明确说不让使用同一个元素两次,而我们是先查后放,当前元素还没入库时查到的肯定是之前其他位置的值。

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 是否存在:

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 个元素

代码

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」「四数之和」都能迎刃而解。