使用typescript类型实现ThreeSum

目录
  • 前言
  • 思路整理
  • 实现TwoSum
    • 实现减法
    • 元祖中是否包含差值
    • 递归元组
    • 测试
  • 实现ThreeSum
    • 实现排序
    • 实现ThreeSum
    • 测试

前言

本文执行环境typescript,版本4.7.4

不使用typescript的计算能力,通过类型来实现ThreeSum

思路整理

实现ThreeSum之前我们先降低下难度,实现TwoSum,因为TwoSum可以作为ThreeSum的基础泛型

TwoSum需要准备什么呢?

  • 递归元组,模拟for循环
  • 减法,递归过程中求出差值
  • 对每一项差值判断是否存在

完成TwoSum后如何实现ThreeSum?

  • 每一项和剩余元组走一遍 TwoSum泛型,筛选满足条件的
  • 为了保证每一项能够走TwoSum泛型,对于元组大到小排序

实现TwoSum

实现减法

因为元组下标是递增有序数列,我们在每次递归的时候返回一个长度+1的新元组并获取长度,就可以对非负整数依次点名了

如求A - B,我们假设A - B永远是非负整数数,无限递归产生新元祖的过程中,排查掉A和B相等后,必定是先点名到B,然后点名到A,而B 到 A的递归次数就是差值,也就是求得的结果

实现这个差值的计算

  • A作为被减数,R作为长度与减数相等的数组,Z则用于递归累增
  • 当被减数R长度等于A的过程中,Z则是被减数和减数的差值
type GetLen<A extends number, R extends number[], Z extends number[] = []> =
  A extends R['length'] ? Z['length'] : GetLen<A, [...R, 0], [...Z, 0]>;

减法如下:

  • 排除掉A和B相等的情况
  • 前提条件:A大于或者等于B
  • 用差值泛型求A 和 B的差
type Subtract<A extends number, B extends number, R extends number[] = []> =
  A extends B ? 0 :
  A extends R['length'] ? never :
  B extends R['length'] ? GetLen<A, R> :
  Subtract<A, B, [...R, 0]>;

元祖中是否包含差值

求出每一项的差值后,需要判断元组中是否存在,存在则满足 被减数和减数 都存在元祖,作为复合条件的一组返回

  • 从元祖第一项开始递归至末尾,则返回false
  • 若某一项的值满足寻找的值,返回ture,否则递归
type Includes<A extends number[], T extends number, L extends number[] = []> =
  A['length'] extends L['length'] ? false :
  A[L['length']] extends T ? true : Includes<A, T, [...L, 0]>;

递归元组

根据最开始的思路可以实现:

  • 依次递归元祖
  • 对每一项求差值
  • 判断差值是否存在于数组中
  • R是返回的结果,N是递归计数,Item是被减数,SubItem是减数
type TwoSum<
  T extends number,
  L extends number[],
  R extends number[][] = [],
  N extends number[] = [],
  Item extends number = L[N['length']],
  SubItem extends number = Subtract<T, Item>,
> = L['length'] extends N['length'] ?
  R : TwoSum<
    T,
    L,
    Includes<L, SubItem> extends true ? [
      ...R,
      [Item, SubItem]
    ] : R,
    [...N, 0]
  >;

type t1 = TwoSum<4, [1, 2, 3]>;
// [[1, 3], [2, 2], [3, 1]]

存在缺陷:

  • 如果被减数和减数值相同,且只存在一个,那结果也是满足的。如:4 和 [1, 2, 3],我们要的是 [1, 3],需要排除掉 [2, 2]
  • 递归到被减数和减数都会满足条件,会存在重复的两个结果。如:4 和 [1, 2, 3],我们要的是 [1, 3],需要排除掉 [3, 1]

出现这两个问题,是因为递归过的被减数仍然保留在元祖中,所以我们需要把递归过的被减数移除掉

优化一下:

  • 每次递归后移除当前项
