前言
每一个程序员应该都对力扣非常熟悉,多刷算法题都有些什么好处呢?
- 提升算法和数据结构能力 :帮助深入理解和掌握各种算法和数据结构。
- 增强问题解决能力:培养分析问题、设计算法和编写有效代码的能力。
- 提高代码质量和效率:优化代码,提高代码的执行效率和性能。
- 拓展编程思维:接触不同的问题类型,拓展编程思维和思路。
- 备战面试:在面试中经常会涉及到算法和数据结构的问题。
- 提升编程技能:通过实践不断提高编程技能和经验。
- 加强逻辑思维能力:锻炼逻辑思维和推理能力。
- 与其他程序员交流和学习:可以与其他程序员分享经验和解决方案。
那么今天,就让我们一起回到梦开始的地方,感受一下力扣的第一题,大家也可以给出自己的想法或者思路,为我们打开跨越时间与空间的大门。
题目
思路一(暴力枚举)
试想,有了目标值,又知道数组的每一个值(遍历数组),我们是否可以拿到数组中前一个值的同时,用目标值与手上的值做差,并得到这个差值,再通过遍历的方式检索判断这个值是否存在。
ini
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]+nums[j] == target){
return [i,j];
}
}
}
};
时间复杂度O(n^2)
可以看到,此处用时非常多,在这个资源过剩的时代,时间就显得异常宝贵了,因此我们需要继续优化算法,想出更好的解答方式
思路2(数组的indexOf()方法)
我们需要需到数组里的每一个值,因此第一层循环还是必不可少,但是我们可以通过数组的方式,用目标值减去数组中的某个值,得到差值,再通过数组中已经封装好了的方法nums.indexOf(插值,从哪里开始),这个方法如果存在值,则返回下标,否则返回-1
ini
var twoSum = function(nums, target) {
for (let i = 0; i < nums.length; i++) {
var res = target - nums[i]
var index = nums.indexOf(res,i+1)
if(index!== -1 ){
return [i,index]
}
}
};
可以看见,结果依旧不尽人意,原因是底层已经封装好了的indexOf方法事实上也是套用了一层循环实现的,这里相当于还是两层循环,只是代码少了一点。
思路三(利用对象)
试想一下,我们是否可以先把拿到的值与目标值做差,也就是说这个差值就是我们的期望值,我们希望后续数组中能够出现这个值与我们目前遍历到的这个值进行配对,我们都知道对象中的属性都是键值对,我们可以创建一个空对象,遍历第一次的时候,取到这个差值作为键,将目前遍历到的值的下标作为值,毕竟我们最终期望的就是下标,第一次遍历一定是没用的,因为对象是空的,但是我们往后遍历时,如果真的出现了我们要的期望值,那我们通过对象.的方式进行判断,如果没有,返回值会是undefined,如果有则会返回我们第一个值的下标。
ini
var twoSum = function(nums, target) {
// 创建空对象
var diff = {}
// 第一层遍历取值
for (let i = 0; i < nums.length; i++) {
// 真的取到了,没取到会返回undefined
// 注意这里diff中是数组本身,而不是target-nums[i],因为在我们的存储逻辑是(目标-目前值)也就是我们的期望值,键值对对应的值是目前值的下标
if(diff[nums[i]]!= undefined){
// 返回第一个值的下标i和期望值下标
return [i,diff[nums[i]]]
}
diff[target-nums[i]] = i
}
};
由此能够看到,时间提升上去了,时间复杂度为O(1)!
结语
初始算法时,我们需要不断探索,在一次次更优解的探索中,找到好的方法并转化为自己的解题思路,这样后续的算法题我们也都能想到很多的解题办法,并理解时间与空间带来的不同效益。