算法进阶—[LeetCode] 1. Two Sum 两数之和

1,159 阅读2分钟

方法一: 暴力枚举

思路以及算法

最容易想到的方法是枚举数组中的每一个数x,寻找数组中是否存在target-x

当我们使用遍历整个数组的方式寻找target-x时候,需要注意到每一个位于x之前的元素都已经和x匹配过,因此不能进行重复匹配。而每一个元素不能被使用两次,所以我们只需要在x后面的元素中寻找 target-x。

这个算法的时间复杂度是O(n^2)。我们想要实现一个O(n)的算法来是实现。

代码

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

复杂度分析:

  • 时间复杂度:O(N^2) ,其中 NN 是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次.
  • 空间复杂度:O(1)。

方法二 借用hashMap:

由于暴力搜索的方法是遍历所有的两个数字的组合,然后算其和,这样虽然节省了空间,但是时间复杂度高,一般来说,为了减少时间的复杂度,需要使用空间来换,这里我们想要使用线性的时间复杂度来解决问题,也就是说,只能遍历一个数字,而另外一个数字呢,可以事先将其存储起来,使用一个Map数据结构,来建立数字和坐标之间的映射关系,由于Map是常数级查找效率,这样在遍历数组的时候,用target减去遍历到的数字,就是另外一个需要的数字了,直接在Map中查找其是否存在即可,需要注意的是,判断查找的数字不是第一个数字,比如target是4,遍历得到了一个2,那么另外一个2不能是之前的那个2,整个实现步骤为: 先遍历一遍数组,建立Map映射,然后再遍历一遍,开始查找,找到则记录index.

代码:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function (nums, target) {
  let result = [];
  let map = new Map();

  for (let i = 0; i < nums.length; i++) {
    map.set(nums[i], i);
  }

  for (let i = 0; i < nums.length; i++) {
    let anotherOne = target - nums[i];
    if (map.has(anotherOne) && map.get(anotherOne) !== i) {
      result.push(i);
      result.push(map.get(anotherOne))
      break;
    }
  }
  return result
};

复杂度分析

  • 时间复杂度: O(N), 其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。
  • 空间复杂度: O(N), 其中 NN 是数组中的元素数量。主要为哈希表的开销。