力扣1. 两数之和
定一个整数数组 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 <= 104
- -109 <= nums[i] <= 109
- -109 <= target <= 109
- 只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
1.暴力求解
class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {int n = nums.size();for (int i = 0; i < n; ++i){for (int j = i + 1; j < n; ++j){if(nums[i] + nums[j] == target){return {i,j};}}}return {};}
};1. 函数定义与参数
vector<int> twoSum(vector<int>& nums, int target)- 作用:接收一个整数数组 nums和目标值target,返回一个包含两个下标的数组(这两个下标对应的元素之和为target)。
- vector<int>& nums:输入的数组(- &表示引用传递,避免复制数组的开销)。
- int target:需要达成的目标和。
- 返回值 vector<int>:存储两个符合条件的元素下标。
2. 获取数组长度
int n = nums.size();- 先获取数组 nums的长度n(元素个数),避免在循环中多次调用nums.size()(提升微小效率)。
3. 外层循环(遍历第一个元素)
for (int i = 0; i < n; ++i)- i是第一个元素的下标,从- 0遍历到- n-1(覆盖数组中所有元素)。
- 作用:逐个选取数组中的元素作为 “第一个候选元素”(nums[i])。
4. 内层循环(遍历第二个元素)
for (int j = i + 1; j < n; ++j)- j是第二个元素的下标,从- i + 1开始遍历到- n-1。
- 关键逻辑:j从i + 1开始,是为了避免重复检查同一对元素(比如i=0, j=1和i=1, j=0是同一对元素,无需重复判断),同时确保i ≠ j(同一个元素不能用两次)。
- 作用:为每个 nums[i]匹配它后面的所有元素作为 “第二个候选元素”(nums[j])。
5. 判断和是否等于目标值
if(nums[i] + nums[j] == target)- 检查当前两个候选元素的和是否等于 target。
- 如果满足条件,直接返回这两个元素的下标 {i, j}(因为题目保证 “有且仅有一个解”,找到后可立即返回)。
6. 兜底返回(语法兼容)
return {};- 题目明确说明 “输入一定存在唯一解”,因此这个语句实际不会被执行。
- 作用:保证函数在语法上完整(C++ 要求函数必须有返回值)。
核心特点总结
- 优点:逻辑简单直接,容易理解,不需要额外的存储空间。
- 缺点:时间复杂度高,为 O(n²)(n是数组长度),因为外层循环执行n次,内层循环平均执行n/2次,总操作次数约为n²/2。
- 适用场景:适合理解问题逻辑,或处理数据量很小的情况;大数据量时推荐用哈希表解法(时间复杂度 O(n))。
2.哈希表
class Solution {
public:vector<int> twoSum(vector<int>& nums, int target) {// 哈希表:键为数组元素值,值为对应下标unordered_map<int, int> numMap;// 遍历数组for (int i = 0; i < nums.size(); ++i) {// 计算当前元素的“补数”(目标值 - 当前元素)int complement = target - nums[i];// 若补数存在于哈希表中,说明找到符合条件的两个数if (numMap.find(complement) != numMap.end()) {return {numMap[complement], i};}// 若补数不存在,将当前元素和下标存入哈希表numMap[nums[i]] = i;}// 题目保证有解,此处仅为语法兼容return {};}
};代码逐行解释
1. 函数定义
vector<int> twoSum(vector<int>& nums, int target) {- 这是函数的声明:返回值是 vector<int>(存储两个下标),函数名是twoSum,参数有两个:nums是输入的整数数组(引用传递,避免拷贝开销),target是目标和。
2. 哈希表定义
unordered_map<int, int> numMap;- unordered_map是 C++ 中的哈希表容器,这里定义了一个名为- numMap的哈希表。
- 哈希表的键(key) 是 int类型,存储数组中已经遍历过的元素的值;
- 哈希表的值(value) 是 int类型,存储该元素在数组中的下标。
- 作用:通过哈希表快速记录 “已遍历元素的值” 和 “其下标” 的对应关系,后续查找时可以直接通过 “值” 定位到 “下标”,时间复杂度为 O (1)(比数组遍历查找的 O (n) 快很多)。
3. 遍历数组
for (int i = 0; i < nums.size(); ++i) {- 用 for循环遍历整个数组nums,i是当前遍历到的元素的下标(从 0 开始)。
- 遍历的目的:逐个处理数组中的每个元素,检查是否存在另一个已遍历的元素,与当前元素的和等于 target。
4. 计算 “补数”
int complement = target - nums[i];- nums[i]是当前遍历到的元素的值。
- complement翻译为 “补数”,指的是:如果存在一个数- x,使得- nums[i] + x = target,那么- x就等于- target - nums[i],也就是这里的- complement。
- 例如:如果 target是 9,当前nums[i]是 2,那么complement就是 7(因为 2 + 7 = 9)。我们需要检查之前是否遍历过 7。
5. 检查补数是否已存在于哈希表中
if (numMap.find(complement) != numMap.end()) {- numMap.find(complement):在哈希表中查找 “键为- complement” 的元素。如果找到,返回该元素的迭代器(类似指针);如果没找到,返回- numMap.end()(哈希表的 “结束标记”)。
- 条件 numMap.find(complement) != numMap.end()的意思是:补数complement存在于哈希表中(即之前已经遍历过值为complement的元素)。
- 此时说明:当前元素 nums[i]和哈希表中记录的complement对应的元素,它们的和就是target,这两个元素就是我们要找的解。
6. 返回结果
- 如果上一步的条件成立,直接返回一个包含两个下标的数组:- numMap[complement]:哈希表中 “键为- complement” 对应的值,也就是之前遍历过的- complement元素的下标;
- i:当前元素- nums[i]的下标。
 
- 这两个下标对应的元素之和为 target,因此是正确结果。
7. 补数不存在时,将当前元素存入哈希表
numMap[nums[i]] = i;8. 兜底返回(语法兼容)
- 题目明确说明 “输入数组中一定存在唯一解”,因此这个 return 语句实际上永远不会被执行。
- 它的作用是保证函数在语法上完整(C++ 要求函数必须有返回值)。
核心逻辑总结
- 用哈希表记录 “已遍历元素的值 → 下标” 的映射,实现快速查找;
- 遍历每个元素时,计算它的 “补数”(需要和它相加得 target的数);
- 检查补数是否已在哈希表中(即是否已遍历过):- 若是,直接返回两个元素的下标;
- 若否,将当前元素存入哈希表,继续遍历。
 
为什么这样高效?
- 时间复杂度:O (n)(n 是数组长度)。每个元素只遍历一次,哈希表的查找和插入操作都是 O (1);
- 空间复杂度:O (n)。最坏情况下,需要将数组中所有元素存入哈希表。
