普通版两数之和
最简单的办法,双重循环,如果 i + j === target 就直接返回
暴力版:
var twoSum = function(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
return [j, i]
}
}
}
};
优化版:
暴力版我们使用了两层循环,来处理,这样的时间复杂度为 O(n2),并且因为数组是无序的,我们没办法使用双指针来进行处理,所以这里其实可以使用一个 Map 来进行存储,Map 的 key 就是 当前的值,value 是当前的下标,然后我们判断 如果 target - nums[i] === target 的话,就是我们需要找的结果,这样最终的时间和空间的复杂度 都是O(n)。
function twoSum (nums, target) {
const map = new Map()
for (let i = 0; i < nums.length; i++) {
if (map.has(target - nums[i])) {
return [map.get(target - nums[i]), i]
}
map.set(nums[i], i)
}
};
有序数组的两数之和
上面我我们实现了无序数组的两数之和,现在我们在看下有序版的两数之和,有序版的话,我们可以通过双指针来实现,其实就2个变量,一个指向开始,另外一个指向结束的,每次循环的时候,l + r 去判断 是否大于 target, 大于的话,就 r--,如果小于的话,r++ 相等的话,直接返回就行。
function twoSum (numbers: number[], target: number): number[] {
let l = 0
let r = numbers.length - 1
while (l <= r) {
if (numbers[l] + numbers[r] === target) {
return [l + 1, r + 1]
}
if (numbers[l] + numbers[r] < target) {
l++
}
if (numbers[l] + numbers[r] > target) {
r--
}
}
}
通过类型体操来实现两数之和
上面我们大概过了下 leetcode 的两数之和,现在如何用类型体操来实现两数之和呢?
typescript 强大的类型能力,不仅可以实现类型推断,我们还可以用类型来实现函数的功能,大部分应该都可以实现,从而来实现类型推断,虽然写上去会复杂一丢丢,但是对于开发体验会友好点。
大概的说下思路,这里采用双指针的思路,去进行实现,由于 ts 类型并没一些 好用的 api,所以这些都要自己实现,这边的主要思路是如果不匹配的话,就删除第一个或者最后一个,直到等于 target,抽空去看下如何用类型实现 index 获取数组对应的值,这样的话,就不需要删除数组的第一个和最后一个了。
type shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never
type pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never
// 判断 2 个数 是否相等
type If<number1 extends unknown, number2 extends unknown> = number1 extends number2 ? true : false
// 获取数组第一个值
type getFirst<T extends unknown[]> = T extends [infer F, ...unknown[]] ? F : never
// 获取数组最后一个值
type getEnd<T extends unknown[]> = T extends [...unknown[], infer End] ? End : never
// 填充 n 个值
type fill<Len extends number, V extends any, R extends unknown[] = []> = R['length'] extends Len ? R : fill<Len, V, [...R, V]>
// 实现加法
type add<number1 extends number, number2 extends number> = [...fill<number1, unknown>, ...fill<number2, unknown>]['length']
// 大于
type greaterThan<number1 extends number, number2 extends number, Len extends unknown[] = []> =
number1 extends number2 ? false :
Len['length'] extends number1 ? false :
Len['length'] extends number2 ? true : greaterThan<number1, number2, [...Len, unknown]>
/**
* 通过类型体操,来实现两数之和
*/
type TwoSum<T extends number[], target extends number> =
// 如果 数组 长度小于2 直接返回 never
greaterThan<T['length'], 1> extends false ? never
// 如果两个值相等,则返回当前数组符合条件的结果
: add<getFirst<T>, getEnd<T>> extends target ? [getFirst<T>, getEnd<T>]
// 如果左边 + 右边的 比 target 大,删除数组最后一个,在递归调用计算
: greaterThan<add<getFirst<T>, getEnd<T>>, target> extends true ? TwoSum<pop<T>, target>
// 如果比 target 小 ,则相反 依然递归计算
: greaterThan<add<getFirst<T>, getEnd<T>>, target> extends false ? TwoSum<shift<T>, target>
: never
type result11 = TwoSum<[1, 2, 3, 4, 5, 6], 9> // [2, 7]
type result12 = TwoSum<[1, 2, 3, 4, 100, 104], 109> // never
差不多就是这样了。