前言
很多人会说你一个前端,学习算法干什么,前端发展至今,不管大厂还是创业公司,都对于数据结构与算法有了一定的要求,其次是随着技术的发展,用户对于产品的性能要求越来越高,网页加载速度慢几百毫秒就可能失去成千上万的用户。所以,拥有良好的算法基础,可以写出高质量的代码逻辑成为了前端工程师的必备技能。(内容摘录于网上)
进入正题
题目是这样的,在递增数组中找出和为n的两个数(不考虑多个结果的情况如4+11=15,6+9=15,这里只考虑一种结果的情况),如在数组为[1,2,3,4,5,6,11]中找出和为15的两个数。
第一反应
很多人看到这道题的第一反应就是嵌套循环,写出来的代码大概是下面这个样子。
function findTwoNumbers1(arr,n){
if(arr.length === 0) return []
let res = []
// [arr.length-1] 最后一位不用比较
for(let i = 0;i<arr.length-1;i++){
let n1 = arr[i]
let flag = false;
// [i+1] 从i的后面一位开始比较
for(let j = i+1;j<arr.length;j++){
let n2 = arr[j]
if(n1+n2 === n){
res.push(n1)
res.push(n2)
flag = true
break;
}
}
// 找到结果就跳出循环
if(flag) break
}
return res;
}
// 功能测试
const arr = [1,2,3,4,5,6,11]
const n = 15
console.log(findTwoNumbers1(arr,n)) // [4,11]
问题
这个方法用了两层循环,那么从时间复杂度来分析的话达到了O(n^2),前端是重时间轻空间,所以本文不分析空间复杂度,那么时间复杂度如果达到了O(n^2),那么该算法基本上是不可用了,虽然内层循环没有达到全循环,但是从数量级上来说还是达到了O(n^2)。
用双指针的思想
题目中提到,是递增的数据,也即是有序的数组,算法界有一句话叫凡有序必二分,所以这里也可以借用二分的思想,但不是真正的二分,定义一个起始索引和一个结束索引,用这两个指针的值相加然后跟目标数n进行比较,如果这两个数相加大于n,那么结束索引就往前移动,如果小于n起始索引就往后移动,直到两个索引相交。代码写出来差不多是这样的。
function findTwoNumbers2(arr,n){
if(arr.length === 0) return []
let res = []
let sIndex = 0;
let eIndex = arr.length-1;
while(sIndex < eIndex){
let n1 = arr[sIndex];
let n2 = arr[eIndex];
let sum = n1+n2
if(sum > n){
eIndex--
}else if(sum < n){
sIndex++
}else{
res.push(n1)
res.push(n2)
break
}
}
return res;
}
// 功能测试
const arr = [1,2,3,4,5,6,11]
const n = 15
console.log(findTwoNumbers2(arr,n)) // [4,11]
结果
优化过后时间复杂度从O(n^2)降低到了O(n),在寻找难度越大,数量级越高的场景下,两种思路的性能差别会越大。
写在最后
如果本文对你有一点点帮助,希望能点赞支持一下,如果本文有错误的地方,希望能不吝赐教,感谢。