算法题 - 在递增数组中找出和为n的两个数

144 阅读1分钟

前言

很多人会说你一个前端,学习算法干什么,前端发展至今,不管大厂还是创业公司,都对于数据结构与算法有了一定的要求,其次是随着技术的发展,用户对于产品的性能要求越来越高,网页加载速度慢几百毫秒就可能失去成千上万的用户。所以,拥有良好的算法基础,可以写出高质量的代码逻辑成为了前端工程师的必备技能。(内容摘录于网上)

进入正题

题目是这样的,在递增数组中找出和为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),在寻找难度越大,数量级越高的场景下,两种思路的性能差别会越大。

写在最后

如果本文对你有一点点帮助,希望能点赞支持一下,如果本文有错误的地方,希望能不吝赐教,感谢。