两数之和
题目
输入一个递增的数字数组,和一个数字 n 。求和等于 n 的两个数字。
例如输入 [1, 2, 4, 7, 11, 15] 和 15 ,返回两个数 [4, 11]
分析
注意题目的要点
- 递增,从小打大排序
- 只需要两个数字,而不是多个
常规思路
嵌套循环,找个一个数,然后再遍历剩余的数,求和,判断。
时间复杂度 O(n^2) ,基本不可用。
利用递增的特性
数组是递增的
- 随便找两个数
- 如果和大于 n ,则需要向前寻找
- 如果和小于 n ,则需要向后寻找 —— 二分法
双指针(指针就是索引,如数组的 index)
- i 指向头,j 指向尾, 求 i + j 的和
- 和如果大于 n ,则说明需要减少,则 j 向前移动(递增特性)
- 和如果小于 n ,则说明需要增加,则 i 向后移动(递增特性)
时间复杂度降低到 O(n)
答案
1.嵌套循环版(性能差,不推荐)
/**
* 寻找和为 n 的两个数(嵌套循环)
* @param arr arr
* @param n n
*/
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
}
2.双指针版(推荐):
/**
* 查找和为 n 的两个数(双指针)
* @param arr arr
* @param n 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
}
划重点
- 有序数据,要善用二分法
- 优化嵌套循环,可以考虑“双指针”
非递增数组
给定一个任意排序的整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
力扣:1. 两数之和 - 力扣(LeetCode) (leetcode-cn.com)
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
const map = new Map()
for(let i = 0; i < nums.length; i++) {
const n = nums[i]
const n2 = target - n
if(map.has(n2)) {
return [map.get(n2), i]
} else {
map.set(n, i)
}
}
};
/**
* 时间复杂度是 O(n) 空间复杂度 O(n)
*/