LeetCode-1-两数之和

题目信息

  • 题目编号: 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 ✗

算法步骤:

  1. 创建一个空的结果列表 result
  2. 使用外层循环 i 从 0 遍历到 len(nums) - 2
  3. 使用内层循环 ji + 1 遍历到 len(nums) - 1
  4. 如果 nums[i] + nums[j] == target,则返回 [i, j]
  5. 如果遍历结束都没有找到,返回空列表或抛出异常

复杂度分析:

  • 时间复杂度: 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] ✓

遍历完成!

算法步骤:

  1. 创建一个空哈希表 hash_map 用于存储元素值到索引的映射
  2. 遍历数组,对于每个元素 num 和索引 i
    • 计算 complement = target - num
    • 如果 complementhash_map 中,返回 [hash_map[complement], i]
    • 否则,将当前元素和索引存入 hash_map
  3. 如果遍历结束都没有找到,返回空列表

复杂度分析:

  • 时间复杂度: 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![]
    }
}

总结与收获

知识点

  1. 数组遍历技巧:学习如何通过嵌套循环遍历数组的所有元素组合
  2. 哈希表应用:掌握使用哈希表进行快速查找,将时间复杂度从 O(n²) 优化到 O(n)
  3. 空间换时间:理解用额外空间(哈希表)换取时间效率的经典思想
  4. 一次遍历:学会在一次遍历中同时完成查找和存储操作

易错点

  1. 重复元素处理 :当数组中存在相同元素时(如示例3中的 [3,3]),要注意两个相同的元素可以组成答案,但不能用同一个元素两次
  2. 返回顺序:题目不要求返回顺序,但要注意返回值是索引而不是元素值
  3. 边界条件 :当数组长度为2时,直接返回 [0, 1] 即可
  4. 哈希表查找时机:一定要先查找,再存储当前元素,否则会找到自己

优化思路

  1. 提前终止:如果哈希表法在遍历过程中找到答案,可以立即返回,不需要遍历完整个数组
  2. 空间优化:如果数组元素范围有限且较小,可以使用数组代替哈希表,进一步提高性能
  3. 并行处理:对于超大数组,可以考虑分块并行处理,但需要注意结果合并

相似题目

相关推荐
不知名XL8 小时前
day20 回溯算法part02
算法
superman超哥8 小时前
双端迭代器(DoubleEndedIterator):Rust双向遍历的优雅实现
开发语言·后端·rust·双端迭代器·rust双向遍历
嵌入式进阶行者8 小时前
【算法】TLV格式解析实例:华为OD机考双机位A卷 - TLV解析 Ⅱ
数据结构·c++·算法
1二山似8 小时前
crmeb多商户启动swoole时报‘加密文件丢失’
后端·swoole
马卡巴卡8 小时前
Java CompletableFuture 接口与原理详解
后端
OC溥哥9998 小时前
Paper MinecraftV3.0重大更新(下界更新)我的世界C++2D版本隆重推出,拷贝即玩!
java·c++·算法
Jayden_Ruan8 小时前
C++蛇形方阵
开发语言·c++·算法
星火开发设计8 小时前
C++ map 全面解析与实战指南
java·数据结构·c++·学习·算法·map·知识
执笔论英雄8 小时前
【RL] advantages白化与 GRPO中 advantages均值,怎么变化,
算法·均值算法