算法题讲解
真题描述:
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
思路分析
我相信很多同学看一眼就很快能得出一个最基本的思路:两层循环来遍历同一个数组;第一层循环遍历的值记为 a,第二层循环时遍历的值记为 b;若 a+b = 目标值,那么 a 和 b 对应的数组下标就是我们想要的答案。
方法一
代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
for(let i =0 ,len = nums.length;i<len;i++){
for(let j=i+1;j<len;j++){
if(nums[i]+nums[j]===target){
return [i,j]
}
}
}
};
代码讲解:
- 定义一个twoSum函数。
- 在函数里我们先执行一个for循环从数组的第一位起往后遍历。
- 然后在第一个for循环里面,再嵌套一个for循环,内嵌的for循环从数组的第二位起往后遍历。
- 然后在内嵌的for循环里做一个if判断如果我们两个循环的当前值相加等于我们的target就返回,我们两个循环的当前次数,否则继续执行循环。
for循环虽然可以解决我们的题目,但是for循环写起来实在笨重,而且耗时很久。那我们还有没有更加快捷、简单的解题方式呢?
当然有我们可以用Map函数去解答
Map
我们可以使用Map来解题,用空间来换时间。优化成一层循环。因为两层循环很多情况下都意味着 O(n^2) 的复杂度,这个复杂度非常容易导致你的算法超时。即便没有超时,在明明有一层遍历解法的情况下,你写了两层遍历,面试官对你的印象分会大打折扣。
所以我们以后做算法题的时候,要有这样的一种本能:当发现自己的代码里有两层循环时,先反思一下,能不能用空间换时间,把它优化成一层循环。
大家记住一个结论:几乎所有的求和问题,都可以转化为求差问题
这道题就是一个典型的例子,通过把求和问题转化为求差问题,事情会变得更加简单
思路分析
我们可以在遍历数组的过程中,增加一个 Map 来记录已经遍历过的数字及其对应的索引值。然后每遍历到一个新数字的时候,都回到 Map 里去查询 targetNum 与该数的差值是否已经在前面的数字中出现过了。若出现过,那么答案已然显现,我们就不必再往下走了。
方法二
代码
第二种解法,使用map对象
var twoSum = function(nums, target) {
// 拿到数组长度
let numsLen = nums.length
// 创建一个Map对象
const map =new Map()
for(let i =0; i<numsLen;i++){
// 拿到目标值与当前数组相减的值
let a = target-nums[i]
// 判断map里是否存在这个值,存在的话退出循环
if(map.has(a)){
// 获取对应的值和键 返回结果
return [map.get(a),i]
}
// 把我们当前数组的值和键存储在map里面
map.set(nums[i],i)
}
};
- 定义一个twoSum函数。
- 然后在函数里面定于一个变量numsLe用来存放数组长度,以及一个Map对象。
- 然后我们创建一个for循环,在循环里面我们要拿到目标值与当前数组相减的值并且用一个变量存储。已经把我们当前数组的值和键存储在Map里面
- 然后我们进行判断我们目标值与当前数组相减的值是否存在我们的Map里面,如果存在的话退出循环,并且返回当前的这个两数值的下标值。
总结
- 在解算法题时我们可以记住一个解题思路结论:几乎所有的求和问题,都可以转化为求差问题
- 我们以后做算法题的时候,当发现自己的代码里有两层循环时,先反思一下,能不能用空间换时间,把它优化成一层循环。
- Map函数的使用
写在最后
伙伴们,如果你觉得我写的文章对你有帮助就给zayyo点一个赞👍或者关注➕都是对我最大的支持。当然你也可以加我微信:IsZhangjianhao,邀你进我的前端学习交流群,一起学习前端,成为更优秀的工程师~