Skip to content

两数之和

LeetCode 官方题目链接

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[i], nums[j]),判断它们的和是否等于 target

  1. 第一层循环遍历数组中的每一个元素 nums[i]
  2. 第二层循环从 i 的下一个位置开始遍历 nums[j]
  3. 检查 nums[i] + nums[j] 是否等于 target
  4. 如果相等,则返回下标 [i, j]
  • 时间复杂度:$O(n^2)$,最坏情况下需要遍历所有元素对。
  • 空间复杂度:$O(1)$,只需要常数级空间存储变量。

方法二:哈希表优化法 (推荐)

暴力法的时间复杂度较高,瓶颈在于寻找 target - nums[i] 是否存在于数组中。我们可以利用 哈希表 (Map) 将查找时间从 $O(n)$ 降低到 $O(1)$。

  1. 创建一个哈希表 Map,用于存储 { 元素值: 下标 } 的映射关系。
  2. 遍历数组 nums,对于当前元素 nums[i]
    • 计算目标补数:complement = target - nums[i]
    • 检查哈希表中是否存在 complement
    • 如果存在:说明之前遍历过的某个元素与当前元素之和为 target,直接返回它们的下标 [map.get(complement), i]
    • 如果不存在:将当前元素及其下标 (nums[i], i) 存入哈希表,供后续查找使用。
  3. 这种方法只需要遍历一次数组,因此效率更高。

3. 代码实现

方法一:暴力枚举

javascript
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    // 边界条件判断
    if (!nums || nums.length < 2) return [];

    const n = nums.length;
    // 第一层循环:选择第一个数
    for (let i = 0; i < n; i++) {
        // 第二层循环:选择第二个数(从 i+1 开始,避免重复和自身相加)
        for (let j = i + 1; j < n; j++) {
            // 判断两数之和是否等于目标值
            if (nums[i] + nums[j] === target) {
                return [i, j]; // 找到答案,返回下标
            }
        }
    }
    return []; // 未找到答案
};

方法二:哈希表 (Map)

javascript
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let map = new Map(); // 创建哈希表
    for(let i = 0; i < nums.length; i++) { // 遍历数组
        let c = target - nums[i]; // 计算补数 c
        if(map.has(c)) { // 如果补数存在于哈希表中
            return [map.get(c), i]; // 返回结果 [补数下标, 当前下标]
        }
        map.set(nums[i], i); // 将当前数和下标存入 Map
    }
};

代码执行演示

假设输入 nums = [2, 7, 11, 15], target = 9,代码执行过程如下:

  1. 初始化map 为空 {}
  2. 第一轮循环 (i=0)
    • 当前数 nums[0] = 2
    • 计算补数 c = 9 - 2 = 7
    • 检查 map 中是否有 7? -> 没有
    • 存入 map{2 => 0}
  3. 第二轮循环 (i=1)
    • 当前数 nums[1] = 7
    • 计算补数 c = 9 - 7 = 2
    • 检查 map 中是否有 2? -> (在 i=0 时存入)
    • 找到答案:返回 [map.get(2), 1][0, 1]

4. 可视化演示

假设输入 nums = [2, 7, 11, 15], target = 9

执行过程流程图

逐步状态演示

步骤当前元素 nums[i]目标补数 9 - nums[i]Map 状态 (检查前)Map 中有补数?操作
i = 027{}No2: 0 存入 Map
i = 172{2: 0}Yes (找到 2)返回 [0, 1]

5. 复杂度对比分析

维度暴力枚举法哈希表优化法差异分析
时间复杂度$O(n^2)$$O(n)$哈希表将查找时间从 $O(n)$ 降为 $O(1)$,只需一次遍历。
空间复杂度$O(1)$$O(n)$哈希表需要额外空间存储 $n$ 个元素,是用空间换时间。
代码简洁度简单直观略微复杂哈希表逻辑需要理解 Map 数据结构。

实际应用建议

  • 数据量小 (N < 100):两种方法差异不大,暴力法无需额外空间,可能略优。
  • 数据量大 (N > 1000)强烈推荐哈希表法。例如当 $N=10000$ 时,暴力法运算次数约为 $10^8$ 级,可能导致超时;而哈希表法仅需 $10^4$ 级,瞬间完成。

Power by VitePress