leetcode-1-两数之和

两数之和

题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

ini 复制代码
输入: nums = [2,7,11,15], target = 9
输出: [0,1]
解释: 因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

ini 复制代码
输入: nums = [3,2,4], target = 6
输出: [1,2]

示例 3:

ini 复制代码
输入: nums = [3,3], target = 6
输出: [0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

暴力解法

  1. 思路

    • 使用两层嵌套循环遍历数组。外层循环从数组的第一个元素开始,内层循环从外层循环当前元素的下一个元素开始。对于每一对元素,计算它们的和,并检查是否等于目标值 target。如果相等,则返回这两个元素的下标。
  2. 代码实现

ts 复制代码
function twoSum(nums: number[], target: number): number[] {
    for (let i = 0; i < nums.length; i++) {
        for (let j = i + 1; j < nums.length; j++) {
            if (nums[i] + nums[j] === target) {
                return [i, j];
            }
        }
    }
    return [];
}
  1. 分析
  • 时间复杂度 :O(n2),因为有两层嵌套循环,外层循环执行 n 次,内层循环对于每次外层循环平均执行 (n - 1) / 2 次,总体时间复杂度为 O(n×n)。
  • 空间复杂度:O(1),除了存储结果的数组外,只使用了常数级别的额外空间。
  1. 缺点:时间复杂度较高,当数组长度较大时,运行效率很低。对于大规模数据,计算量会急剧增加。

哈希表解法

  1. 思路
  • 使用一个哈希表(在 TypeScript 中可以用对象或者Map模拟)来存储数组元素及其下标。遍历数组时,对于每个元素 nums[i],计算 target - nums[i],然后检查这个差值是否已经在哈希表中。如果在哈希表中,则说明找到了两个数的和为 target,返回哈希表中差值对应的下标和当前下标 i。如果不在哈希表中,则将当前元素 nums[i] 及其下标 i 存入哈希表,继续遍历。
  1. 代码实现
ts 复制代码
//对象
function twoSum(nums: number[], target: number): number[] {
    const hashMap: { [key: number]: number } = {};
    for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i];
        if (complement in hashMap) {
            return [hashMap[complement], i]
        }
        hashMap[nums[i]] = i;
    }
    return [];
}

// Map
function twoSum(nums: number[], target: number): number[] {
  const hashMap = new Map();
  for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];
    if (hashMap.has(complement)) {
      return [hashMap.get(complement), i];
    }
    hashMap.set(nums[i], i);
  }
  return [];
}
  1. 分析

    • 时间复杂度:O(n),因为只需要遍历数组一次。每次在哈希表中查找和插入操作平均时间复杂度为 O(1)。
    • 空间复杂度:O(n),在最坏情况下,哈希表需要存储数组中的所有元素。
  2. 优点:相比暴力解法,哈希表解法的时间复杂度大大降低,在处理大规模数据时效率更高。虽然空间复杂度为 O(n),但在实际应用中,时间效率的提升往往更为关键。

最优解及原因

  1. 最优解:哈希表解法是本题的最优解。
  2. 原因:在大多数情况下,尤其是处理大规模数据时,时间复杂度是衡量算法优劣的重要指标。哈希表解法将时间复杂度从暴力解法的 O(n2) 降低到了 O(n),这使得算法在运行时间上有了显著的提升。虽然哈希表解法需要额外的 O(n) 空间,但以空间换取时间的方式在很多场景下是值得的。

拓展和题目变形

1.拓展

  • 如果需要返回所有满足和为 target 的二元组,而不只是一组解。

  • 思路:可以使用一个数组来存储所有符合条件的二元组。在哈希表解法的基础上,每次找到符合条件的二元组时,将其添加到结果数组中。

  • 代码实现

ts 复制代码
function twoSumAllPairs(nums: number[], target: number): number[][] {
    const hashMap: { [key: number]: number } = {};
    const result: number[][] = [];
    for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i];
        if (complement in hashMap) {
            result.push([hashMap[complement], i]);
        }
        hashMap[nums[i]] = i;
    }
    return result;
}
  1. 题目变形
  • 变形一 :如果数组中可能存在重复元素,且要求返回所有满足和为 target 的二元组,包括重复元素组成的二元组。

  • 思路:在上述返回所有二元组的代码基础上,修改哈希表的存储方式,使其可以处理重复元素。可以将哈希表的值改为数组,存储所有相同元素的下标。

  • 代码实现

ts 复制代码
function twoSumAllPairsWithDuplicates(nums: number[], target: number): number[][] {
    const hashMap: { [key: number]: number[] } = {};
    const result: number[][] = [];
    for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i];
        if (complement in hashMap) {
            for (const index of hashMap[complement]) {
                result.push([index, i]);
            }
        }
        if (!hashMap[nums[i]]) {
            hashMap[nums[i]] = [];
        }
        hashMap[nums[i]].push(i);
    }
    return result;
}
  • 变形二 :给定一个有序数组 nums,找到两个数使得它们的和最接近目标值 target

  • 思路:使用双指针法。初始化两个指针,一个指向数组开头,一个指向数组末尾。计算两个指针所指元素的和,与目标值比较。如果和等于目标值,直接返回;如果和大于目标值,将右指针左移;如果和小于目标值,将左指针右移。每次移动指针后,更新最接近目标值的和及对应的两个数。

  • 代码实现

ts 复制代码
function twoSumClosest(nums: number[], target: number): number[] {
    let left = 0;
    let right = nums.length - 1;
    let closestSum = nums[left] + nums[right];
    let result: number[] = [nums[left], nums[right]];
    while (left < right) {
        const sum = nums[left] + nums[right];
        if (sum === target) {
            return [nums[left], nums[right]];
        } else if (sum > target) {
            right--;
        } else {
            left++;
        }
        if (Math.abs(sum - target) < Math.abs(closestSum - target)) {
            closestSum = sum;
            result = [nums[left], nums[right]];
        }
    }
    return result;
}
相关推荐
CoderYanger33 分钟前
优选算法-栈:67.基本计算器Ⅱ
java·开发语言·算法·leetcode·职场和发展·1024程序员节
jllllyuz1 小时前
Matlab实现基于Matrix Pencil算法实现声源信号角度和时间估计
开发语言·算法·matlab
夏鹏今天学习了吗1 小时前
【LeetCode热题100(72/100)】前 K 个高频元素
leetcode
稚辉君.MCA_P8_Java1 小时前
DeepSeek 插入排序
linux·后端·算法·架构·排序算法
多多*1 小时前
Java复习 操作系统原理 计算机网络相关 2025年11月23日
java·开发语言·网络·算法·spring·microsoft·maven
初学者,亦行者1 小时前
DevUI微前端集成实战解析
前端·typescript
.YM.Z2 小时前
【数据结构】:排序(一)
数据结构·算法·排序算法
Chat_zhanggong3452 小时前
K4A8G165WC-BITD产品推荐
人工智能·嵌入式硬件·算法
百***48072 小时前
【Golang】slice切片
开发语言·算法·golang
墨染点香3 小时前
LeetCode 刷题【172. 阶乘后的零】
算法·leetcode·职场和发展