在一个数组中找出和为n的两个数
- 有一个递增的数组[1,2,4,7,11,15]和一个n=15
- 数组中有两个数,和是n。即4 + 11 === 15
- 写一个JS函数,找出这两个数
常规思路
- 嵌套循环,找到一个数,然后去遍历下一个数,求和,判断
- 时间复杂度是O(n^2), 不可用
代码实现——嵌套循环
function searchNum1 (arr:number[],n:number):number[] {
const len = arr.length
if (len < 2) return [];
const res:number[] = []
for (let i = 0; i < len; i++) {
let flag = false // 判断是否找到了结果
for (let j = i + 1; j < len; j++) {
let sum = arr[i] + arr[j]
if (sum === n ) {
flag = true
res.push(arr[i])
res.push(arr[j])
break;
}
}
if (flag) break;
}
return res
}
功能测试——嵌套循环
const arr = [1,2,4,7,11,15]
console.log(searchNum1(arr,15)) // [4, 11]
单元测试——嵌套循环
describe('查找两数之和',() => {
it('正常情况', () => {
const arr = [1,2,4,7,11,15]
const res = searchNum1(arr,15)
expect(res).toEqual([4,11])
})
it('空数组', () => {
const res = searchNum1([],15)
expect(res).toEqual([])
})
it('找不到值的情况', () => {
const arr = [1,2,4,7,11,15]
const res = searchNum1(arr,100)
expect(res).toEqual([])
})
})
利用递增有序的特性找出优化方法
- 随便找两个数
- 如果和大于n,则需要向前寻找
- 如果和小于n,则需要向后寻找——二分法思想
使用双指针,时间复杂度降低到O(n)
- 定义i指向头,j指向尾,求arr[i] + arr[j]
- 如果大于n,则需要向前移动
- 如果小于n,则需要向后移动
代码实现——双指针
function searchNum2 (arr:number[],n:number):number[] {
const len:number = arr.length
if (len < 2) return []
const res:number[] = []
let i = 0 // head
let j = len -1 // tail
while(i < j) {
let sum = arr[i] + arr[j]
if (sum > n) {
// 和大于n,则向前找,尾部下标-1
j--;
} else if (sum < n) {
// 和小于n,则向后找,头部+1
i++;
} else {
// 相等
res.push(arr[i])
res.push(arr[j])
break;
}
}
return res
}
功能测试——双指针
const arr = [1,2,4,7,11,15,18,19,20]
console.log(searchNum2(arr,15)) // [4,11]
单元测试——双指针
describe('查找两数之和',() => {
it('正常情况', () => {
const arr = [1,2,4,7,11,15]
const res = searchNum2(arr,15)
expect(res).toEqual([4,11])
})
it('空数组', () => {
const res = searchNum2([],15)
expect(res).toEqual([])
})
it('找不到值的情况', () => {
const arr = [1,2,4,7,11,15]
const res = searchNum2(arr,100)
expect(res).toEqual([])
})
})
性能比较——嵌套循环VS双指针
const arr = [1,2,1,2,1,2,1,2,1,2,1,2,4,7,11,15,18,19,20]
console.time('searchNum1')
for (let i = 0; i < 100 * 10000; i++) {
searchNum1(arr,15)
}
console.timeEnd('searchNum1')
console.time('searchNum2')
for (let i = 0; i < 100 * 10000; i++) {
searchNum2(arr,15)
}
console.timeEnd('searchNum2')
打印结果:
searchNum1: 133.034ms
searchNum2: 38.977ms
时间复杂度:嵌套循环O(n^2) ,双指针O(n)
综上所述,双指针用于解决此题更优
总结
- 时间复杂度达到O(n^2),是不可用的算法
- 凡是有序,都可以考虑二分法思想
- 优化嵌套循环,可以考虑“双指针 ”