**标签**：`数组` `哈希表`
## 题目理解

- **输入**：一个整数数组 `nums` 和一个整数目标值 `target`。
- **输出**：两个整数的下标，这两个下标对应的数组元素之和等于 `target`。
- **约束条件**：
  - 数组长度至少为 2。
  - 数组中每个数和目标值的范围很大（-10⁹ 到 10⁹），但 Python 的 int 可以处理。
  - **保证有且只有一对下标满足条件**，且不能重复使用同一个元素（即两个下标必须不同）。
- **例子**：`nums = [2, 7, 11, 15]`，`target = 9`。因为 `2 + 7 = 9`，它们的位置分别是 0 和 1，所以输出 `[0, 1]`。

---

## 解法一：暴力枚举（最简单解法）

> 这是最直接、最容易想到的方法。虽然速度慢，但能清晰展示解题逻辑，适合零基础入门。

### 思路

1. 我们需要找到两个不同的位置 `i` 和 `j`，使得 `nums[i] + nums[j] == target`。
2. 最笨的办法：**固定第一个数**，然后**依次检查它后面的每一个数**，看它们的和是否等于目标值。
3. 具体步骤：
   - 用第一个循环变量 `i` 遍历数组的每一个位置（从 0 到 倒数第二个，因为最后一个数后面没有数可以和它配对）。
   - 对每一个 `i`，再用第二个循环变量 `j` 遍历从 `i+1` 到最后一个位置。
   - 如果 `nums[i] + nums[j] == target`，就立即返回 `[i, j]`。
4. 因为题目保证一定有答案，所以循环一定会返回。

### 代码

```python
class Solution:
    def twoSum(self, nums: list[int], target: int) -> list[int]:
        # 遍历数组，i 是第一个数的下标
        for i in range(len(nums) - 1):          # 最后一个数不需要作为 i，因为 j 从 i+1 开始
            # 对于每个 i，遍历它后面的所有数，j 是第二个数的下标
            for j in range(i + 1, len(nums)):
                # 如果这两个数的和等于目标值，就找到了答案
                if nums[i] + nums[j] == target:
                    return [i, j]
        # 理论上不会执行到这里，因为题目保证有解
        return []

# 测试示例
sol = Solution()
print(sol.twoSum([2, 7, 11, 15], 9))   # 期望输出：[0, 1]
print(sol.twoSum([3, 2, 4], 6))        # 期望输出：[1, 2]
print(sol.twoSum([3, 3], 6))           # 期望输出：[0, 1]
```

### 复杂度分析

| 维度 | 复杂度 | 说明 |
|------|--------|------|
| 时间 | O(n²) | 有两层循环，最坏情况下要检查大约 n²/2 次加法 |
| 空间 | O(1) | 只用了几个固定变量，与数组大小无关 |

### 边界情况

- **数组只有两个元素**：`i` 取 0，`j` 取 1，直接判断并返回，没有问题。
- **有重复值**：例如 `[3, 3]`，`i=0, j=1` 时和为 6，正确返回 `[0,1]`，不会出现使用同一个元素两次（因为 `j` 从 `i+1` 开始）。
- **题目保证有解**，所以不用担心找不到答案的情况。

---

## 解法二：哈希表（优化解法）

> 暴力解法需要两层循环，时间复杂度高。我们可以用“哈希表”（Python 中的字典 `dict`）来**记住已经看过的数**，从而将查找另一个数的时间从 O(n) 降到 O(1)，整体变成 O(n)。

### 思路

1. **核心想法**：对于当前遍历到的数 `nums[i]`，我们想知道**它需要和哪个数配对**才能得到 `target`。那个配对的数就是 `complement = target - nums[i]`。
2. 如果我们之前已经遇到了这个 `complement`，并且记下了它的下标，那么就直接返回 `[complement的下标, i]`。
3. 如果没有遇到过，就把当前数 `nums[i]` 和它的下标 `i` 存入哈希表（字典），供后面的数使用。
4. 这样**只需要一次遍历**，每个数只访问一次。

### 代码

```python
class Solution:
    def twoSum(self, nums: list[int], target: int) -> list[int]:
        # 创建空字典，用来存放“已经见过的数”及其下标
        # 字典结构：{数值: 下标}
        seen = {}

        # 遍历数组，i 是当前数的下标，num 是当前数的值
        for i, num in enumerate(nums):   # enumerate 会返回 (下标, 值) 的配对
            # 计算需要的另一个数是多少
            complement = target - num

            # 检查这个需要的数是否已经在 seen 字典中
            if complement in seen:   # in 操作对于字典很快，平均 O(1)
                # 如果存在，说明找到了答案
                # seen[complement] 是之前存的下标，i 是当前下标
                return [seen[complement], i]

            # 如果没有找到，就把当前数 num 和它的下标 i 存入字典
            # 这样后面的数可以找到它
            seen[num] = i

        # 题目保证有解，不会执行到这里
        return []

# 测试示例
sol = Solution()
print(sol.twoSum([2, 7, 11, 15], 9))   # 期望输出：[0, 1]
print(sol.twoSum([3, 2, 4], 6))        # 期望输出：[1, 2]
print(sol.twoSum([3, 3], 6))           # 期望输出：[0, 1]
```

### 复杂度分析

| 维度 | 复杂度 | 说明 |
|------|--------|------|
| 时间 | O(n) | 只遍历一次数组，每次字典查找和插入都是 O(1) 平均时间 |
| 空间 | O(n) | 字典最多存储 n 个元素（最坏情况下，最后一个数才找到配对） |

### 边界情况

- **数组很长**：依然高效，不会超时。
- **重复值**：例如 `[3, 3]`，`target = 6`：
  - 遍历第一个 `3`，`complement = 3`，此时 `seen` 为空，所以存入 `{3:0}`。
  - 遍历第二个 `3`，`complement = 3`，检查 `seen` 发现 `3` 已存在，返回 `[0, 1]`。正确。
- **题目保证有解**，所以不需要处理找不到的情况。

---

## 解法对比总结

| 解法 | 时间复杂度 | 空间复杂度 | 推荐场景 |
|------|-----------|-----------|----------|
| 解法一：暴力枚举 | O(n²) | O(1) | 理解解题逻辑、数组很小（长度 < 1000） |
| 解法二：哈希表 | O(n) | O(n) | 实际开发、面试、大数据量情况 |

- **零基础建议**：先完全理解暴力解法，再学习哈希表优化。
- **面试推荐**：直接写哈希表解法，并解释为什么可以用空间换时间。