两数之和
题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
解题思路分析
方法一:暴力解法(容易想到)
两层for循环遍历所有可能的组合,时间复杂度O(n²),空间复杂度O(1)
#include<iostream>
#include<vector>
using namespace std;
int main(){
// 输入:nums = [2,7,11,15], target = 9
vector<int> nums = {2,7,11,15};
int target = 9;
for(int i=0;i<nums.size();i++){//遍历数组
int x=target-nums[i];//获取差值
for(int j=i+1;j<nums.size();j++){
if(nums[j]==x){
cout<<j<<" "<<i;
}
}
}
}
方法二:哈希表优化
通过一次遍历,在遍历过程中使用哈希表记录已访问过的元素及其索引,从而将查找时间复杂度从O(n)降低到O(1)。
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
int main(){
// 输入:nums = [2,7,11,15], target = 9
vector<int> nums = {2,7,11,15};
int target = 9;
unordered_map<int,int> hash;//创建一个哈希表
for(int i=0;i<nums.size();i++){//遍历数组
int x=target-nums[i];//获取差值
if(hash.find(x) != hash.end()){//判断差值是否存在于哈希表中
cout<<hash[x]<<" "<<i;//输出索引
}
hash[nums[i]] = i;//将当前元素及其索引存入哈希表
}
}
核心思想与关键问题解答
1. 为什么会想到用哈希表?
我的思考过程:
当我分析这个问题时,最直观的想法是使用双重循环暴力求解,时间复杂度为O(n²)。但仔细思考后发现:
- 对于每个元素nums[i],我需要快速找到是否存在另一个元素等于target - nums[i]
- 如果能在O(1)时间内完成这个查找操作,整体时间复杂度就能降到O(n)
- 哈希表正好提供了这样的能力——平均情况下查找、插入、删除都是O(1)的时间复杂度
- 这样就能避免重复计算,大大提高效率
2. 哈希表为什么用unordered_map?
选择理由:
- 在C++中,
unordered_map是基于哈希表实现的,查询和增删效率都是最优的,平均时间复杂度为O(1) - 如果不需要有序,优先使用
unordered_set(集合)或unordered_map(键值对),因为它们查询、插入、删除最快 - 如果需要集合有序,使用
set - 如果不仅有序还要允许重复数据,使用
multiset
3. 哈希法的适用场景
当我们遇到要快速判断一个元素是否出现在集合里的时候,就要考虑哈希法。
4. 算法流程详解
-
初始化一个空的unordered_map
-
遍历每个元素nums[i]:
- 计算差值x = target - nums[i]
- 检查哈希表中是否存在key为x的元素
- 如果存在,返回hash[x]和i
- 如果不存在,将当前元素nums[i]及其索引i存入哈希表
-
由于题目保证有且只有一个答案,循环结束后一定能找到解
-
模拟执行过程(nums = [2, 7, 11, 15], target = 9)
我们用一个表格来跟踪每一步:
| 步骤 | i | nums[i] | complement = 9 - nums[i] | 哈希表当前状态(key: value = 值: 下标) | 是否找到? | 说明 |
|---|---|---|---|---|---|---|
| 初始 | - | - | - | {} | - | 哈希表为空 |
| 1 | 0 | 2 | 7 | {} → 存入 {2: 0} | ❌ | 7 不在表中,存入当前元素 |
| 2 | 1 | 7 | 2 | {2: 0} → 找到 2! | ✅ | 2 在表中,下标是 0,当前下标是 1 → 返回 [0, 1] |