使用typescript類型實現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內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 教你用typescript類型來推算斐波那契
- TypeScript中條件類型精讀與實踐記錄
- TypeScript中extends的正確打開方式詳解
- TypeScript 的條件類型使用示例詳解
- 如何通俗的解釋TypeScript 泛型