type GetNext<T extends number[]> = T extends [number, ...infer U] ? U : [];

type TwoSum<
  T extends number,
  L extends number[],
  R extends number[][] = [],
  Item extends number = L[0],
  SubItem extends number = Subtract<T, Item>,
  NextL extends number[] = GetNext<L>,
> = L['length'] extends 0 ?
  R : TwoSum<
    T,
    NextL,
    Includes<NextL, SubItem> extends true ? [
      ...R,
      [Item, SubItem]
    ] : R
  >;

测试

type t1 = TwoSum<7, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
// [[0, 7], [1, 6], [2, 5], [3, 4]]

type t2 = TwoSum<12, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
// [[3, 9], [4, 8], [5, 7]]

type t3 = TwoSum<20, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>;
// []

type t4 = TwoSum<10, [0, 8, 2, 1, 4, 7, 6, 3, 4, 9]>;
// [[8, 2], [1, 9], [4, 6], [7, 3], [6, 4]]

实现ThreeSum

实现排序

之前已经实现typescript的快排,移步:用typescript类型来实现快排

为什么需要实现排序,因为上文中 TwoSum泛型的实现,需要满足

  • 输入参数 - 被减数 = 减数。所以 输入参数 > 被减数 、 输入参数 > 减数
  • 从头选取参数、被减数、减数

所以排序后可以直接使用TwoSum泛型

实现ThreeSum

  • 递归元祖
  • 依次选择 TwoSum的参数,剩余元组
  • 剩余元组中挑选符合条件的被减数、减数并返回
  • R为返回结果,NextL为剩余元组,NewList为合并TwoSum的结果
// 合并参数到TwoSum的结果,因为TwoSum返回的二元数组
type GetNewList<
  A extends number,
  T extends number[][],
  N extends number[] = [],
  R extends number[][] = []
> = T['length'] extends N['length'] ? R :
  GetNewList<A, T, [...N, 0], [...R, [A, ...T[N['length']]]]>;

type IsArray<T> = T extends number[] ? T : [];

type IsArray2<T> = T extends number[][] ? T : [];

type ThreeSumLoop<
  L extends number[],
  R extends number[][] = [],
  NextL extends number[] = GetNext<L>,
  NewList extends number[][] = IsArray2<TwoSum<L[0], NextL>>
> = L['length'] extends 0 | 1 ? R :
  ThreeSumLoop<NextL, NewList['length'] extends 0 ? R :
  IsArray2<[...R, ...GetNewList<L[0], NewList>]>>;

type ThreeSum<L extends number[]> = ThreeSumLoop<IsArray<QuickSort<L>>>;

测试

type l1 = ThreeSum<[1, 3, 2, 4]>;
// [[4, 3, 1], [3, 2, 1]]

type l2 = ThreeSum<[1, 6, 3, 7, 5, 4, 2]>;
// [[7, 6, 1], [7, 5, 2], [7, 4, 3], [6, 5, 1], [6, 4, 2], [5, 4, 1], [5, 3, 2], [4, 3, 1], [3, 2, 1]]

type l3 = ThreeSum<[0, 5, 15, 10, 5, 25, 20]>;
// [[25, 20, 5], [25, 15, 10], [20, 15, 5], [15, 10, 5], [10, 5, 5], [5, 5, 0]]

type l4 = ThreeSum<[1, 16, 3, 17, 5, 4, 21]>;
// [[21, 17, 4], [21, 16, 5], [17, 16, 1], [5, 4, 1], [4, 3, 1]]

