前言
题目如下:输入一个有序数组(从小到大),一个变量n(number),找出数组中两个元素之和为n的元素,使用数组形式返回
样例输出
示例1:
输入:
arr = [1, 2, 4, 7, 11, 15]
n = 15
输出:
arr = [4, 11]
示例2:
输入:
arr = [1, 2, 4, 7, 11, 15]
n = 100
输出:
arr = []
方法一(嵌套循环)
解题思路
通过for循环先遍历第一个元素,之后内部嵌套for循环,从该元素的下一位开始遍历,直到两个元素之和为n,或者都不为n,那么进行下一次循环,由于双for嵌套, 没有跳步情况因此时间复杂度为O(n^2)
代码实现
export function findTowNumbers1(arr: number[], n: number): number[] {
const res: number[] = []
const length = arr.length
if (length === 0) return res
// O(n^2)
for (let i = 0; i < length - 1; i++) {
const n1 = arr[i]
let flag = false // 是否得到了结果
for (let j = i + 1; j < length; j++) {
const n2 = arr[j]
if (n1 + n2 === n) {
res.push(n1)
res.push(n2)
flag = true
break
}
}
if (flag) break
}
return res
}
方法二(双指针)
解题思路
只使用一次for循环,别从头部和末尾开始移动,当两个元素之和大于n,末尾元素向前移动一位;当两个当两个元素之和小于n,头部元素向后移动一位,继续比较,由于只使用了一次for循环,因此时间复杂度为O(n)
代码实现
export function findTowNumbers2(arr: number[], n: number): number[] {
const res: number[] = []
const length = arr.length
if (length === 0) return res
let i = 0 // 头
let j = length - 1 // 尾
// O(n)
while (i < j) {
const n1 = arr[i]
const n2 = arr[j]
const sum = n1 + n2
if (sum > n) {
// sum 大于 n ,则 j 要向前移动
j--
} else if (sum < n) {
// sum 小于 n ,则 i 要向后移动
i++
} else {
// 相等
res.push(n1)
res.push(n2)
break
}
}
return res
}
性能测试
console.time('findTowNumbers1')
for (let i = 0; i < 100 * 10000; i++) {
findTowNumbers1(arr, 15)
}
console.timeEnd('findTowNumbers1') // 730ms
console.time('findTowNumbers2')
for (let i = 0; i < 100 * 10000; i++) {
findTowNumbers2(arr, 15)
}
console.timeEnd('findTowNumbers2') // 102ms
性能测试结果验证了我们对两个方法的时间复杂度的推测
单元测试
describe('两数之和', () => {
it('正常情况', () => {
const arr = [1, 2, 4, 7, 11, 15]
const res = findTowNumbers2_1(arr, 15)
expect(res).toEqual([4, 11])
})
it('空数组', () => {
const res = findTowNumbers2_1([], 100)
expect(res).toEqual([])
})
it('找不到结果', () => {
const arr = [1, 2, 4, 7, 11, 15]
const n = 100
const res = findTowNumbers2_1(arr, n)
expect(res).toEqual([])
})
})