面试中常被问及的一道面试题,其实这道题不难,关键是写出来的算法需要在时间复杂度上尽量做到最优。实现不难,但是要做到让面试官满意那就得多多思考。
# 方法1: 循环+indexOf
## 思路
循环数组,使用目标值减去当前循环的元素,带着计算出的减后的值去数组中寻找有没有这个值,如果有即indexOf不是-1,那当前循环的索引和indexOf返回的索引,即为答案
## 实现
```
function findIndexes1 (arr, target) {
let currentNum, otherNum, currentIndex, otherIndex
for (let i = 0, len = arr.length; i < len; i++) {
currentNum = arr[i]
// 当前元素如果大于目标值,则直接进行下一轮循环,加快检索速度
if (currentNum > target) {
continue
}
otherNum = target - currentNum
otherIndex = arr.indexOf(otherNum)
if (otherIndex !== -1) {
currentIndex = i
break
}
}
return [currentIndex, otherIndex]
}
```
## 说明
这个算法看似只有一个循环,实则不然。因为循环内部使用了indexOf方法,而这个方法底层仍然是靠循环实现的,所以时间复杂度仍然是 On平方
# 方法2 循环+Map
## 思路
思路和方法1其实特别像。只不过是把使用indexOf()在数组中寻找的方法替换为使用Map的has()在Map中寻找的方法。消除了嵌套循环,从而性能得到提升。具体做法是:先将数组转为Map,其中key为数组中的数值,value为数值的下标索引。然后循环数组,计算出另一个值,使用has()寻找是否有这个值,若找到,使用get()获取索引,跳出循环;若没找到,继续下轮循环。
## 实现
```
function findIndexes2 (arr, target) {
let map = new Map()
// 数组转Map key为数值 value为下表索引
arr.forEach((item, index) => {
map.set(item, index)
})
let currentIndex, otherIndex, currentNum, otherNum
for (let i = 0, len = arr.length; i < len; i++) {
currentNum = arr[i]
if (currentNum > target) {
continue
}
otherNum = target - currentNum
if (map.has(otherNum)) {
currentIndex = i
otherIndex = map.get(otherNum)
break
}
}
return [currentIndex, otherIndex]
}
```
# 综合性能测试
生成一个测试数组,这个数组共有100002个元素,其中前100000个元素均为1,最后两个元素分别是2和3。目标值设计的是5。这样做的目的是程序需要循环好多次才能找到结果,从而对比出性能的差异。
```
function getArr () {
let arr = []
let i = 0
while (i < 100000) {
arr.push(1)
i++
}
arr.push(2, 3)
return arr
}
```
```
let start = Date.now()
console.log(findIndexes1(getArr(), 5))
let end = Date.now()
console.log(end - start) // chrome 5753ms
```
```
let start = Date.now()
console.log(findIndexes2(getArr(), 5))
let end = Date.now()
console.log(end - start) // chrome 35ms
```
结果不言而喻,一个四位数,一个两位数,这性能差异完全不在一个数量级。