两数之和

题目描述

想象你手中有一串随机排列的数字。我们的目标很简单:从这串数字中挑选出两个,让它们的和恰好等于一个给定的目标值。这听起来像是在杂乱的抽屉里寻找一双颜色匹配的袜子——你不仅要找到这两个数字,还需要记下它们在原始队列中所在的位置。为了让逻辑更清晰,我们约定在一次寻找过程中,每个数字只能参与一次配对,且题目保证这组数字中一定存在且仅存在一组完美的搭配。

思路分析

最直白的想法莫过于“地毯式搜索”:我们先固定住第一个数字,然后让剩下的每一个数字与其尝试相加。如果失败了,我们就换下一个数字继续尝试。这种双重循环的暴力解法虽然直观得像手工清点,但当数字成千上万时,重复的遍历会让整个过程变得沉重且低效。

为了变得更聪明,我们可以试着建立一份“记忆”。当我们看到一个数字时,立刻就能算出它匹配成功所需的“另一半”应该是多少。接着,我们只需在一个随身携带的清单中查找这个“另一半”是否出现过。如果在那儿,说明我们找到了失散的搭档;如果不在,就把当前的数字和它的位置记在清单上,留给后来的数字查看。这种方案利用哈希表(Map)将“漫长的寻找”变成了“瞬间的相遇”,在牺牲极小额外存储空间的情况下,极大地提升了处理的速度。

解法

哈希表解法
暴力解法
/**
 * 两数之和:哈希表解法
 * 给定一个整数数组 nums 和一个目标值 target,在该数组中找出和为目标值的两个整数,并返回它们的数组下标。
 * 
 * 时间复杂度: O(n) - 只需要遍历一次数组,哈希表的查找和插入操作平均时间复杂度为 O(1)。
 * 空间复杂度: O(n) - 在最坏情况下需要存储所有数组元素。
 * 
 * @param {number[]} nums - 输入的整数数组
 * @param {number} target - 目标和
 * @returns {number[]} - 返回两个数字的下标数组,如果未找到则返回空数组 []
 */
export function twoSum(nums, target) {
  // 1. 参数校验:如果数组不存在、不是数组或者长度不足 2(至少需要两个数才能求和)
  if (!nums || !Array.isArray(nums) || nums.length < 2) {
    return [];
  }

  // 2. 创建一个 Map 用来存储已经遍历过的数字及其索引
  // Key: 数字的值, Value: 该数字在原数组中的下标 (Index)
  const hashMap = new Map();

  // 3. 遍历数组
  for (let i = 0; i < nums.length; i++) {
    // 计算当前数字想要达成目标值所需要的“另一半”(也就是补数)
    const diff = target - nums[i];

    // 4. 检查补数是否已经在哈希表中
    // 如果在,说明我们之前遇到过它,它的下标 + 当前下标 i 就是结果
    if (hashMap.has(diff)) {
      // 返回保存的下标和当前下标
      return [hashMap.get(diff), i];
    }

    // 5. 如果补数不在哈希表里,将当前数字及其下标存入哈希表
    // 这样后面遍历到的数字如果需要它,就能快速找到
    hashMap.set(nums[i], i);
  }

  // 6. 如果遍历完都没有找到结果,按照约定返回空数组
  return [];
}

// --- 测试用例 (Test Cases) ---
/**
 * 运行示例
 */
// 示例 1: 标准情况
// nums = [2, 7, 11, 15], target = 9
// 2 + 7 = 9, 返回 [0, 1]
console.log('Test 1:', twoSum([2, 7, 11, 15], 9)); 

// 示例 2: 目标值由后面的数字组成
// nums = [3, 2, 4], target = 6
// 2 + 4 = 6, 返回 [1, 2]
console.log('Test 2:', twoSum([3, 2, 4], 6));

// 示例 3: 两个相同的数字相加
// nums = [3, 3], target = 6
// 3 + 3 = 6, 返回 [0, 1]
console.log('Test 3:', twoSum([3, 3], 6));

// 示例 4: 无解情况
console.log('Test 4 (No solution):', twoSum([1, 2, 3], 7));

// 示例 5: 边界情况(空数组或长度不足)
console.log('Test 5 (Empty/Short):', twoSum([1], 9));