题干
给定一个整数数组 nums 和一个整数目标值 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 <= 103
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
错误答案:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
for(var i=0;i<nums.length;i++){
let neededNum = target - nums[i];
let neededIndex = nums.indexOf(neededNum);
if(neededIndex != null && i!=neededIndex){
return [i,neededIndex]
}
}
};
输入:
[3,2,3] 6
输出:
[1,-1]
预期结果:
[0,2]
反思:
完全没有考虑会出现重复数字的情况,而且我这个1,-1的结果怎么出来的呢?查了一下,Js里面的indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,如果没有找到匹配的字符串则返回 -1。那么indexOf()达不到我们的需求了,还是不能偷懒只能自己手写了。
暴力解法(正解):
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
for(var i=0;i<nums.length;i++){
let neededNum = target - nums[i];
for(var j = 0;j<nums.length;j++){
if(neededNum == nums[j] && j!=i){
return [i,j]
}
}
}
};
LeetCode跑出来的数据:
执行用时:92 ms, 在所有 JavaScript 提交中击败了32.64%的用户
内存消耗:37.9 MB, 在所有 JavaScript 提交中击败了60.27%的用户
时间复杂度: O(n^2)
反思2:
这个方法可以说真的是超级无敌暴力了,跑出来的数据也不怎么好看…于是思考有什么更好的办法去解决呢。我想到一个比较浪费空间但是有可能会变快的办法,那就是先把它们按照顺序再排一遍,这样再找的话,其实就不用再穷尽选项了…
画蛇添足想太多型新方案:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
//排序
var newArray = new Array();
nums.map((num,index) => {
newArray.push([num,index]);
newArray.sort((a,b) => { return a[0] - b[0]});
});
for(var i=0;i<nums.length;i++){
let neededNum = target - newArray[i][0];
for(var j=0;j<nums.length;j++){
if(newArray[j][0] > neededNum){
break;
}else if(newArray[j][0] == neededNum){
if(newArray[j][1] != newArray[i][1]){
return [newArray[i][1],newArray[j][1]];
}else{
if(newArray[j+1][0] == newArray[j][0]){
return [newArray[i][1],newArray[j+1][1]];
}
break;
}
}
}
}
};
LeetCode跑出来的数据:
执行用时:208 ms, 在所有 JavaScript 提交中击败了5.12%的用户
内存消耗:43.6 MB, 在所有 JavaScript 提交中击败了5.00%的用户
反思3:
似乎排序过的数据查找省的那点时间,还没有排序本身耗时多,是我太久没碰算法了,于是看了大佬们的解答…说要用哈希表,然而什么是哈希表我都快忘记的一干二净。刚好现在就来复习一下好了。
什么是哈希表?
哈希表是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
在js当中可以通过存成类似{"3":0,"2":1,"3":2}的形式,在用neededNum in hashMap判断是否在哈希表中,但是仍然存在前面说的类似indexOf的重复数字只能查到第一项的问题,于是有个比较tricky的办法,每次遍历一项往哈希表中插入一项。这样每次后面的数据只能往前面的表内查找,这样可以避免重复的问题。同理我之前的indexOf的代码也可以这么修改。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let hashMap = {}
for (let i = 0; i < nums.length; i++) {
let neededNum = target - nums[i];//获取需要匹配的
if (neededNum in hashMap) {//需要匹配的在hashMap里面有没有,有的话返回
return [i, hashMap[neededNum]]
}
hashMap[nums[i]] = i/*无论有没有都插入到hashMap,这样的话就是永远是往前面的找,所以也不会重复,因为在找的时候本身是没有被加入到hashMap当中去的*/
}
};
LeetCode跑出来的数据:
执行用时:92 ms, 在所有 JavaScript 提交中击败了32.64%的用户
内存消耗:38 MB, 在所有 JavaScript 提交中击败了48.21%的用户
时间复杂度: O(n)
空间复杂度: O(n)
使用indexOf的办法:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
let beforeNums = [];
for(var i=0;i<nums.length;i++){
let neededNum = target - nums[i];
let neededIndex = beforeNums.indexOf(neededNum);
if(neededIndex >-1){
return [i,neededIndex]
}
beforeNums.push(nums[i]);
}
};
相同思路使用Map()的办法:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
var map = new Map()
for (let i = 0; i < nums.length; i++) {
let neededNum = target - nums[i];
const getTargetValue = map.get(neededNum)//有的话返回下标,没有的话返回undefined
if (!isNaN(getTargetValue)) {
return [i, getTargetValue]
}
map.set(nums[i], i)
}
}
这三种方法时间和内存消耗上基本都没有区别