力扣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.暴力求解

cpp 复制代码
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. 函数定义与参数

cpp 复制代码
vector<int> twoSum(vector<int>& nums, int target)
  • 作用:接收一个整数数组 nums 和目标值 target,返回一个包含两个下标的数组(这两个下标对应的元素之和为 target)。
  • vector<int>& nums:输入的数组(& 表示引用传递,避免复制数组的开销)。
  • int target:需要达成的目标和。
  • 返回值 vector<int>:存储两个符合条件的元素下标。

2. 获取数组长度

cpp 复制代码
int n = nums.size();
  • 先获取数组 nums 的长度 n(元素个数),避免在循环中多次调用 nums.size()(提升微小效率)。

3. 外层循环(遍历第一个元素)

cpp 复制代码
for (int i = 0; i < n; ++i)
  • i 是第一个元素的下标,从 0 遍历到 n-1(覆盖数组中所有元素)。
  • 作用:逐个选取数组中的元素作为 "第一个候选元素"(nums[i])。

4. 内层循环(遍历第二个元素)

cpp 复制代码
for (int j = i + 1; j < n; ++j)
  • j 是第二个元素的下标,从 i + 1 开始遍历到 n-1
  • 关键逻辑:ji + 1 开始,是为了避免重复检查同一对元素 (比如 i=0, j=1i=1, j=0 是同一对元素,无需重复判断),同时确保 i ≠ j(同一个元素不能用两次)。
  • 作用:为每个 nums[i] 匹配它后面的所有元素作为 "第二个候选元素"(nums[j])。

5. 判断和是否等于目标值

cpp 复制代码
if(nums[i] + nums[j] == target)
  • 检查当前两个候选元素的和是否等于 target
  • 如果满足条件,直接返回这两个元素的下标 {i, j}(因为题目保证 "有且仅有一个解",找到后可立即返回)。

6. 兜底返回(语法兼容)

cpp 复制代码
return {};
  • 题目明确说明 "输入一定存在唯一解",因此这个语句实际不会被执行。
  • 作用:保证函数在语法上完整(C++ 要求函数必须有返回值)。

核心特点总结

  • 优点:逻辑简单直接,容易理解,不需要额外的存储空间。
  • 缺点 :时间复杂度高,为 O(n²)n 是数组长度),因为外层循环执行 n 次,内层循环平均执行 n/2 次,总操作次数约为 n²/2
  • 适用场景 :适合理解问题逻辑,或处理数据量很小的情况;大数据量时推荐用哈希表解法(时间复杂度 O(n))。

2.哈希表

cpp 复制代码
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. 函数定义

cpp 复制代码
vector<int> twoSum(vector<int>& nums, int target) {
  • 这是函数的声明:返回值是 vector<int>(存储两个下标),函数名是 twoSum,参数有两个:nums 是输入的整数数组(引用传递,避免拷贝开销),target 是目标和。

2. 哈希表定义

cpp 复制代码
unordered_map<int, int> numMap;
  • unordered_map 是 C++ 中的哈希表容器,这里定义了一个名为 numMap 的哈希表。
  • 哈希表的键(key)int 类型,存储数组中已经遍历过的元素的值;
  • 哈希表的值(value)int 类型,存储该元素在数组中的下标。
  • 作用:通过哈希表快速记录 "已遍历元素的值" 和 "其下标" 的对应关系,后续查找时可以直接通过 "值" 定位到 "下标",时间复杂度为 O (1)(比数组遍历查找的 O (n) 快很多)。

3. 遍历数组

cpp 复制代码
for (int i = 0; i < nums.size(); ++i) {
  • for 循环遍历整个数组 numsi 是当前遍历到的元素的下标(从 0 开始)。
  • 遍历的目的:逐个处理数组中的每个元素,检查是否存在另一个已遍历的元素,与当前元素的和等于 target

4. 计算 "补数"

cpp 复制代码
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. 检查补数是否已存在于哈希表中

cpp 复制代码
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. 补数不存在时,将当前元素存入哈希表

cpp 复制代码
numMap[nums[i]] = i;

8. 兜底返回(语法兼容)

  • 题目明确说明 "输入数组中一定存在唯一解",因此这个 return 语句实际上永远不会被执行。
  • 它的作用是保证函数在语法上完整(C++ 要求函数必须有返回值)。

核心逻辑总结

  1. 用哈希表记录 "已遍历元素的值 → 下标" 的映射,实现快速查找;
  2. 遍历每个元素时,计算它的 "补数"(需要和它相加得 target 的数);
  3. 检查补数是否已在哈希表中(即是否已遍历过):
    • 若是,直接返回两个元素的下标;
    • 若否,将当前元素存入哈希表,继续遍历。

为什么这样高效?

  • 时间复杂度:O (n)(n 是数组长度)。每个元素只遍历一次,哈希表的查找和插入操作都是 O (1);
  • 空间复杂度:O (n)。最坏情况下,需要将数组中所有元素存入哈希表。
相关推荐
Blossom.1185 小时前
把AI“灌”进奶瓶:1KB决策树让婴儿温奶器自己学会「恒温+计时」
人工智能·python·深度学习·算法·决策树·机器学习·计算机视觉
快乐的钢镚子8 小时前
【leetcode hot 100】49.字母异位词分组
python·leetcode
一念&10 小时前
每日一个C语言知识:C 头文件
c语言·开发语言·算法
tao35566714 小时前
【Python刷力扣hot100】42. Trapping Rain Water
开发语言·python·leetcode
Miraitowa_cheems14 小时前
LeetCode算法日记 - Day 88: 环绕字符串中唯一的子字符串
java·数据结构·算法·leetcode·深度优先·动态规划
B站_计算机毕业设计之家15 小时前
python电商商品评论数据分析可视化系统 爬虫 数据采集 Flask框架 NLP情感分析 LDA主题分析 Bayes评论分类(源码) ✅
大数据·hadoop·爬虫·python·算法·数据分析·1024程序员节
小白菜又菜15 小时前
Leetcode 1518. Water Bottles
算法·leetcode·职场和发展
长存祈月心15 小时前
Rust Option 与 Result深度解析
算法
杭州杭州杭州16 小时前
机器学习(3)---线性算法,决策树,神经网络,支持向量机
算法·决策树·机器学习