文章目录
-
-
- 题目描述
- 解法一:暴力枚举法 (Brute Force)
-
- [1. 思路解析](#1. 思路解析)
- [2. 流程图 (Mermaid)](#2. 流程图 (Mermaid))
- [3. Java 代码实现](#3. Java 代码实现)
- [4. 复杂度分析](#4. 复杂度分析)
- [解法二:哈希表法 (Hash Map) ------ **推荐解法**](#解法二:哈希表法 (Hash Map) —— 推荐解法)
-
- [1. 思路解析](#1. 思路解析)
- [2. 状态演变图解](#2. 状态演变图解)
- [3. 逻辑流程图 (Mermaid)](#3. 逻辑流程图 (Mermaid))
- [4. Java 代码实现](#4. Java 代码实现)
- [5. 复杂度分析](#5. 复杂度分析)
- 总结对比
-
题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
示例:
输入:
nums = [2,7,11,15],target = 9输出:
[0,1]解释:因为
nums[0] + nums[1] == 9,返回[0, 1]。
解法一:暴力枚举法 (Brute Force)
1. 思路解析
这是最直观的思路。我们需要找到两个数,不妨设为 x 和 y。
我们可以通过两层循环来遍历数组:
- 第一层循环 选取第一个数
x(索引为i)。 - 第二层循环 选取第二个数
y(索引为j)。 - 判断
nums[i] + nums[j]是否等于target。 - 注意:题目要求不能使用同一个元素,所以内层循环从
i + 1开始。
2. 流程图 (Mermaid)
是
是
是
否
否
否
开始
外层循环 i 从 0 到 n-1
i < n ?
内层循环 j 从 i+1 到 n-1
j < n ?
nums[i] + nums[j] == target ?
找到答案! 返回 {i, j}
j++
i++
未找到答案 (理论上不会到达)
3. Java 代码实现
java
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
// 第一层循环
for (int i = 0; i < n; i++) {
// 第二层循环,从 i+1 开始,避免重复和自身相加
for (int j = i + 1; j < n; j++) {
// 判断和是否为 target
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[0]; // 题目保证有解,此处仅为语法完整
}
}
4. 复杂度分析
- 时间复杂度 : O ( N 2 ) O(N^2) O(N2)。最坏情况下,我们需要遍历几乎所有的元素对。对于较大的数组,这种方法会非常慢。
- 空间复杂度 : O ( 1 ) O(1) O(1)。只使用了常数个变量存储索引。
解法二:哈希表法 (Hash Map) ------ 推荐解法
1. 思路解析
暴力法的瓶颈在于:对于每个 x,我们需要遍历数组来寻找是否存在 target - x。这个查找过程是 O ( N ) O(N) O(N) 的。
我们可以利用 哈希表 (HashMap) 这种数据结构,利用其 O ( 1 ) O(1) O(1) 的查找时间复杂度来优化算法。
- 我们在遍历数组的过程中,将元素的值和它的索引存入哈希表。
- 核心逻辑 :在遍历当前元素
num时,先检查哈希表中是否存在target - num(即补数)。- 如果存在,说明之前已经遍历过那个数了,直接返回两个索引。
- 如果不存在,将当前元素
num和它的索引i存入哈希表,供后面的元素查找。
2. 状态演变图解
假设 nums = [2, 7, 11, 15], target = 9。
| 步骤 | 当前索引 (i) | 当前数值 (num) | 需要寻找的补数 (target - num) | 哈希表状态 (Check -> Put) | 结果 |
|---|---|---|---|---|---|
| 1 | 0 | 2 | 9 - 2 = 7 | Map中含7吗? No 。 Put {2: 0} |
继续 |
| 2 | 1 | 7 | 9 - 7 = 2 | Map中含2吗? Yes (index 0)。 | 找到! 返回 [0, 1] |
3. 逻辑流程图 (Mermaid)
是
否
开始
初始化 HashMap
遍历数组 nums
获取当前元素 nums/i
计算补数: complement = target - nums/i
Map中包含 key 为
complement 吗?
获取 Map.get/complement
作为第一个索引
返回 {mapIndex, i}
结束
将 {nums/i : i}
存入 Map
4. Java 代码实现
java
import java.util.HashMap;
import java.util.Map;
class Solution {
public int[] twoSum(int[] nums, int target) {
// 创建哈希表,Key存放数值,Value存放索引
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
// 检查哈希表中是否存在我们需要的那个"补数"
if (map.containsKey(complement)) {
// 如果存在,直接返回 [补数的索引, 当前索引]
return new int[] { map.get(complement), i };
}
// 如果不存在,将当前数字和索引存入哈希表
map.put(nums[i], i);
}
return new int[0];
}
}
5. 复杂度分析
- 时间复杂度 : O ( N ) O(N) O(N)。我们只遍历了包含 N N N 个元素的数组一次。在表中进行每次查找花费 O ( 1 ) O(1) O(1) 的时间。
- 空间复杂度 : O ( N ) O(N) O(N)。最坏情况下,我们需要将 N N N 个元素都存入哈希表中(例如数组中没有匹配对,或者匹配对在最后两个)。
总结对比
| 特性 | 暴力枚举法 | 哈希表法 (最优) |
|---|---|---|
| 核心思想 | 双重循环遍历 | 空间换时间 (查表) |
| 时间复杂度 | O ( N 2 ) O(N^2) O(N2) (慢) | O ( N ) O(N) O(N) (快) |
| 空间复杂度 | O ( 1 ) O(1) O(1) (省空间) | O ( N ) O(N) O(N) (耗空间) |
| 代码简洁度 | 简单 | 适中 |
| 适用场景 | N 非常小的时候 | 通用场景,尤其是大数据量 |