题目信息
- 题目编号: 1
- 题目名称: 两数之和
- 标签: 数组、哈希表
- 难度: 简单
- 题目链接 : 两数之和 - LeetCode
题目描述
给定一个整数数组 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]
解题思路
初步思考
看到这道题,我的第一反应是:这不就是从数组里找两个数加起来等于目标值吗?最直观的想法就是两层循环,遍历所有可能的组合,看看哪两个数加起来等于target。这就像是在一个聚会上找两个陌生人,他们的年龄加起来正好是30岁------最笨的方法就是每个人都问一下其他所有人的年龄,看看有没有匹配的。
方法分析
方法一:暴力枚举法
思路 :
两层 for 循环,外层循环遍历每个元素,内层循环从当前元素的下一个位置开始继续遍历,检查是否存在另一个数使得两数之和等于 target。
这个方法的思路非常直接:既然我不知道哪两个数加起来等于 target,那我就把所有的可能组合都试一遍,总能找到答案!
根据一个简单示例,通过图示展示思路:
以 nums = [2,7,11,15], target = 9 为例:
数组: [2] [7] [11] [15]
索引: 0 1 2 3
第一轮遍历 (i=0, num=2):
- 寻找 complement = 9 - 2 = 7
- j 从 1 开始查找
- 找到 nums[1] = 7 ✓ 返回 [0, 1]
遍历过程:
i=0: 2 + 7 = 9 ✓ (找到答案!)
i=0: 2 + 11 = 13 ✗
i=0: 2 + 15 = 17 ✗
i=1: 7 + 11 = 18 ✗
i=1: 7 + 15 = 22 ✗
i=2: 11 + 15 = 26 ✗
算法步骤:
- 创建一个空的结果列表
result - 使用外层循环
i从 0 遍历到len(nums) - 2 - 使用内层循环
j从i + 1遍历到len(nums) - 1 - 如果
nums[i] + nums[j] == target,则返回[i, j] - 如果遍历结束都没有找到,返回空列表或抛出异常
复杂度分析:
- 时间复杂度: O(n²),其中 n 是数组长度
- 空间复杂度: O(1),只使用了常数级别的额外空间
代码实现:
Python 实现:
python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
for i in range(n - 1):
for j in range(i + 1, n):
if nums[i] + nums[j] == target:
return [i, j]
return []
Java 实现:
java
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[]{};
}
}
方法二:哈希表法
思路 :
使用哈希表(字典)来存储已经遍历过的元素及其索引。在遍历数组时,对于每个元素,计算它与目标值的差值(complement),然后在哈希表中查找这个差值。如果找到,说明当前元素和哈希表中的元素就是我们要找的两个数。
这个方法就像是在约会软件上找对象:你有一个理想伴侣的标准(target - 当前人选),然后在已经见过的人里面快速查找有没有符合这个标准的。
根据一个简单示例,通过图示展示思路:
以 nums = [2,7,11,15], target = 9 为例:
初始状态:
hash_map = {}
result = 未找到
i=0, num=2:
complement = 9 - 2 = 7
在 hash_map 中查找 7: 未找到
将 (2, 0) 加入 hash_map
hash_map = {2: 0}
i=1, num=7:
complement = 9 - 7 = 2
在 hash_map 中查找 2: 找到! index=0
返回 [0, 1] ✓
遍历完成!
算法步骤:
- 创建一个空哈希表
hash_map用于存储元素值到索引的映射 - 遍历数组,对于每个元素
num和索引i:- 计算
complement = target - num - 如果
complement在hash_map中,返回[hash_map[complement], i] - 否则,将当前元素和索引存入
hash_map
- 计算
- 如果遍历结束都没有找到,返回空列表
复杂度分析:
- 时间复杂度: O(n),其中 n 是数组长度
- 空间复杂度: O(n),最坏情况下需要存储 n 个元素
代码实现:
Python 实现:
python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hash_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hash_map:
return [hash_map[complement], i]
hash_map[num] = i
return []
Java 实现:
java
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (hashMap.containsKey(complement)) {
return new int[]{hashMap.get(complement), i};
}
hashMap.put(nums[i], i);
}
return new int[]{};
}
}
Rust 实现:
rust
use std::collections::HashMap;
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
let mut hash_map: HashMap<i32, usize> = HashMap::new();
for (i, &num) in nums.iter().enumerate() {
let complement = target - num;
if let Some(&j) = hash_map.get(&complement) {
return vec![j as i32, i as i32];
}
hash_map.insert(num, i);
}
vec![]
}
}
代码实现
Python 实现:
python
from typing import List
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
"""
在数组中找到两个数,使它们的和等于目标值
Args:
nums: 整数数组
target: 目标值
Returns:
两个数在数组中的索引
"""
hash_map = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hash_map:
return [hash_map[complement], i]
hash_map[num] = i
return []
Java 实现:
java
import java.util.HashMap;
import java.util.Map;
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (hashMap.containsKey(complement)) {
return new int[]{hashMap.get(complement), i};
}
hashMap.put(nums[i], i);
}
return new int[]{};
}
}
Rust 实现:
rust
use std::collections::HashMap;
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
let mut hash_map: HashMap<i32, usize> = HashMap::new();
for (i, &num) in nums.iter().enumerate() {
let complement = target - num;
if let Some(&j) = hash_map.get(&complement) {
return vec![j as i32, i as i32];
}
hash_map.insert(num, i);
}
vec![]
}
}
总结与收获
知识点
- 数组遍历技巧:学习如何通过嵌套循环遍历数组的所有元素组合
- 哈希表应用:掌握使用哈希表进行快速查找,将时间复杂度从 O(n²) 优化到 O(n)
- 空间换时间:理解用额外空间(哈希表)换取时间效率的经典思想
- 一次遍历:学会在一次遍历中同时完成查找和存储操作
易错点
- 重复元素处理 :当数组中存在相同元素时(如示例3中的
[3,3]),要注意两个相同的元素可以组成答案,但不能用同一个元素两次 - 返回顺序:题目不要求返回顺序,但要注意返回值是索引而不是元素值
- 边界条件 :当数组长度为2时,直接返回
[0, 1]即可 - 哈希表查找时机:一定要先查找,再存储当前元素,否则会找到自己
优化思路
- 提前终止:如果哈希表法在遍历过程中找到答案,可以立即返回,不需要遍历完整个数组
- 空间优化:如果数组元素范围有限且较小,可以使用数组代替哈希表,进一步提高性能
- 并行处理:对于超大数组,可以考虑分块并行处理,但需要注意结果合并