到此这篇关于使用typescript类型实现ThreeSum的文章就介绍到这了,更多相关typescript ThreeSum内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解TypeScript使用及类型声明文件

    目录 简介 Script 与 Vue3 defineProps 与 Typescript defineEmits 与 Typescript ref 与 Typescript computed 与 Typescript 事件对象 与 Typescript 模板 Ref 与 Typescript 可选链操作符 非空断言-TS TypeScript类型声明文件 基本介绍 内置类型声明文件 第三方库类型声明文件 自定义类型声明文件 简介 声明文件是以.d.ts为后缀的文件,开发者在声明文件中编写类型声明

  • 使用TypeScript实现一个类型安全的EventBus示例详解

    目录 前言 准备工作 目标 思路 具体实现 全部代码 后记 前言 随着vue3的发布,TypeScript在国内越来越流行,学习TypeScript也随即变成了大势所趋.本文就通过实现一个类型安全的EventBus来练习TypeScript,希望对小伙伴们有所帮助. 准备工作 生成一个TypeScript的基础架子: // 创建目录 mkdir ts-event-bus && cd ts-event-bus // 初始化工程 yarn init -y // 安装typescript yar

  • typescript返回值类型和参数类型的具体使用

    目录 返回值类型 可缺省和可推断的返回值类型 Generator 函数的返回值 参数类型 可选参数和默认参数 剩余参数 返回值类型 在 JavaScript 中,我们知道一个函数可以没有显式 return,此时函数的返回值应该是 undefined: function fn() { // TODO } console.log(fn()); // => undefined 需要注意的是,在 TypeScript 中,如果我们显式声明函数的返回值类型为 undfined,将会得到如下所示的错误提醒.

  • TypeScript中的类型断言[as语法|<>语法]的使用

    Typescript中类型断言官方解释 要理解好类型断言,其实就深刻理解一句话:你会比TypeScript更了解某个值的详细信息 . 类型断言,断言 断言,顾名思义,我断定怎么怎么样,代入这句话里就是,我断定这个类型是什么.当然这是我们主观上的思维逻辑,程序并不认可,所以我们需要告诉程序:“相信我,我知道自己在干什么” . 这么干说,大家可能还是理解的不够透彻,我用两个函数举一个例子: /** * @param d 日期 * @param f 想要格式化的字符串 */ function date

  • 使用typescript类型实现ThreeSum

    目录 前言 思路整理 实现TwoSum 实现减法 元祖中是否包含差值 递归元组 测试 实现ThreeSum 实现排序 实现ThreeSum 测试 前言 本文执行环境typescript,版本4.7.4 不使用typescript的计算能力,通过类型来实现ThreeSum 思路整理 实现ThreeSum之前我们先降低下难度,实现TwoSum,因为TwoSum可以作为ThreeSum的基础泛型 TwoSum需要准备什么呢? 递归元组,模拟for循环 减法,递归过程中求出差值 对每一项差值判断是否存在

  • TypeScript类型声明书写详解

    本文总结一下TypeScript类型声明的书写,很多时候写TypeScript不是问题,写类型就特别纠结,我总结下,我在使用TypeScript中遇到的问题.如果你遇到类型声明不会写的时候,多看看lodash的声明,因为lodash对数据进行各种变形操作,所以你能遇到的,都有参考示例. 基本类型 // 变量 const num: number = 1; const str: string = 'str'; const bool: boolean = true; const nulls: null

  • 为react组件库添加typescript类型提示的方法

    以我自己的组件react-better-countdown为例, 首先在package.json里面添加types: types/index.d.ts, , 然后文件目录上添加对应文件夹和文件, 最后是index.d.ts文件的编写,具体看代码: import * as React from 'react'; interface CountdownProps { count?: number; dayText?: string | React.ReactElement; hourText?: s

  • 教你用typescript类型来推算斐波那契

    目录 写在前面 斐波那契 实现逻辑 第一个问题:第0和第1个数返回自身 第二个问题:某个数等于前两个数相加 第三个问题:推算一个数需要循环或者递归得到前两个值 第四个问题 结论 解决todo +1操作 数字转array 非负数判断 实现斐波那契 总结 写在前面 本文执行环境typescript,版本4.5.4 斐波那契 虽然大家都熟悉斐波那契了,还是简单的说说吧,一个知名的数学数列,地推方式如下 Fib(0) = 0 Fib(1) = 1 Fib(n) = Fib(n-1) + Fib(n-2)

  • TypeScript类型检查详谈及火爆原因

    目录 让我们先思考一个问题:类型是什么? 动态类型检查 静态类型检查 总结 TypeScript 这些年越来越火,可以说是前端工程师的必备技能了,各大框架都基于它实现. 那么,TypeScript 的出现和爆火是偶然发生的吗?其实不是,类似 TypeScript 这种静态类型语言成为主流是必然会发生的.为什么这么说呢? 让我们先思考一个问题:类型是什么? 类型具体点来说就是指 number.boolean.string 等基础类型和 Object.Function 等复合类型,它们是编程语言提供

  • 使用typescript类型来实现快排详情

    目录 前言 元组快排 实现逻辑 实现数字的大小比较 实现 A 是否 小于或等于 B 实现 A 是否 大于或等于 B 实现Filter 优化Filter 重构数字的大小值比较 重构Filter 实现快排 测试快排 优化:负数 负数的判断 字符串转数字 获取负数的值 完善获取绝对值 重构数字的大小比较 重构快排 测试快排V2 前言 本文执行环境typescript,版本4.7.4 不使用typescript的计算能力,通过类型来实现快排 元组快排 能否将元组 [3, 1, 2, 4] 通过泛型转换成

  • TypeScript 类型编程之索引类型递归去掉可选修饰

    目录 前言 总结 前言 这两天东东遇到一个 TS 的问题,跑来问我. 问题是这样的: 这样一个 interface,想取出 userInfo 的类型来: interface Result{ data?: { userInfo?: { name: string; } } } 他是这样取的: type userInfo = Result['data']['userInfo']; 但是会报错: 说是 userInfo 不在这个联合类型上. 这很正常,因为可选索引的含义就是值和 undefined 的联

  • 详解Anyscript开发指南绕过typescript类型检查

    目录 前言 场景设定 解决方法 注释忽略 场景用例 类型断言 场景用例 泛型转换 场景用例 总结 前言 随着越来越多的前端项目采用 typescript 来开发,越来越多前端开发者会接触.使用这门语言.它是前端项目工程化的一个重要帮手,结合 vscode 编辑器,给予了前端开发者更严谨.高效的编码体验.但同时,严格的类型检查也会使部分开发者的编码效率有所降低,将时间花费在解决类型冲突.类型不匹配上,从而导致望而却步,迟迟不敢上手. 本文描述了几种绕过 typescript 类型检查的方法,帮助t

  • Zod进行TypeScript类型验证使用详解

    目录 引言 什么是类型验证,为什么需要它? 为什么要使用zod? 使用 Zod 进行类型验证的示例 Primitives 对象 类型推断 组合类型 注意事项 安全解析 无法识别的Key被删除 其他事项 Zod 与其他库的比较 结论 引言 这篇文章将描述如何使用Zod为您的项目设置类型验证.Zod 是一个用于类型声明和验证的开源 TypeScript 库.我们将研究为什么使用 Zod 进行类型验证,提供如何使用它的示例,并将其与其他库进行比较. 什么是类型验证,为什么需要它? 类型验证是验证数据结

  • TypeScript类型断言VS类型守卫示例详解

    目录 类型断言 类型守卫 使用 in 关键字 使用 instanceof 关键字 使用 typeof 关键字 自定义类型守卫 总结 类型断言 类型断言有两种写法,分别为value as Type和<Type>value,它让 TypeScript 编译器将 value 当作 Type 类型.类型断言是一个编译时特性,不进行类型转换,因此不会影响变量在运行时的数据类型.如果某变量是 any 类型,但现在你知道它确切的数据类型,使用类型断言能让 IDE 有代码提示的能力,也能让 TypeScrip

随机推荐