题目描述
给定一个整数数组 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 的下一个位置开始,避免了:
- 重复计算(如 A+B 和 B+A);
- 自身相加(使用两次相同元素)。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 或对象 {})。
-
当我们遍历到当前数字 nums[i] 时,我们需要的另一个数字是
complement(互补数) ,即:complement=target−nums[i]
-
我们只进行一次循环。在每次迭代中:
-
先检查哈希表(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) 。