Leetcode热题100——1.两数之和

58 阅读3分钟

题目描述

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

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

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

示例 :

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

题解 (JavaScript)

暴力枚举

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    for (let i = 0 ;i<nums.length-1;i++){
        for(let j = i+1;j<nums.length;j++){
            if(nums[i]+nums[j]===target){
                return [i,j];
            }
        }
    }
    return [];
};

双层循环暴力枚举每一种组合

外层 i i = 0``i < nums.length - 1

i 只需遍历到倒数第二个元素。 这是因为如果 i 到了最后一个元素,j 将没有后续元素可供检查,可以提前结束。

内层 j j = i + 1``j < nums.length

j 从 i 的下一个位置开始,避免了

  1. 重复计算(如 A+B 和 B+A);
  2. 自身相加(使用两次相同元素)。j 必须遍历到最后一个元素 (nums.length−1)。

时间、空间复杂度

时间复杂度: O(N^2)

由于代码采用了两层嵌套循环,在最坏情况下,它需要执行大约 N×N 次操作(其中 N 是数组的长度)。虽然代码简单易懂,但对于包含数万个元素的数组,这种方法将非常慢。

空间复杂度 O(1)

算法运行不需要额外的辅助数据结构,空间利用率极高

哈希表

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
    const map = new Map();
    for (let i = 0; i < nums.length; i++) {
        if (map.get(target - nums[i]) != undefined) {
            return [i, map.get(target - nums[i])]
        } else {
            map.set(nums[i], i);
        }
    }
};

这个解法采用了 哈希表(Map) 的技术。

暴力枚举法的时间复杂度是 O(N2)。为了提高效率,我们需要将查找 target−A 的时间从 O(N)降低到 O(1)

在数据结构中,能够提供 O(1) 快速查找(平均时间复杂度)的就是 哈希表(在 JavaScript 中对应 Map 或对象 {})。

  1. 当我们遍历到当前数字 nums[i] 时,我们需要的另一个数字是 complement (互补数) ,即:

    complement=target−nums[i]

  2. 我们只进行一次循环。在每次迭代中:

    • 先检查哈希表(Map)中是否已经存在这个 complement

      • 如果存在:立即返回当前索引 i 和 Map 中存储的索引。
      • 如果不存在:继续。
    • 后将当前的数字 nums[i] 及其索引 i 存入 Map 中,以便后续的数字能找到它,作为 complement

时间、空间复杂度

时间复杂度:O(N)

只进行了一次数组遍历,循环次数为 N。 在循环体内部,Map 的 get()set() 操作的平均时间复杂度都是 O(1)。 因此,总的时间复杂度为 O(N)

空间复杂度:O(N)

使用了一个额外的 Map 数据结构来存储数组中的元素及其索引。 在最坏情况下(没有找到解,或者解在数组末尾),Map 中会存储接近 N 个元素。 因此,空间复杂度与输入规模成正比,为 O(N)