使用typescript類型實現(xiàn)ThreeSum
前言
本文執(zhí)行環(huán)境typescript,版本4.7.4
不使用typescript的計算能力,通過類型來實現(xiàn)ThreeSum
思路整理
實現(xiàn)ThreeSum之前我們先降低下難度,實現(xiàn)TwoSum,因為TwoSum可以作為ThreeSum的基礎(chǔ)泛型
TwoSum需要準備什么呢?
- 遞歸元組,模擬for循環(huán)
- 減法,遞歸過程中求出差值
- 對每一項差值判斷是否存在
完成TwoSum后如何實現(xiàn)ThreeSum?
- 每一項和剩余元組走一遍 TwoSum泛型,篩選滿足條件的
- 為了保證每一項能夠走TwoSum泛型,對于元組大到小排序
實現(xiàn)TwoSum
實現(xiàn)減法
因為元組下標(biāo)是遞增有序數(shù)列,我們在每次遞歸的時候返回一個長度+1的新元組并獲取長度,就可以對非負整數(shù)依次點名了
如求A - B,我們假設(shè)A - B永遠是非負整數(shù)數(shù),無限遞歸產(chǎn)生新元祖的過程中,排查掉A和B相等后,必定是先點名到B,然后點名到A,而B 到 A的遞歸次數(shù)就是差值,也就是求得的結(jié)果
實現(xiàn)這個差值的計算
- A作為被減數(shù),R作為長度與減數(shù)相等的數(shù)組,Z則用于遞歸累增
- 當(dāng)被減數(shù)R長度等于A的過程中,Z則是被減數(shù)和減數(shù)的差值
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]>;
元祖中是否包含差值
求出每一項的差值后,需要判斷元組中是否存在,存在則滿足 被減數(shù)和減數(shù) 都存在元祖,作為復(fù)合條件的一組返回
- 從元祖第一項開始遞歸至末尾,則返回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]>;
遞歸元組
根據(jù)最開始的思路可以實現(xiàn):
- 依次遞歸元祖
- 對每一項求差值
- 判斷差值是否存在于數(shù)組中
- R是返回的結(jié)果,N是遞歸計數(shù),Item是被減數(shù),SubItem是減數(shù)
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]]
存在缺陷:
- 如果被減數(shù)和減數(shù)值相同,且只存在一個,那結(jié)果也是滿足的。如:4 和 [1, 2, 3],我們要的是 [1, 3],需要排除掉 [2, 2]
- 遞歸到被減數(shù)和減數(shù)都會滿足條件,會存在重復(fù)的兩個結(jié)果。如:4 和 [1, 2, 3],我們要的是 [1, 3],需要排除掉 [3, 1]
出現(xiàn)這兩個問題,是因為遞歸過的被減數(shù)仍然保留在元祖中,所以我們需要把遞歸過的被減數(shù)移除掉
優(yōu)化一下:
- 每次遞歸后移除當(dāng)前項
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]]
實現(xiàn)ThreeSum
實現(xiàn)排序
之前已經(jīng)實現(xiàn)typescript的快排,移步:用typescript類型來實現(xiàn)快排
為什么需要實現(xiàn)排序,因為上文中 TwoSum泛型的實現(xiàn),需要滿足
- 輸入?yún)?shù) - 被減數(shù) = 減數(shù)。所以 輸入?yún)?shù) > 被減數(shù) 、 輸入?yún)?shù) > 減數(shù)
- 從頭選取參數(shù)、被減數(shù)、減數(shù)
所以排序后可以直接使用TwoSum泛型
實現(xiàn)ThreeSum
- 遞歸元祖
- 依次選擇 TwoSum的參數(shù),剩余元組
- 剩余元組中挑選符合條件的被減數(shù)、減數(shù)并返回
- R為返回結(jié)果,NextL為剩余元組,NewList為合并TwoSum的結(jié)果
// 合并參數(shù)到TwoSum的結(jié)果,因為TwoSum返回的二元數(shù)組 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]]
到此這篇關(guān)于使用typescript類型實現(xiàn)ThreeSum的文章就介紹到這了,更多相關(guān)typescript ThreeSum內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript與CSS復(fù)習(xí)(《精通javascript》)
js和css結(jié)合來產(chǎn)生醒目的交互效果,我們可以快速的訪問元素自身的樣式屬性2010-06-06