两数之和
暴力解法
时间复杂度:每个元素需要遍历n-1个元素,共有n个元素,复杂度为
空间复杂度:
function twoSum(nums: number[], target: number): number[] {
let res: number[] = []
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
res.push(i)
res.push(j)
}
}
}
return res
};
优化解法
时间复杂度:对于数组的遍历,是必不可少的,但是检查某个元素是否可以相加得到target不一定要花费,可以存储在map中,s时间内判断是否满足条件。总体复杂度
空间复杂度:
function twoSum(nums: number[], target: number): number[] {
let res: number[] = [], cache: Map<number, number> = new Map()
for (let i = 0; i < nums.length; i++) {
const num1: number = nums[i]
const num2Index: number | undefined = cache.get(target - num1)
if (num2Index !== undefined) {
res.push(i)
res.push(num2Index)
break
} else {
cache.set(num1, i)
}
}
return res
};
三数之和
暴力解法
时间复杂度:,超出时间限制了。。。
function compareTwoArray(nums1: number[], nums2: number[]): boolean {
if (nums1.length !== nums2.length) return false
for (let i = 0; i < nums1.length; i++) {
if (nums1[i] !== nums2[i]) return false
}
return true
}
function threeSum(nums: number[]): number[][] {
let res: number[][] = []
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
for (let k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] === 0) {
const exist: boolean = !!res.find(item => {
return compareTwoArray([nums[i], nums[j], nums[k]].sort(), item)
})
!exist && res.push([nums[i], nums[j], nums[k]].sort())
}
}
}
}
return res
}
优化解法
时间复杂度: 空间复杂度: 在计算两数之和时,本解法采用了双指针方案,但是该方案依赖于数组是有序的。
function threeSum(nums: number[]): number[][] {
// 先对数组进行排序,便于双指针查找
nums.sort((a, b) => a - b)
let res: number[][] = []
if (nums.length < 3) return []
for (let i = 0; i < nums.length - 2; i++) {
// 避免重复
if (i > 0 && nums[i] === nums[i - 1]) continue
// 提前退出
if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break
if (nums[i] + nums[nums.length - 2] + nums[nums.length - 1] < 0) continue
let j = i + 1,
k = nums.length - 1
while (j < k) {
const sum = nums[i] + nums[j] + nums[k]
if (sum === 0) {
res.push([nums[i], nums[j], nums[k]])
j++
while (j < k && nums[j] === nums[j - 1]) j++
k--
while (j < k && nums[k] === nums[k + 1]) k--
} else if (sum > 0) {
k--
} else {
j++
}
}
}
return res
}
最接近的三数之和
跟三数之和类似,但是需要注意指针状态的转移:
-
当前sum > target
- 距离target更近时,需要更新距target最近的值
- right--
-
当前sum < target
- 距离target更近时,需要更新距target最近的值
- left++
-
当前sum = target
- 直接返回结果
function threeSumClosest(nums: number[], target: number): number {
nums.sort((a, b) => a - b)
let res: number = Infinity
for (let i = 0; i < nums.length - 2; i++) {
let left = i + 1,
right = nums.length - 1
while (left < right) {
const sum = nums[i] + nums[left] + nums[right]
if (sum > target) {
if (Math.abs(sum - target) < Math.abs(res - target)) {
res = sum
}
right--
} else if (sum < target) {
if (Math.abs(sum - target) < Math.abs(res - target)) {
res = sum
}
left++
} else {
return target
}
}
}
return res
}
三数之和的多种可能性
整体迭代流程与三数之和类似,需要注意状态的转移
- sum > target时,right--
- sum < target时,left++
- sum = target时
- arr[left] = arr[right], 如 1 1 1 1,此时结果为,n的值为right - left + 1
- arr[left] !== arr[right],如 1 1 2 2,左边相同数字个数 * 右边相同数字个数
function threeSumMulti(arr: number[], target: number): number {
arr.sort((a, b) => a - b)
let res: number = 0
for (let i = 0; i < arr.length - 2; i++) {
let left = i + 1,
right = arr.length - 1
while (left < right) {
const sum = arr[i] + arr[left] + arr[right]
if (sum > target) {
right--
} else if (sum < target) {
left++
} else {
if (arr[left] === arr[right]) {
res += ((right - left) * (right - left + 1)) / 2
break
} else {
let leftNum = 1
let rightNum = 1
left++
while (arr[left] === arr[left - 1]) {
left++
leftNum++
}
right--
while (arr[right] === arr[right + 1]) {
right--
rightNum++
}
res += leftNum * rightNum
}
}
}
}
return res % (1000000000 + 7)
}
竟然是runtime和memory双100%!!!
总结
a+b=c
对于数组中两个数等于某个数的场景,正常双向指针即可
a+b>c
数组中两个数之和大于某个数的场景,c从最大值反向枚举(假设c的index为k)
- left + right < target时,left++
- left + right > target时,从left到right-1的值和right相加时均大于target,执行相关操作后,right--
- left + right = target,执行相关操作
a+b<c
- left + right < target时,从left+1到right的值和left相加均小于target,执行相关操作后,left++
- left + right > target时,right--
- left + right = target,执行相关操作