LeetCode1. 两数之和
更新:2021年7月20日16:43:59
问题描述及示例
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解
我的题解1(双层for
循环)
总体思路是使用双层for
循环来对数组进行遍历,指针i
指向第一个加数,指针j
指向第二个加数。外层循环用于在固定i
值,同时内层循环用于逐个匹配j
值。当满足nums[i] + nums[j] === target
时,可以立即返回结果。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
// i 用于固定第一个加数
for(let i = 0, length = nums.length; i < length; i++) {
// j 用于逐个匹配第二个加数
for(let j = i+1; j < length; j++) {
if(nums[i] + nums[j] === target) {
return [i, j];
}
}
}
};
提交记录
54 / 54 个通过测试用例
状态:通过
执行用时: 128 ms,在所有 JavaScript 提交中击败了25.27%的用户
内存消耗: 38.5 MB,在所有 JavaScript 提交中击败了56.91%的用户
时间:2021年7月20日16:48:00
我的题解2(单层for
循环)
上面的题解中用了双层for
循环,时间复杂度为O(n^2),下面的题解中只用了一层for
循环,我以为最后的表现会好很多,结果发现似乎并没有太多的提升。我猜想可能是includes
函数的实现中其实也用到了for
循环或者类似的思路,所以导致运行时间增加,但是内存消耗稍稍减小了。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let temp;
for(let i = 0, length = nums.length; i < length; i++) {
temp = target - nums[i];
// 注意下面的判断,不能光判断是否包含temp,还要求temp的下标不能和i的一样,
// 否则像[3,2,4], 6这样的用例就无法返回正确结果
// (预期返回:[1,2] 实际返回:[0,0])
if(nums.includes(temp) && nums.indexOf(temp) !== i) {
return [i, nums.indexOf(temp)];
}
}
};
提交记录
54 / 54 个通过测试用例
状态:通过
执行用时: 172 ms,在所有 JavaScript 提交中击败了11%的用户
内存消耗: 38.1 MB,在所有 JavaScript 提交中击败了70%的用户
时间:2021年7月20日17:41:53
我的题解3(利用Map
)
更新:2021年8月10日19:41:52
整体思路是用一个Map
类型的变量map
存储按情况存储nums
的下标和其元素值,其中,nums[i]
作为map
的key
,而i
作为相应的的value
。因为这样就可以比较方便地使用map
中的has()
函数来判断map
中是否包含某个元素值。
在遍历nums
数组时,先判断map
中是否包含值为target - nums[i]
的数组元素,如果有,则将该元素的下标和当前遍历的元素下标作为返回值返回。注意题目里说了可以假设每种输入只会对应一个答案,所以这里是可以直接返回的,若是有多组答案,则还要进行后续遍历和判断,当然相应的逻辑也要有所改变。
如果map
中没有包含值为target - nums[i]
的数组元素,则使用set()
方法将当前遍历元素放入map
中。
当数组遍历完后仍然没有匹配到合适的元素(也就是map
中已经被加入了所有的nums
中的元素),那么就要按题目要求返回空数组,当然如果nums
一开始就为空,那么经过for
中的判断后也会触发最后的返回。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let map = new Map();
for(let i = 0; i < nums.length; i++) {
// 如果map中能匹配到与当前遍历的数组元素值之和的元素,则可以直接将相应结果返回
if(map.has(target - nums[i])) {
return [i, map.get(target - nums[i])];
}
// 如果没有相应的匹配,那么则将当前遍历数组存入map
map.set(nums[i], i);
}
// 如果nums为空或者不存在符合条件的两个元素,则返回空数组
return [];
};
提交记录
54 / 54 个通过测试用例
状态:通过
执行用时:72 ms, 在所有 JavaScript 提交中击败了92.92%的用户
内存消耗:40.2 MB, 在所有 JavaScript 提交中击败了17.73%的用户
时间:2021/08/10 19:33
可以看到,利用Map
类型的变量存储相关的下标和元素值之后,在时间表现上有了比较大的提升。但是在空间方面的表现还是不好。
其实我还想过也参照【【算法-LeetCode】15. 三数之和_赖念安的博客-CSDN博客】中的双指针的写法,但是用这种方法需要先将数组排序,而排序后将会打乱数组元素原有的下标,这样就无法返回正确的结果,而在三数之和的题目中是不需要考虑这个的。当然如果用map存储原来的数组元素和下标的对应值,也许也能行得通,但是相比于上面题解3中的做法,那样无疑是绕了一个弯,徒劳增加了时间消耗和空间消耗,显得很没必要。
【更新结束】
官方题解
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
更新:2021年7月29日20:00:10
【更新结束】
有关参考
更新:2021年7月20日16:26:48
参考:Array.prototype.map() - JavaScript | MDN参考:parseInt - JavaScript | MDN
参考:Number.parseInt() - JavaScript | MDN
参考:JS深入理解—探索parseInt函数_LS.Yu.大前端-CSDN博客
参考:JavaScript Array map() 方法 | 菜鸟教程
参考:Array.prototype.indexOf() - JavaScript | MDN
更新:2021年7月20日17:49:35
参考:JS 中数组常用方法以及它的原理实现(三)
更新:2021年8月10日20:14:45
参考:Map.prototype.get() - JavaScript | MDN
参考:【微信公众号:三分钟学前端 2021-07-27】每日算法:两数之和
参考:JavaScript forEach() 方法 | 菜鸟教程