From ecf4d6ad11766abcaa61ee5beecd37176a138ed0 Mon Sep 17 00:00:00 2001 From: Revone Date: Wed, 3 Jan 2024 15:52:10 +0800 Subject: [PATCH] fix: Fix bug #71. feat: Define abstract methods through a base class to add clone and isEmpty methods to all data structures. Add delete and deleteAt methods to Deque, Queue, and Stack data structures. refactor: Optimize the type definition N to NODE for alignment with the design of TREE. --- README.md | 26 +- src/data-structures/base/iterable-base.ts | 10 +- src/data-structures/binary-tree/avl-tree.ts | 85 +-- .../binary-tree/binary-tree.ts | 553 +++++++++--------- src/data-structures/binary-tree/bst.ts | 162 ++--- src/data-structures/binary-tree/rb-tree.ts | 132 ++--- .../binary-tree/tree-multimap.ts | 73 +-- src/data-structures/graph/abstract-graph.ts | 4 + src/data-structures/graph/directed-graph.ts | 30 + src/data-structures/graph/map-graph.ts | 15 + src/data-structures/graph/undirected-graph.ts | 28 + src/data-structures/hash/hash-map.ts | 35 +- src/data-structures/queue/queue.ts | 20 + src/data-structures/stack/stack.ts | 20 + src/data-structures/trie/trie.ts | 10 +- .../binary-tree/binary-tree.ts | 2 +- src/types/utils/utils.ts | 2 + .../binary-tree/avl-tree.test.ts | 37 ++ .../binary-tree/binary-tree.test.ts | 63 +- .../data-structures/binary-tree/bst.test.ts | 66 ++- .../binary-tree/rb-tree.test.ts | 39 +- .../binary-tree/tree-multimap.test.ts | 69 ++- .../graph/abstract-graph.test.ts | 8 + .../data-structures/hash/hash-map.test.ts | 23 + test/unit/data-structures/heap/heap.test.ts | 19 +- .../linked-list/doubly-linked-list.test.ts | 17 + .../linked-list/singly-linked-list.test.ts | 17 + .../linked-list/skip-list.test.ts | 2 +- test/unit/data-structures/queue/deque.test.ts | 17 + test/unit/data-structures/queue/queue.test.ts | 17 + test/unit/data-structures/stack/stack.test.ts | 17 + test/unit/data-structures/trie/trie.test.ts | 17 + 32 files changed, 1069 insertions(+), 566 deletions(-) diff --git a/README.md b/README.md index a17da64..c6cbf31 100644 --- a/README.md +++ b/README.md @@ -939,43 +939,43 @@ We strictly adhere to computer science theory and software development standards [//]: # (No deletion!!! Start of Replace Section)
avl-tree
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly132.617.540.03
10,000 add & delete randomly179.825.560.00
10,000 addMany128.847.767.04e-4
10,000 get48.4020.663.34e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 add randomly120.958.270.00
10,000 add & delete randomly180.365.540.00
10,000 addMany137.917.250.02
10,000 get49.5220.190.00
binary-tree-overall
-
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add5.84171.128.80e-5
10,000 RBTree add & delete randomly16.3061.340.01
10,000 RBTree get19.8050.500.00
10,000 AVLTree add122.948.130.00
10,000 AVLTree add & delete randomly185.435.390.00
10,000 AVLTree get0.961044.696.87e-6
+
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add6.17162.128.39e-5
10,000 RBTree add & delete randomly16.2461.597.03e-4
10,000 RBTree get21.7246.050.01
10,000 AVLTree add126.307.920.01
10,000 AVLTree add & delete randomly187.045.350.00
10,000 AVLTree get0.911095.031.07e-5
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add79.3912.600.00
100,000 add & delete randomly211.764.720.00
100,000 getNode169.345.916.62e-4
100,000 add & iterator112.028.930.01
+
test nametime taken (ms)executions per secsample deviation
100,000 add89.0811.230.01
100,000 add & delete randomly232.784.300.01
100,000 getNode190.645.250.00
100,000 add & iterator135.297.390.05
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109590.361.32e-6
1,000 addEdge6.19161.684.32e-4
1,000 getVertex0.052.16e+43.75e-7
1,000 getEdge24.7240.450.01
tarjan226.084.420.01
tarjan all6667.550.150.27
topologicalSort186.595.360.00
+
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.119163.909.52e-6
1,000 addEdge6.11163.621.43e-4
1,000 getVertex0.052.14e+45.12e-7
1,000 getEdge24.8440.260.00
tarjan236.104.240.02
tarjan all6707.730.150.11
topologicalSort192.625.190.01
hash-map
-
test nametime taken (ms)executions per secsample deviation
1,000,000 set137.007.300.08
Native Map 1,000,000 set236.584.230.05
Native Set 1,000,000 add187.785.330.05
1,000,000 set & get123.918.070.04
Native Map 1,000,000 set & get286.033.500.03
Native Set 1,000,000 add & has188.675.300.03
1,000,000 ObjKey set & get327.703.050.05
Native Map 1,000,000 ObjKey set & get285.223.510.05
Native Set 1,000,000 ObjKey add & has278.083.600.07
+
test nametime taken (ms)executions per secsample deviation
1,000,000 set123.328.110.05
Native Map 1,000,000 set232.464.300.03
Native Set 1,000,000 add190.545.250.05
1,000,000 set & get118.718.420.03
Native Map 1,000,000 set & get294.153.400.04
Native Set 1,000,000 add & has182.795.470.02
1,000,000 ObjKey set & get331.703.010.06
Native Map 1,000,000 ObjKey set & get282.443.540.06
Native Set 1,000,000 ObjKey add & has290.193.450.07
heap
-
test nametime taken (ms)executions per secsample deviation
100,000 add & poll23.9941.680.00
100,000 add & dfs33.2330.090.00
10,000 fib add & pop358.162.790.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add & poll24.3641.060.00
100,000 add & dfs32.1031.153.40e-4
10,000 fib add & pop354.322.820.00
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push229.074.370.06
1,000,000 unshift217.644.590.08
1,000,000 unshift & shift175.135.710.04
1,000,000 addBefore342.222.920.08
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push215.494.640.02
1,000,000 unshift229.174.360.05
1,000,000 unshift & shift182.415.480.04
1,000,000 addBefore313.243.190.05
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift210.654.750.06
10,000 push & pop214.544.660.01
10,000 addBefore248.454.020.01
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift209.034.780.05
10,000 push & pop217.384.600.01
10,000 addBefore245.424.070.01
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add & poll75.6713.220.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add & poll75.4813.250.00
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push13.1476.131.36e-4
10,000 push & delete4716.790.210.13
1,000,000 push & pop22.3844.680.00
100,000 push & shift2.15464.201.98e-5
Native Array 100,000 push & shift2241.300.450.14
100,000 unshift & shift2.34426.690.00
Native Array 100,000 unshift & shift3971.320.250.18
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push13.6973.070.00
10,000 push & delete4928.030.200.41
1,000,000 push & pop25.1639.740.01
100,000 push & shift3.12320.280.00
Native Array 100,000 push & shift2216.180.450.08
100,000 unshift & shift2.17461.584.01e-4
Native Array 100,000 unshift & shift4474.580.220.29
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push44.8022.320.01
100,000 push & shift4.91203.641.15e-4
Native Array 100,000 push & shift2116.780.470.12
Native Array 100,000 push & pop4.30232.299.32e-5
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push49.4220.230.02
100,000 push & shift5.21191.829.75e-4
Native Array 100,000 push & shift2328.850.430.25
Native Array 100,000 push & pop4.44225.241.83e-4
stack
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push42.1523.720.00
1,000,000 push & pop52.9018.900.02
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.5421.960.01
1,000,000 push & pop50.5919.770.01
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push44.5522.458.46e-4
100,000 getWords87.4811.430.00
+
test nametime taken (ms)executions per secsample deviation
100,000 push47.7420.950.00
100,000 getWords94.4010.590.01
[//]: # (No deletion!!! End of Replace Section) diff --git a/src/data-structures/base/iterable-base.ts b/src/data-structures/base/iterable-base.ts index 968438a..35d6083 100644 --- a/src/data-structures/base/iterable-base.ts +++ b/src/data-structures/base/iterable-base.ts @@ -287,10 +287,14 @@ export abstract class IterableEntryBase { console.log([...this]); } + abstract isEmpty(): boolean; + + abstract clone(): any; + protected abstract _getIterator(...args: any[]): IterableIterator<[K, V]>; } -export abstract class IterableElementBase { +export abstract class IterableElementBase { /** * Time Complexity: O(n) * Space Complexity: O(1) @@ -491,5 +495,9 @@ export abstract class IterableElementBase { console.log([...this]); } + abstract isEmpty(): boolean; + + abstract clone(): C; + protected abstract _getIterator(...args: any[]): IterableIterator; } diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 026b2ae..ef95641 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -17,11 +17,11 @@ import type { } from '../../types'; import { IBinaryTree } from '../../interfaces'; -export class AVLTreeNode = AVLTreeNodeNested> extends BSTNode< - K, - V, - N -> { +export class AVLTreeNode< + K = any, + V = any, + NODE extends AVLTreeNode = AVLTreeNodeNested +> extends BSTNode { height: number; constructor(key: K, value?: V) { @@ -42,21 +42,21 @@ export class AVLTreeNode = AVLT export class AVLTree< K = any, V = any, - N extends AVLTreeNode = AVLTreeNode>, - TREE extends AVLTree = AVLTree> + NODE extends AVLTreeNode = AVLTreeNode>, + TREE extends AVLTree = AVLTree> > - extends BST - implements IBinaryTree { + extends BST + implements IBinaryTree { /** * The constructor function initializes an AVLTree object with optional keysOrNodesOrEntries and options. - * @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry` + * @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry` * objects. It represents a collection of nodes that will be added to the AVL tree during * initialization. * @param [options] - The `options` parameter is an optional object that allows you to customize the * behavior of the AVL tree. It is of type `Partial`, which means that you can * provide only a subset of the properties defined in the `AVLTreeOptions` interface. */ - constructor(keysOrNodesOrEntries: Iterable> = [], options?: AVLTreeOptions) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: AVLTreeOptions) { super([], options); if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries); } @@ -67,11 +67,11 @@ export class AVLTree< * the new node. It is used to determine the position of the node in the binary search tree. * @param [value] - The parameter `value` is an optional value that can be assigned to the node. It is of * type `V`, which means it can be any value that is assignable to the `value` property of the - * node type `N`. + * node type `NODE`. * @returns a new AVLTreeNode object with the specified key and value. */ - override createNode(key: K, value?: V): N { - return new AVLTreeNode(key, value) as N; + override createNode(key: K, value?: V): NODE { + return new AVLTreeNode(key, value) as NODE; } /** @@ -82,7 +82,7 @@ export class AVLTree< * @returns a new AVLTree object. */ override createTree(options?: AVLTreeOptions): TREE { - return new AVLTree([], { + return new AVLTree([], { iterationType: this.iterationType, variant: this.variant, ...options @@ -91,10 +91,10 @@ export class AVLTree< /** * The function checks if an keyOrNodeOrEntry is an instance of AVLTreeNode. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the AVLTreeNode class. */ - override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is N { + override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { return keyOrNodeOrEntry instanceof AVLTreeNode; } @@ -116,7 +116,7 @@ export class AVLTree< * being added to the binary tree. * @returns The method is returning either the inserted node or undefined. */ - override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { + override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { if (keyOrNodeOrEntry === null) return false; const inserted = super.add(keyOrNodeOrEntry, value); if (inserted) this._balancePath(keyOrNodeOrEntry); @@ -140,13 +140,13 @@ export class AVLTree< * @param {C} callback - The `callback` parameter is a function that will be called for each node * that is deleted from the binary tree. It is an optional parameter and if not provided, it will * default to the `_defaultOneParamCallback` function. The `callback` function should have a single - * parameter of type `N - * @returns The method is returning an array of `BinaryTreeDeleteResult`. + * parameter of type `NODE + * @returns The method is returning an array of `BinaryTreeDeleteResult`. */ - override delete>( + override delete>( identifier: ReturnType, callback: C = this._defaultOneParamCallback as C - ): BinaryTreeDeleteResult[] { + ): BinaryTreeDeleteResult[] { if ((identifier as any) instanceof AVLTreeNode) callback = (node => node) as C; const deletedResults = super.delete(identifier, callback); for (const { needBalanced } of deletedResults) { @@ -160,14 +160,17 @@ export class AVLTree< /** * The `_swapProperties` function swaps the key, value, and height properties between two nodes in a binary * tree. - * @param {K | N | undefined} srcNode - The `srcNode` parameter represents the source node that - * needs to be swapped with the destination node. It can be of type `K`, `N`, or `undefined`. - * @param {K | N | undefined} destNode - The `destNode` parameter represents the destination + * @param {K | NODE | undefined} srcNode - The `srcNode` parameter represents the source node that + * needs to be swapped with the destination node. It can be of type `K`, `NODE`, or `undefined`. + * @param {K | NODE | undefined} destNode - The `destNode` parameter represents the destination * node where the values from the source node will be swapped to. * @returns either the `destNode` object if both `srcNode` and `destNode` are defined, or `undefined` * if either `srcNode` or `destNode` is undefined. */ - protected override _swapProperties(srcNode: BSTNKeyOrNode, destNode: BSTNKeyOrNode): N | undefined { + protected override _swapProperties( + srcNode: BSTNKeyOrNode, + destNode: BSTNKeyOrNode + ): NODE | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -203,11 +206,11 @@ export class AVLTree< * Space Complexity: O(1) * * The function calculates the balance factor of a node in a binary tree. - * @param {N} node - The parameter "node" represents a node in a binary tree data structure. + * @param {NODE} node - The parameter "node" represents a node in a binary tree data structure. * @returns the balance factor of a given node. The balance factor is calculated by subtracting the * height of the left subtree from the height of the right subtree. */ - protected _balanceFactor(node: N): number { + protected _balanceFactor(node: NODE): number { if (!node.right) // node has no right subtree return -node.height; @@ -229,9 +232,9 @@ export class AVLTree< * * The function updates the height of a node in a binary tree based on the heights of its left and * right children. - * @param {N} node - The parameter "node" represents a node in a binary tree data structure. + * @param {NODE} node - The parameter "node" represents a node in a binary tree data structure. */ - protected _updateHeight(node: N): void { + protected _updateHeight(node: NODE): void { if (!node.left && !node.right) node.height = 0; else if (!node.left) { const rightHeight = node.right ? node.right.height : 0; @@ -252,10 +255,10 @@ export class AVLTree< * * The `_balancePath` function is used to update the heights of nodes and perform rotation operations * to restore balance in an AVL tree after inserting a node. - * @param {N} node - The `node` parameter in the `_balancePath` function represents the node in the + * @param {NODE} node - The `node` parameter in the `_balancePath` function represents the node in the * AVL tree that needs to be balanced. */ - protected _balancePath(node: KeyOrNodeOrEntry): void { + protected _balancePath(node: KeyOrNodeOrEntry): void { node = this.ensureNode(node); const path = this.getPathToRoot(node, false); // first O(log n) + O(log n) for (let i = 0; i < path.length; i++) { @@ -306,9 +309,9 @@ export class AVLTree< * Space Complexity: O(1) * * The function `_balanceLL` performs a left-left rotation to balance a binary tree. - * @param {N} A - A is a node in a binary tree. + * @param {NODE} A - A is a node in a binary tree. */ - protected _balanceLL(A: N): void { + protected _balanceLL(A: NODE): void { const parentOfA = A.parent; const B = A.left; A.parent = B; @@ -344,9 +347,9 @@ export class AVLTree< * Space Complexity: O(1) * * The `_balanceLR` function performs a left-right rotation to balance a binary tree. - * @param {N} A - A is a node in a binary tree. + * @param {NODE} A - A is a node in a binary tree. */ - protected _balanceLR(A: N): void { + protected _balanceLR(A: NODE): void { const parentOfA = A.parent; const B = A.left; let C = undefined; @@ -400,9 +403,9 @@ export class AVLTree< * Space Complexity: O(1) * * The function `_balanceRR` performs a right-right rotation to balance a binary tree. - * @param {N} A - A is a node in a binary tree. + * @param {NODE} A - A is a node in a binary tree. */ - protected _balanceRR(A: N): void { + protected _balanceRR(A: NODE): void { const parentOfA = A.parent; const B = A.right; A.parent = B; @@ -443,9 +446,9 @@ export class AVLTree< * Space Complexity: O(1) * * The function `_balanceRL` performs a right-left rotation to balance a binary tree. - * @param {N} A - A is a node in a binary tree. + * @param {NODE} A - A is a node in a binary tree. */ - protected _balanceRL(A: N): void { + protected _balanceRL(A: NODE): void { const parentOfA = A.parent; const B = A.right; let C = undefined; @@ -488,7 +491,7 @@ export class AVLTree< C && this._updateHeight(C); } - protected _replaceNode(oldNode: N, newNode: N): N { + protected _replaceNode(oldNode: NODE, newNode: NODE): NODE { newNode.height = oldNode.height; return super._replaceNode(oldNode, newNode); diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 72de2cd..852f1d8 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -28,46 +28,46 @@ import { IterableEntryBase } from '../base'; /** * Represents a node in a binary tree. * @template V - The type of data stored in the node. - * @template N - The type of the family relationship in the binary tree. + * @template NODE - The type of the family relationship in the binary tree. */ export class BinaryTreeNode< K = any, V = any, - N extends BinaryTreeNode = BinaryTreeNode> + NODE extends BinaryTreeNode = BinaryTreeNode> > { key: K; value?: V; - parent?: N; + parent?: NODE; constructor(key: K, value?: V) { this.key = key; this.value = value; } - protected _left?: N | null; + protected _left?: NODE | null; - get left(): N | null | undefined { + get left(): NODE | null | undefined { return this._left; } - set left(v: N | null | undefined) { + set left(v: NODE | null | undefined) { if (v) { - v.parent = this as unknown as N; + v.parent = this as unknown as NODE; } this._left = v; } - protected _right?: N | null; + protected _right?: NODE | null; - get right(): N | null | undefined { + get right(): NODE | null | undefined { return this._right; } - set right(v: N | null | undefined) { + set right(v: NODE | null | undefined) { if (v) { - v.parent = this as unknown as N; + v.parent = this as unknown as NODE; } this._right = v; } @@ -77,7 +77,7 @@ export class BinaryTreeNode< * @returns {FamilyPosition} - The family position of the node. */ get familyPosition(): FamilyPosition { - const that = this as unknown as N; + const that = this as unknown as NODE; if (!this.parent) { return this.left || this.right ? FamilyPosition.ROOT : FamilyPosition.ISOLATED; } @@ -103,11 +103,11 @@ export class BinaryTreeNode< export class BinaryTree< K = any, V = any, - N extends BinaryTreeNode = BinaryTreeNode>, - TREE extends BinaryTree = BinaryTree> + NODE extends BinaryTreeNode = BinaryTreeNode>, + TREE extends BinaryTree = BinaryTree> > extends IterableEntryBase - implements IBinaryTree { + implements IBinaryTree { iterationType = IterationType.ITERATIVE; /** @@ -119,7 +119,7 @@ export class BinaryTree< * `Partial`, which means that not all properties of `BinaryTreeOptions` are * required. */ - constructor(keysOrNodesOrEntries: Iterable> = [], options?: BinaryTreeOptions) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: BinaryTreeOptions) { super(); if (options) { const { iterationType, extractor } = options; @@ -138,9 +138,9 @@ export class BinaryTree< return this._extractor; } - protected _root?: N | null; + protected _root?: NODE | null; - get root(): N | null | undefined { + get root(): NODE | null | undefined { return this._root; } @@ -154,10 +154,10 @@ export class BinaryTree< * Creates a new instance of BinaryTreeNode with the given key and value. * @param {K} key - The key for the new node. * @param {V} value - The value for the new node. - * @returns {N} - The newly created BinaryTreeNode. + * @returns {NODE} - The newly created BinaryTreeNode. */ - createNode(key: K, value?: V): N { - return new BinaryTreeNode(key, value) as N; + createNode(key: K, value?: V): NODE { + return new BinaryTreeNode(key, value) as NODE; } /** @@ -168,21 +168,21 @@ export class BinaryTree< * @returns a new instance of a binary tree. */ createTree(options?: Partial>): TREE { - return new BinaryTree([], { iterationType: this.iterationType, ...options }) as TREE; + return new BinaryTree([], { iterationType: this.iterationType, ...options }) as TREE; } /** * The function `keyValueOrEntryToNode` converts an keyOrNodeOrEntry object into a node object. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. * @param {V} [value] - The `value` parameter is an optional value that can be passed to the * `keyValueOrEntryToNode` function. It represents the value associated with the keyOrNodeOrEntry node. If no value * is provided, it will be `undefined`. - * @returns a value of type N (node), or null, or undefined. + * @returns a value of type NODE (node), or null, or undefined. */ - keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): N | null | undefined { + keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): NODE | null | undefined { if (keyOrNodeOrEntry === undefined) return; - let node: N | null | undefined; + let node: NODE | null | undefined; if (keyOrNodeOrEntry === null) { node = null; } else if (this.isEntry(keyOrNodeOrEntry)) { @@ -215,7 +215,7 @@ export class BinaryTree< * * The function `ensureNode` returns the node corresponding to the given key if it is a valid node * key, otherwise it returns the key itself. - * @param {K | N | null | undefined} keyOrNodeOrEntry - The `key` parameter can be of type `K`, `N`, + * @param {K | NODE | null | undefined} keyOrNodeOrEntry - The `key` parameter can be of type `K`, `NODE`, * `null`, or `undefined`. It represents a key used to identify a node in a binary tree. * @param iterationType - The `iterationType` parameter is an optional parameter that specifies the * type of iteration to be used when searching for a node by key. It has a default value of @@ -224,10 +224,10 @@ export class BinaryTree< * itself if it is not a valid node key. */ ensureNode( - keyOrNodeOrEntry: KeyOrNodeOrEntry, + keyOrNodeOrEntry: KeyOrNodeOrEntry, iterationType = IterationType.ITERATIVE - ): N | null | undefined { - let res: N | null | undefined; + ): NODE | null | undefined { + let res: NODE | null | undefined; if (this.isRealNode(keyOrNodeOrEntry)) { res = keyOrNodeOrEntry; } else if (this.isEntry(keyOrNodeOrEntry)) { @@ -242,20 +242,20 @@ export class BinaryTree< /** * The function "isNode" checks if an keyOrNodeOrEntry is an instance of the BinaryTreeNode class. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is a variable of type `KeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the class N. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is a variable of type `KeyOrNodeOrEntry`. + * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the class NODE. */ - isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is N { + isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { return keyOrNodeOrEntry instanceof BinaryTreeNode; } /** * The function checks if a given value is an entry in a binary tree node. - * @param keyOrNodeOrEntry - KeyOrNodeOrEntry - A generic type representing a node in a binary tree. It has - * two type parameters V and N, representing the value and node type respectively. + * @param keyOrNodeOrEntry - KeyOrNodeOrEntry - A generic type representing a node in a binary tree. It has + * two type parameters V and NODE, representing the value and node type respectively. * @returns a boolean value. */ - isEntry(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is BTNEntry { + isEntry(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is BTNEntry { return Array.isArray(keyOrNodeOrEntry) && keyOrNodeOrEntry.length === 2; } @@ -270,7 +270,7 @@ export class BinaryTree< * @param {any} node - The parameter `node` is of type `any`, which means it can be any data type. * @returns a boolean value. */ - isRealNode(node: KeyOrNodeOrEntry): node is N { + isRealNode(node: KeyOrNodeOrEntry): node is NODE { return node instanceof BinaryTreeNode && String(node.key) !== 'NaN'; } @@ -279,7 +279,7 @@ export class BinaryTree< * @param {any} node - The parameter `node` is of type `any`, which means it can be any data type. * @returns a boolean value. */ - isNIL(node: KeyOrNodeOrEntry) { + isNIL(node: KeyOrNodeOrEntry) { return node instanceof BinaryTreeNode && String(node.key) === 'NaN'; } @@ -288,7 +288,7 @@ export class BinaryTree< * @param {any} node - The parameter `node` is of type `any`, which means it can be any data type. * @returns a boolean value. */ - isNodeOrNull(node: KeyOrNodeOrEntry): node is N | null { + isNodeOrNull(node: KeyOrNodeOrEntry): node is NODE | null { return this.isRealNode(node) || node === null; } @@ -305,9 +305,9 @@ export class BinaryTree< * existing node with the same key. * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be one of the following: * @param {V} [value] - The value to be inserted into the binary tree. - * @returns The function `add` returns either a node (`N`), `null`, or `undefined`. + * @returns The function `add` returns either a node (`NODE`), `null`, or `undefined`. */ - add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { + add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value); if (newNode === undefined) return false; @@ -318,8 +318,8 @@ export class BinaryTree< return true; } - const queue = new Queue([this.root]); - let potentialParent: N | undefined; // Record the parent node of the potential insertion location + const queue = new Queue([this.root]); + let potentialParent: NODE | undefined; // Record the parent node of the potential insertion location while (queue.size > 0) { const cur = queue.shift(); @@ -374,9 +374,9 @@ export class BinaryTree< * adds each node with its corresponding value to the data structure. * @param keysOrNodesOrEntries - An iterable collection of KeyOrNodeOrEntry objects. * @param [values] - An optional iterable of values that will be assigned to each node being added. - * @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values. + * @returns The function `addMany` returns an array of `NODE`, `null`, or `undefined` values. */ - addMany(keysOrNodesOrEntries: Iterable>, values?: Iterable): boolean[] { + addMany(keysOrNodesOrEntries: Iterable>, values?: Iterable): boolean[] { // TODO not sure addMany not be run multi times const inserted: boolean[] = []; @@ -413,31 +413,28 @@ export class BinaryTree< * * The `refill` function clears the current data and adds new key-value pairs to the data structure. * @param keysOrNodesOrEntries - An iterable containing keys, nodes, or entries. These can be of type - * KeyOrNodeOrEntry. + * KeyOrNodeOrEntry. * @param [values] - The `values` parameter is an optional iterable that contains the values to be * associated with the keys or nodes or entries in the `keysOrNodesOrEntries` parameter. If provided, * the values will be associated with the corresponding keys or nodes or entries in the * `keysOrNodesOrEntries` iterable */ - refill(keysOrNodesOrEntries: Iterable>, values?: Iterable): void { + refill(keysOrNodesOrEntries: Iterable>, values?: Iterable): void { this.clear(); this.addMany(keysOrNodesOrEntries, values); } - /** - * Time Complexity: O(k * n) - * Space Complexity: O(1) - * "n" is the number of nodes in the tree, and "k" is the number of keys to be inserted. - */ + delete>(identifier: K, callback?: C): BinaryTreeDeleteResult[]; - delete>(identifier: K, callback?: C): BinaryTreeDeleteResult[]; + delete>( + identifier: NODE | null | undefined, + callback?: C + ): BinaryTreeDeleteResult[]; - delete>(identifier: N | null | undefined, callback?: C): BinaryTreeDeleteResult[]; - - delete>(identifier: ReturnType, callback: C): BinaryTreeDeleteResult[]; + delete>(identifier: ReturnType, callback: C): BinaryTreeDeleteResult[]; /** - * Time Complexity: O(n) + * Time Complexity: O(log n) * Space Complexity: O(1) * * The function deletes a node from a binary tree and returns an array of the deleted nodes along @@ -449,13 +446,13 @@ export class BinaryTree< * @param {C} callback - The `callback` parameter is a function that is used to determine the * identifier of the node to be deleted. It is optional and has a default value of * `this._defaultOneParamCallback`. The `callback` function should return the identifier of the node. - * @returns an array of `BinaryTreeDeleteResult`. + * @returns an array of `BinaryTreeDeleteResult`. */ - delete>( + delete>( identifier: ReturnType | null | undefined, callback: C = this._defaultOneParamCallback as C - ): BinaryTreeDeleteResult[] { - const deletedResult: BinaryTreeDeleteResult[] = []; + ): BinaryTreeDeleteResult[] { + const deletedResult: BinaryTreeDeleteResult[] = []; if (!this.root) return deletedResult; if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; @@ -463,38 +460,37 @@ export class BinaryTree< const curr = this.getNode(identifier, callback); if (!curr) return deletedResult; - const parent: N | null | undefined = curr?.parent ? curr.parent : null; - let needBalanced: N | null | undefined = undefined; - let orgCurrent: N | undefined = curr; + const parent: NODE | undefined = curr?.parent; + let needBalanced: NODE | undefined; + let orgCurrent: NODE | undefined = curr; - if (!curr.left) { - if (!parent) { - // Handle the case when there's only one root node - this._setRoot(null); - } else { - const { familyPosition: fp } = curr; - if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) { - parent.left = curr.right; - } else if (fp === FamilyPosition.RIGHT || fp === FamilyPosition.ROOT_RIGHT) { - parent.right = curr.right; + if (!curr.left && !curr.right && !parent) { + this._setRoot(undefined); + } else if (curr.left) { + const leftSubTreeRightMost = this.getRightMost(curr.left); + if (leftSubTreeRightMost) { + const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; + orgCurrent = this._swapProperties(curr, leftSubTreeRightMost); + if (parentOfLeftSubTreeMax) { + if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) + parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; + else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left; + needBalanced = parentOfLeftSubTreeMax; } - needBalanced = parent; } + } else if (parent) { + const { familyPosition: fp } = curr; + if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) { + parent.left = curr.right; + } else if (fp === FamilyPosition.RIGHT || fp === FamilyPosition.ROOT_RIGHT) { + parent.right = curr.right; + } + needBalanced = parent; } else { - if (curr.left) { - const leftSubTreeRightMost = this.getRightMost(curr.left); - if (leftSubTreeRightMost) { - const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; - orgCurrent = this._swapProperties(curr, leftSubTreeRightMost); - if (parentOfLeftSubTreeMax) { - if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) - parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; - else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left; - needBalanced = parentOfLeftSubTreeMax; - } - } - } + this._setRoot(curr.right); + curr.right = undefined; } + this._size = this.size - 1; deletedResult.push({ deleted: orgCurrent, needBalanced }); @@ -511,15 +507,15 @@ export class BinaryTree< * Space Complexity: O(1) * * The function calculates the depth of a given node in a binary tree. - * @param {K | N | null | undefined} dist - The `dist` parameter represents the node in - * the binary tree whose depth we want to find. It can be of type `K`, `N`, `null`, or + * @param {K | NODE | null | undefined} dist - The `dist` parameter represents the node in + * the binary tree whose depth we want to find. It can be of type `K`, `NODE`, `null`, or * `undefined`. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting node + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting node * from which we want to calculate the depth. It can be either a `K` (binary tree node key) or - * `N` (binary tree node) or `null` or `undefined`. If no value is provided for `beginRoot + * `NODE` (binary tree node) or `null` or `undefined`. If no value is provided for `beginRoot * @returns the depth of the `dist` relative to the `beginRoot`. */ - getDepth(dist: KeyOrNodeOrEntry, beginRoot: KeyOrNodeOrEntry = this.root): number { + getDepth(dist: KeyOrNodeOrEntry, beginRoot: KeyOrNodeOrEntry = this.root): number { dist = this.ensureNode(dist); beginRoot = this.ensureNode(beginRoot); let depth = 0; @@ -544,20 +540,20 @@ export class BinaryTree< * * The function `getHeight` calculates the maximum height of a binary tree using either recursive or * iterative traversal. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the * starting node of the binary tree from which we want to calculate the height. It can be of type - * `K`, `N`, `null`, or `undefined`. If not provided, it defaults to `this.root`. + * `K`, `NODE`, `null`, or `undefined`. If not provided, it defaults to `this.root`. * @param iterationType - The `iterationType` parameter is used to determine whether to calculate the * height of the tree using a recursive approach or an iterative approach. It can have two possible * values: * @returns the height of the binary tree. */ - getHeight(beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType): number { + getHeight(beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType): number { beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { - const _getMaxHeight = (cur: N | null | undefined): number => { + const _getMaxHeight = (cur: NODE | null | undefined): number => { if (!cur) return -1; const leftHeight = _getMaxHeight(cur.left); const rightHeight = _getMaxHeight(cur.right); @@ -566,7 +562,7 @@ export class BinaryTree< return _getMaxHeight(beginRoot); } else { - const stack: { node: N; depth: number }[] = [{ node: beginRoot, depth: 0 }]; + const stack: { node: NODE; depth: number }[] = [{ node: beginRoot, depth: 0 }]; let maxHeight = 0; while (stack.length > 0) { @@ -594,19 +590,19 @@ export class BinaryTree< * * The `getMinHeight` function calculates the minimum height of a binary tree using either a * recursive or iterative approach. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the * starting node of the binary tree from which we want to calculate the minimum height. It can be of - * type `K`, `N`, `null`, or `undefined`. If no value is provided, it defaults to `this.root`. + * type `K`, `NODE`, `null`, or `undefined`. If no value is provided, it defaults to `this.root`. * @param iterationType - The `iterationType` parameter is used to determine the method of iteration * to calculate the minimum height of a binary tree. It can have two possible values: * @returns The function `getMinHeight` returns the minimum height of a binary tree. */ - getMinHeight(beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType): number { + getMinHeight(beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType): number { beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { - const _getMinHeight = (cur: N | null | undefined): number => { + const _getMinHeight = (cur: NODE | null | undefined): number => { if (!cur) return 0; if (!cur.left && !cur.right) return 0; const leftMinHeight = _getMinHeight(cur.left); @@ -616,10 +612,10 @@ export class BinaryTree< return _getMinHeight(beginRoot); } else { - const stack: N[] = []; - let node: N | null | undefined = beginRoot, - last: N | null | undefined = null; - const depths: Map = new Map(); + const stack: NODE[] = []; + let node: NODE | null | undefined = beginRoot, + last: NODE | null | undefined = null; + const depths: Map = new Map(); while (stack.length > 0 || node) { if (node) { @@ -656,12 +652,12 @@ export class BinaryTree< * * The function checks if a binary tree is perfectly balanced by comparing the minimum height and the * height of the tree. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting point + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting point * for calculating the height and minimum height of a binary tree. It can be either a `K` (a key - * value of a binary tree node), `N` (a node of a binary tree), `null`, or `undefined`. If + * value of a binary tree node), `NODE` (a node of a binary tree), `null`, or `undefined`. If * @returns a boolean value. */ - isPerfectlyBalanced(beginRoot: KeyOrNodeOrEntry = this.root): boolean { + isPerfectlyBalanced(beginRoot: KeyOrNodeOrEntry = this.root): boolean { return this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot); } @@ -670,29 +666,29 @@ export class BinaryTree< * Space Complexity: O(log n) */ - getNodes>( + getNodes>( identifier: K, callback?: C, onlyOne?: boolean, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType - ): N[]; + ): NODE[]; - getNodes>( - identifier: N | null | undefined, + getNodes>( + identifier: NODE | null | undefined, callback?: C, onlyOne?: boolean, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType - ): N[]; + ): NODE[]; - getNodes>( + getNodes>( identifier: ReturnType, callback: C, onlyOne?: boolean, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType - ): N[]; + ): NODE[]; /** * Time Complexity: O(n) @@ -704,7 +700,7 @@ export class BinaryTree< * that you want to search for in the binary tree. It can be of any type that is returned by the * callback function `C`. It can also be `null` or `undefined` if you don't want to search for a * specific value. - * @param {C} callback - The `callback` parameter is a function that takes a node of type `N` as + * @param {C} callback - The `callback` parameter is a function that takes a node of type `NODE` as * input and returns a value of type `C`. It is used to determine if a node matches the given * identifier. If no callback is provided, the `_defaultOneParamCallback` function is used as the * default @@ -712,29 +708,29 @@ export class BinaryTree< * matches the identifier. If set to true, the function will stop iterating once it finds a matching * node and return that node. If set to false (default), the function will continue iterating and * return all nodes that match the identifier. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the * starting node for the traversal. It can be either a key, a node object, or `null`/`undefined`. If * it is `null` or `undefined`, an empty array will be returned. * @param iterationType - The `iterationType` parameter determines the type of iteration used to * traverse the binary tree. It can have two possible values: - * @returns an array of nodes of type `N`. + * @returns an array of nodes of type `NODE`. */ - getNodes>( + getNodes>( identifier: ReturnType | null | undefined, callback: C = this._defaultOneParamCallback as C, onlyOne = false, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType - ): N[] { + ): NODE[] { if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; - const ans: N[] = []; + const ans: NODE[] = []; if (iterationType === IterationType.RECURSIVE) { - const _traverse = (cur: N) => { + const _traverse = (cur: NODE) => { if (callback(cur) === identifier) { ans.push(cur); if (onlyOne) return; @@ -746,7 +742,7 @@ export class BinaryTree< _traverse(beginRoot); } else { - const queue = new Queue([beginRoot]); + const queue = new Queue([beginRoot]); while (queue.size > 0) { const cur = queue.shift(); if (cur) { @@ -768,24 +764,24 @@ export class BinaryTree< * Space Complexity: O(log n). */ - override has>( + override has>( identifier: K, callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType ): boolean; - override has>( - identifier: N | null | undefined, + override has>( + identifier: NODE | null | undefined, callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType ): boolean; - override has>( + override has>( identifier: ReturnType | null | undefined, callback: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType ): boolean; @@ -802,18 +798,18 @@ export class BinaryTree< * the binary tree. It is used to filter the nodes based on certain conditions. The `callback` * function should return a boolean value indicating whether the node should be included in the * result or not. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting point + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting point * for the search in the binary tree. It can be specified as a `K` (a unique identifier for a - * node in the binary tree), a node object (`N`), or `null`/`undefined` to start the search from + * node in the binary tree), a node object (`NODE`), or `null`/`undefined` to start the search from * @param iterationType - The `iterationType` parameter is a variable that determines the type of * iteration to be performed on the binary tree. It is used to specify whether the iteration should * be performed in a pre-order, in-order, or post-order manner. * @returns a boolean value. */ - override has>( + override has>( identifier: ReturnType | null | undefined, callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType ): boolean { if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode) @@ -827,26 +823,26 @@ export class BinaryTree< * Space Complexity: O(log n). */ - getNode>( + getNode>( identifier: K, callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType - ): N | null | undefined; + ): NODE | null | undefined; - getNode>( - identifier: N | null | undefined, + getNode>( + identifier: NODE | null | undefined, callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType - ): N | null | undefined; + ): NODE | null | undefined; - getNode>( + getNode>( identifier: ReturnType, callback: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType - ): N | null | undefined; + ): NODE | null | undefined; /** * Time Complexity: O(n) @@ -860,21 +856,21 @@ export class BinaryTree< * identifier. * @param {C} callback - The `callback` parameter is a function that will be called for each node in * the binary tree. It is used to determine if a node matches the given identifier. The `callback` - * function should take a single parameter of type `N` (the type of the nodes in the binary tree) and - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting point + * function should take a single parameter of type `NODE` (the type of the nodes in the binary tree) and + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting point * for searching the binary tree. It can be either a key value, a node object, or `null`/`undefined`. * If `null` or `undefined` is passed, the search will start from the root of the binary tree. * @param iterationType - The `iterationType` parameter is used to specify the type of iteration to * be performed when searching for nodes in the binary tree. It determines the order in which the * nodes are visited during the search. - * @returns a value of type `N | null | undefined`. + * @returns a value of type `NODE | null | undefined`. */ - getNode>( + getNode>( identifier: ReturnType | null | undefined, callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType - ): N | null | undefined { + ): NODE | null | undefined { if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; @@ -897,13 +893,13 @@ export class BinaryTree< * @param iterationType - The `iterationType` parameter is used to determine whether the search for * the node with the given key should be performed iteratively or recursively. It has two possible * values: - * @returns The function `getNodeByKey` returns a node (`N`) if a node with the specified key is + * @returns The function `getNodeByKey` returns a node (`NODE`) if a node with the specified key is * found in the binary tree. If no node is found, it returns `undefined`. */ - getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): N | undefined { + getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): NODE | undefined { if (!this.root) return undefined; if (iterationType === IterationType.RECURSIVE) { - const _dfs = (cur: N): N | undefined => { + const _dfs = (cur: NODE): NODE | undefined => { if (cur.key === key) return cur; if (!cur.left && !cur.right) return; @@ -913,7 +909,7 @@ export class BinaryTree< return _dfs(this.root); } else { - const queue = new Queue([this.root]); + const queue = new Queue([this.root]); while (queue.size > 0) { const cur = queue.shift(); if (cur) { @@ -925,24 +921,24 @@ export class BinaryTree< } } - override get>( + override get>( identifier: K, callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType ): V | undefined; - override get>( - identifier: N | null | undefined, + override get>( + identifier: NODE | null | undefined, callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType ): V | undefined; - override get>( + override get>( identifier: ReturnType, callback: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType ): V | undefined; @@ -959,19 +955,19 @@ export class BinaryTree< * the binary tree. It is used to determine whether a node matches the given identifier. The callback * function should return a value that can be compared to the identifier to determine if it is a * match. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting point + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting point * for the search in the binary tree. It can be specified as a `K` (a unique identifier for a - * node), a node object of type `N`, or `null`/`undefined` to start the search from the root of + * node), a node object of type `NODE`, or `null`/`undefined` to start the search from the root of * @param iterationType - The `iterationType` parameter is used to specify the type of iteration to * be performed when searching for a node in the binary tree. It is an optional parameter with a * default value specified by `this.iterationType`. * @returns The value of the node with the given identifier is being returned. If the node is not * found, `undefined` is returned. */ - override get>( + override get>( identifier: ReturnType | null | undefined, callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType ): V | undefined { if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode) @@ -1018,17 +1014,17 @@ export class BinaryTree< * * The function `getPathToRoot` returns an array of nodes from a given node to the root of a tree * structure, with the option to reverse the order of the nodes. - * @param {K | N | null | undefined} beginNode - The `beginRoot` parameter represents the - * starting node from which you want to find the path to the root. It can be of type `K`, `N`, + * @param {K | NODE | null | undefined} beginNode - The `beginRoot` parameter represents the + * starting node from which you want to find the path to the root. It can be of type `K`, `NODE`, * `null`, or `undefined`. * @param [isReverse=true] - The `isReverse` parameter is a boolean flag that determines whether the * resulting path should be reversed or not. If `isReverse` is set to `true`, the path will be * reversed before returning it. If `isReverse` is set to `false`, the path will be returned as is - * @returns The function `getPathToRoot` returns an array of nodes (`N[]`). + * @returns The function `getPathToRoot` returns an array of nodes (`NODE[]`). */ - getPathToRoot(beginNode: KeyOrNodeOrEntry, isReverse = true): N[] { + getPathToRoot(beginNode: KeyOrNodeOrEntry, isReverse = true): NODE[] { // TODO to support get path through passing key - const result: N[] = []; + const result: NODE[] = []; beginNode = this.ensureNode(beginNode); if (!beginNode) return result; @@ -1054,24 +1050,24 @@ export class BinaryTree< * * The function `getLeftMost` returns the leftmost node in a binary tree, either recursively or * iteratively. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting point - * for finding the leftmost node in a binary tree. It can be either a `K` (a key value), `N` (a + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting point + * for finding the leftmost node in a binary tree. It can be either a `K` (a key value), `NODE` (a * node), `null`, or `undefined`. If not provided, it defaults to `this.root`, * @param iterationType - The `iterationType` parameter is used to determine the type of iteration to * be performed when finding the leftmost node in a binary tree. It can have two possible values: - * @returns The function `getLeftMost` returns the leftmost node (`N`) in the binary tree. If there + * @returns The function `getLeftMost` returns the leftmost node (`NODE`) in the binary tree. If there * is no leftmost node, it returns `null` or `undefined` depending on the input. */ getLeftMost( - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType - ): N | null | undefined { + ): NODE | null | undefined { beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return beginRoot; if (iterationType === IterationType.RECURSIVE) { - const _traverse = (cur: N): N => { + const _traverse = (cur: NODE): NODE => { if (!this.isRealNode(cur.left)) return cur; return _traverse(cur.left); }; @@ -1079,7 +1075,7 @@ export class BinaryTree< return _traverse(beginRoot); } else { // Indirect implementation of iteration using tail recursion optimization - const _traverse = trampoline((cur: N) => { + const _traverse = trampoline((cur: NODE) => { if (!this.isRealNode(cur.left)) return cur; return _traverse.cont(cur.left); }); @@ -1099,25 +1095,25 @@ export class BinaryTree< * * The function `getRightMost` returns the rightmost node in a binary tree, either recursively or * iteratively. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the - * starting node from which we want to find the rightmost node. It can be of type `K`, `N`, + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the + * starting node from which we want to find the rightmost node. It can be of type `K`, `NODE`, * `null`, or `undefined`. If not provided, it defaults to `this.root`, which is a property of the * current object. * @param iterationType - The `iterationType` parameter is an optional parameter that specifies the * type of iteration to use when finding the rightmost node. It can have one of two values: - * @returns The function `getRightMost` returns the rightmost node (`N`) in a binary tree. If there + * @returns The function `getRightMost` returns the rightmost node (`NODE`) in a binary tree. If there * is no rightmost node, it returns `null` or `undefined`, depending on the input. */ getRightMost( - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType - ): N | null | undefined { + ): NODE | null | undefined { // TODO support get right most by passing key in beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return beginRoot; if (iterationType === IterationType.RECURSIVE) { - const _traverse = (cur: N): N => { + const _traverse = (cur: NODE): NODE => { if (!this.isRealNode(cur.right)) return cur; return _traverse(cur.right); }; @@ -1125,7 +1121,7 @@ export class BinaryTree< return _traverse(beginRoot); } else { // Indirect implementation of iteration using tail recursion optimization - const _traverse = trampoline((cur: N) => { + const _traverse = trampoline((cur: NODE) => { if (!this.isRealNode(cur.right)) return cur; return _traverse.cont(cur.right); }); @@ -1144,20 +1140,20 @@ export class BinaryTree< * Space Complexity: O(1) * * The function `isSubtreeBST` checks if a given binary tree is a valid binary search tree. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the root + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the root * node of the binary search tree (BST) that you want to check if it is a subtree of another BST. * @param iterationType - The `iterationType` parameter is an optional parameter that specifies the * type of iteration to use when checking if a subtree is a binary search tree (BST). It can have two * possible values: * @returns a boolean value. */ - isBST(beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType): boolean { + isBST(beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType): boolean { // TODO there is a bug beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return true; if (iterationType === IterationType.RECURSIVE) { - const dfs = (cur: N | null | undefined, min: number, max: number): boolean => { + const dfs = (cur: NODE | null | undefined, min: number, max: number): boolean => { if (!cur) return true; const numKey = this.extractor(cur.key); if (numKey <= min || numKey >= max) return false; @@ -1172,7 +1168,7 @@ export class BinaryTree< const stack = []; let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER; // @ts-ignore - let curr: N | null | undefined = beginRoot; + let curr: NODE | null | undefined = beginRoot; while (curr || stack.length > 0) { while (curr) { stack.push(curr); @@ -1197,16 +1193,16 @@ export class BinaryTree< // * Space complexity: O(log n) // */ // - // subTreeTraverse>( + // subTreeTraverse>( // callback?: C, - // beginRoot?: KeyOrNodeOrEntry, + // beginRoot?: KeyOrNodeOrEntry, // iterationType?: IterationType, // includeNull?: false // ): ReturnType[]; // - // subTreeTraverse>( + // subTreeTraverse>( // callback?: C, - // beginRoot?: KeyOrNodeOrEntry, + // beginRoot?: KeyOrNodeOrEntry, // iterationType?: IterationType, // includeNull?: true // ): ReturnType[]; @@ -1220,9 +1216,9 @@ export class BinaryTree< // * @param {C} callback - The `callback` parameter is a function that will be called for each node in // * the subtree traversal. It takes a single parameter, which is the current node being traversed, and // * returns a value of any type. - // * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the + // * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the // * starting node or key from which the subtree traversal should begin. It can be of type `K`, - // * `N`, `null`, or `undefined`. If not provided, the `root` property of the current object is used as + // * `NODE`, `null`, or `undefined`. If not provided, the `root` property of the current object is used as // * the default value. // * @param iterationType - The `iterationType` parameter determines the type of traversal to be // * performed on the subtree. It can have two possible values: @@ -1233,9 +1229,9 @@ export class BinaryTree< // * the `callback` function on each node in the subtree. The type of the array nodes is determined // * by the return type of the `callback` function. // */ - // subTreeTraverse>( + // subTreeTraverse>( // callback: C = this._defaultOneParamCallback as C, - // beginRoot: KeyOrNodeOrEntry = this.root, + // beginRoot: KeyOrNodeOrEntry = this.root, // iterationType = this.iterationType, // includeNull = false // ): ReturnType[] { @@ -1243,11 +1239,11 @@ export class BinaryTree< // // beginRoot = this.ensureNode(beginRoot); // - // const ans: (ReturnType> | null | undefined)[] = []; + // const ans: (ReturnType> | null | undefined)[] = []; // if (!beginRoot) return ans; // // if (iterationType === IterationType.RECURSIVE) { - // const _traverse = (cur: N | null | undefined) => { + // const _traverse = (cur: NODE | null | undefined) => { // if (cur !== undefined) { // ans.push(callback(cur)); // if (includeNull) { @@ -1262,7 +1258,7 @@ export class BinaryTree< // // _traverse(beginRoot); // } else { - // const stack: (N | null | undefined)[] = [beginRoot]; + // const stack: (NODE | null | undefined)[] = [beginRoot]; // // while (stack.length > 0) { // const cur = stack.pop(); @@ -1281,18 +1277,18 @@ export class BinaryTree< // return ans; // } - dfs>( + dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType, includeNull?: false ): ReturnType[]; - dfs>( + dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1305,11 +1301,11 @@ export class BinaryTree< * specified pattern and iteration type, and returns an array of values obtained from applying a * callback function to each visited node. * @param {C} callback - The `callback` parameter is a function that will be called for each node in - * the tree during the depth-first search. It takes a single parameter, which can be of type `N`, + * the tree during the depth-first search. It takes a single parameter, which can be of type `NODE`, * `null`, or `undefined`, and returns a value of any type. The default value for this parameter is * @param {DFSOrderPattern} [pattern=in] - The `pattern` parameter determines the order in which the * nodes are traversed during the depth-first search. It can have one of the following values: - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting node + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting node * for the depth-first search traversal. It can be specified as a key, a node object, or * `null`/`undefined`. If not provided, the `beginRoot` will default to the root node of the tree. * @param {IterationType} iterationType - The `iterationType` parameter determines the type of @@ -1320,10 +1316,10 @@ export class BinaryTree< * `false`, null or undefined * @returns an array of values that are the return values of the callback function. */ - dfs>( + dfs>( callback: C = this._defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType: IterationType = IterationType.ITERATIVE, includeNull = false ): ReturnType[] { @@ -1331,7 +1327,7 @@ export class BinaryTree< if (!beginRoot) return []; const ans: ReturnType[] = []; if (iterationType === IterationType.RECURSIVE) { - const _traverse = (node: N | null | undefined) => { + const _traverse = (node: NODE | null | undefined) => { switch (pattern) { case 'in': if (includeNull) { @@ -1373,7 +1369,7 @@ export class BinaryTree< _traverse(beginRoot); } else { // 0: visit, 1: print - const stack: { opt: 0 | 1; node: N | null | undefined }[] = [{ opt: 0, node: beginRoot }]; + const stack: { opt: 0 | 1; node: NODE | null | undefined }[] = [{ opt: 0, node: beginRoot }]; while (stack.length > 0) { const cur = stack.pop(); @@ -1420,16 +1416,16 @@ export class BinaryTree< * Space complexity: O(n) */ - bfs>( + bfs>( callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType, includeNull?: false ): ReturnType[]; - bfs>( + bfs>( callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1443,7 +1439,7 @@ export class BinaryTree< * @param {C} callback - The `callback` parameter is a function that will be called for each node in * the breadth-first search traversal. It takes a single parameter, which is the current node being * visited, and returns a value of any type. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the * starting node for the breadth-first search traversal. It can be specified as a key, a node object, * or `null`/`undefined` to indicate the root of the tree. If not provided, the `root` property of * the class is used as @@ -1455,19 +1451,19 @@ export class BinaryTree< * @returns an array of values that are the result of invoking the callback function on each node in * the breadth-first traversal of a binary tree. */ - bfs>( + bfs>( callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[] { beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; - const ans: ReturnType>[] = []; + const ans: ReturnType>[] = []; if (iterationType === IterationType.RECURSIVE) { - const queue: Queue = new Queue([beginRoot]); + const queue: Queue = new Queue([beginRoot]); const traverse = (level: number) => { if (queue.size === 0) return; @@ -1488,7 +1484,7 @@ export class BinaryTree< traverse(0); } else { - const queue = new Queue([beginRoot]); + const queue = new Queue([beginRoot]); while (queue.size > 0) { const levelSize = queue.size; @@ -1514,16 +1510,16 @@ export class BinaryTree< * Space complexity: O(n) */ - listLevels>( + listLevels>( callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType, includeNull?: false ): ReturnType[][]; - listLevels>( + listLevels>( callback?: C, - beginRoot?: KeyOrNodeOrEntry, + beginRoot?: KeyOrNodeOrEntry, iterationType?: IterationType, includeNull?: true ): ReturnType[][]; @@ -1536,10 +1532,10 @@ export class BinaryTree< * a binary tree and contains the values returned by a callback function applied to the nodes at that * level. * @param {C} callback - The `callback` parameter is a function that will be called for each node in - * the tree. It takes a single parameter, which can be of type `N`, `null`, or `undefined`, and + * the tree. It takes a single parameter, which can be of type `NODE`, `null`, or `undefined`, and * returns a value of any type. - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter represents the - * starting node for traversing the tree. It can be either a node object (`N`), a key value + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter represents the + * starting node for traversing the tree. It can be either a node object (`NODE`), a key value * (`K`), `null`, or `undefined`. If not provided, it defaults to the root node of the tree. * @param iterationType - The `iterationType` parameter determines the type of iteration to be * performed on the tree. It can have two possible values: @@ -1549,9 +1545,9 @@ export class BinaryTree< * be excluded * @returns The function `listLevels` returns a two-dimensional array of type `ReturnType[][]`. */ - listLevels>( + listLevels>( callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[][] { @@ -1560,7 +1556,7 @@ export class BinaryTree< if (!beginRoot) return levelsNodes; if (iterationType === IterationType.RECURSIVE) { - const _recursive = (node: N | null, level: number) => { + const _recursive = (node: NODE | null, level: number) => { if (!levelsNodes[level]) levelsNodes[level] = []; levelsNodes[level].push(callback(node)); if (includeNull) { @@ -1574,7 +1570,7 @@ export class BinaryTree< _recursive(beginRoot, 0); } else { - const stack: [N | null, number][] = [[beginRoot, 0]]; + const stack: [NODE | null, number][] = [[beginRoot, 0]]; while (stack.length > 0) { const head = stack.pop()!; @@ -1606,13 +1602,13 @@ export class BinaryTree< * Space Complexity: O(1) * * The function returns the predecessor of a given node in a tree. - * @param {N} node - The parameter `node` is of type `RedBlackTreeNode`, which represents a node in a + * @param {NODE} node - The parameter `node` is of type `RedBlackTreeNode`, which represents a node in a * tree. * @returns the predecessor of the given 'node'. */ - getPredecessor(node: N): N { + getPredecessor(node: NODE): NODE { if (this.isRealNode(node.left)) { - let predecessor: N | null | undefined = node.left; + let predecessor: NODE | null | undefined = node.left; while (!this.isRealNode(predecessor) || (this.isRealNode(predecessor.right) && predecessor.right !== node)) { if (this.isRealNode(predecessor)) { predecessor = predecessor.right; @@ -1626,11 +1622,11 @@ export class BinaryTree< /** * The function `getSuccessor` returns the next node in a binary tree given a current node. - * @param {K | N | null} [x] - The parameter `x` can be of type `K`, `N`, or `null`. + * @param {K | NODE | null} [x] - The parameter `x` can be of type `K`, `NODE`, or `null`. * @returns the successor of the given node or key. The successor is the node that comes immediately * after the given node in the inorder traversal of the binary tree. */ - getSuccessor(x?: K | N | null): N | null | undefined { + getSuccessor(x?: K | NODE | null): NODE | null | undefined { x = this.ensureNode(x); if (!this.isRealNode(x)) return undefined; @@ -1638,7 +1634,7 @@ export class BinaryTree< return this.getLeftMost(x.right); } - let y: N | null | undefined = x.parent; + let y: NODE | null | undefined = x.parent; while (this.isRealNode(y) && x === y.right) { x = y; y = y.parent; @@ -1658,31 +1654,31 @@ export class BinaryTree< * The `morris` function performs a depth-first traversal on a binary tree using the Morris traversal * algorithm. * @param {C} callback - The `callback` parameter is a function that will be called for each node in - * the tree. It takes a single parameter of type `N` (the type of the nodes in the tree) and returns + * the tree. It takes a single parameter of type `NODE` (the type of the nodes in the tree) and returns * a value of any type. * @param {DFSOrderPattern} [pattern=in] - The `pattern` parameter in the `morris` function * determines the order in which the nodes of a binary tree are traversed. It can have one of the * following values: - * @param {K | N | null | undefined} beginRoot - The `beginRoot` parameter is the starting node + * @param {K | NODE | null | undefined} beginRoot - The `beginRoot` parameter is the starting node * for the traversal. It can be specified as a key, a node object, or `null`/`undefined` to indicate * the root of the tree. If no value is provided, the default value is the root of the tree. * @returns The function `morris` returns an array of values that are the result of invoking the * `callback` function on each node in the binary tree. The type of the array nodes is determined * by the return type of the `callback` function. */ - morris>( + morris>( callback: C = this._defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', - beginRoot: KeyOrNodeOrEntry = this.root + beginRoot: KeyOrNodeOrEntry = this.root ): ReturnType[] { beginRoot = this.ensureNode(beginRoot); if (beginRoot === null) return []; - const ans: ReturnType>[] = []; + const ans: ReturnType>[] = []; - let cur: N | null | undefined = beginRoot; - const _reverseEdge = (node: N | null | undefined) => { - let pre: N | null | undefined = null; - let next: N | null | undefined = null; + let cur: NODE | null | undefined = beginRoot; + const _reverseEdge = (node: NODE | null | undefined) => { + let pre: NODE | null | undefined = null; + let next: NODE | null | undefined = null; while (node) { next = node.right; node.right = pre; @@ -1691,9 +1687,9 @@ export class BinaryTree< } return pre; }; - const _printEdge = (node: N | null | undefined) => { - const tail: N | null | undefined = _reverseEdge(node); - let cur: N | null | undefined = tail; + const _printEdge = (node: NODE | null | undefined) => { + const tail: NODE | null | undefined = _reverseEdge(node); + let cur: NODE | null | undefined = tail; while (cur) { ans.push(callback(cur)); cur = cur.right; @@ -1771,7 +1767,15 @@ export class BinaryTree< */ clone(): TREE { const cloned = this.createTree(); - this.bfs(node => cloned.add([node.key, node.value])); + this.bfs( + node => { + if (node === null) cloned.add(null); + else cloned.add([node.key, node.value]); + }, + this.root, + this.iterationType, + true + ); return cloned; } @@ -1855,12 +1859,12 @@ export class BinaryTree< * Space Complexity: O(n) * * The `print` function is used to display a binary tree structure in a visually appealing way. - * @param {K | N | null | undefined} [beginRoot=this.root] - The `root` parameter is of type `K | N | null | + * @param {K | NODE | null | undefined} [beginRoot=this.root] - The `root` parameter is of type `K | NODE | null | * undefined`. It represents the root node of a binary tree. The root node can have one of the * following types: * @param {BinaryTreePrintOptions} [options={ isShowUndefined: false, isShowNull: false, isShowRedBlackNIL: false}] - Options object that controls printing behavior. You can specify whether to display undefined, null, or sentinel nodes. */ - print(beginRoot: KeyOrNodeOrEntry = this.root, options?: BinaryTreePrintOptions): void { + print(beginRoot: KeyOrNodeOrEntry = this.root, options?: BinaryTreePrintOptions): void { const opts = { isShowUndefined: false, isShowNull: false, isShowRedBlackNIL: false, ...options }; beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return; @@ -1869,13 +1873,13 @@ export class BinaryTree< console.log(`U for undefined `); if (opts.isShowNull) - console.log(`N for null + console.log(`NODE for null `); if (opts.isShowRedBlackNIL) console.log(`S for Sentinel Node `); - const display = (root: N | null | undefined): void => { + const display = (root: NODE | null | undefined): void => { const [lines, , ,] = this._displayAux(root, opts); for (const line of lines) { console.log(line); @@ -1889,8 +1893,8 @@ export class BinaryTree< if (!node) return; if (this.iterationType === IterationType.ITERATIVE) { - const stack: (N | null | undefined)[] = []; - let current: N | null | undefined = node; + const stack: (NODE | null | undefined)[] = []; + let current: NODE | null | undefined = node; while (current || stack.length > 0) { while (current && !isNaN(this.extractor(current.key))) { @@ -1916,7 +1920,7 @@ export class BinaryTree< } } - protected _displayAux(node: N | null | undefined, options: BinaryTreePrintOptions): NodeDisplayLayout { + protected _displayAux(node: NODE | null | undefined, options: BinaryTreePrintOptions): NodeDisplayLayout { const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options; const emptyDisplayLayout = [['─'], 1, 0, 0]; @@ -1942,7 +1946,7 @@ export class BinaryTree< ); } else { // For cases where none of the conditions are met, null, undefined, and NaN nodes are not displayed - const line = node === undefined ? 'U' : 'N', + const line = node === undefined ? 'U' : 'NODE', width = line.length; return _buildNodeDisplay(line, width, [[''], 1, 0, 0], [[''], 1, 0, 0]); @@ -1984,15 +1988,18 @@ export class BinaryTree< } } - protected _defaultOneParamCallback = (node: N | null | undefined) => (node ? node.key : undefined); + protected _defaultOneParamCallback = (node: NODE | null | undefined) => (node ? node.key : undefined); /** * Swap the data of two nodes in the binary tree. - * @param {N} srcNode - The source node to swap. - * @param {N} destNode - The destination node to swap. - * @returns {N} - The destination node after the swap. + * @param {NODE} srcNode - The source node to swap. + * @param {NODE} destNode - The destination node to swap. + * @returns {NODE} - The destination node after the swap. */ - protected _swapProperties(srcNode: KeyOrNodeOrEntry, destNode: KeyOrNodeOrEntry): N | undefined { + protected _swapProperties( + srcNode: KeyOrNodeOrEntry, + destNode: KeyOrNodeOrEntry + ): NODE | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -2015,13 +2022,13 @@ export class BinaryTree< /** * The function replaces an old node with a new node in a binary tree. - * @param {N} oldNode - The oldNode parameter represents the node that needs to be replaced in the + * @param {NODE} oldNode - The oldNode parameter represents the node that needs to be replaced in the * tree. - * @param {N} newNode - The `newNode` parameter is the node that will replace the `oldNode` in the + * @param {NODE} newNode - The `newNode` parameter is the node that will replace the `oldNode` in the * tree. * @returns The method is returning the newNode. */ - protected _replaceNode(oldNode: N, newNode: N): N { + protected _replaceNode(oldNode: NODE, newNode: NODE): NODE { if (oldNode.parent) { if (oldNode.parent.left === oldNode) { oldNode.parent.left = newNode; @@ -2042,10 +2049,10 @@ export class BinaryTree< /** * The function sets the root property of an object to a given value, and if the value is not null, * it also sets the parent property of the value to undefined. - * @param {N | null | undefined} v - The parameter `v` is of type `N | null | undefined`, which means it can either be of - * type `N` or `null`. + * @param {NODE | null | undefined} v - The parameter `v` is of type `NODE | null | undefined`, which means it can either be of + * type `NODE` or `null`. */ - protected _setRoot(v: N | null | undefined) { + protected _setRoot(v: NODE | null | undefined) { if (v) { v.parent = undefined; } diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 6a8d247..bddaddc 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -18,12 +18,12 @@ import { BinaryTree, BinaryTreeNode } from './binary-tree'; import { IBinaryTree } from '../../interfaces'; import { Queue } from '../queue'; -export class BSTNode = BSTNodeNested> extends BinaryTreeNode< +export class BSTNode = BSTNodeNested> extends BinaryTreeNode< K, V, - N + NODE > { - override parent?: N; + override parent?: NODE; constructor(key: K, value?: V) { super(key, value); @@ -32,42 +32,42 @@ export class BSTNode = BSTNodeNeste this._right = undefined; } - protected override _left?: N; + protected override _left?: NODE; /** * Get the left child node. */ - override get left(): N | undefined { + override get left(): NODE | undefined { return this._left; } /** * Set the left child node. - * @param {N | undefined} v - The left child node. + * @param {NODE | undefined} v - The left child node. */ - override set left(v: N | undefined) { + override set left(v: NODE | undefined) { if (v) { - v.parent = this as unknown as N; + v.parent = this as unknown as NODE; } this._left = v; } - protected override _right?: N; + protected override _right?: NODE; /** * Get the right child node. */ - override get right(): N | undefined { + override get right(): NODE | undefined { return this._right; } /** * Set the right child node. - * @param {N | undefined} v - The right child node. + * @param {NODE | undefined} v - The right child node. */ - override set right(v: N | undefined) { + override set right(v: NODE | undefined) { if (v) { - v.parent = this as unknown as N; + v.parent = this as unknown as NODE; } this._right = v; } @@ -85,11 +85,11 @@ export class BSTNode = BSTNodeNeste export class BST< K = any, V = any, - N extends BSTNode = BSTNode>, - TREE extends BST = BST> + NODE extends BSTNode = BSTNode>, + TREE extends BST = BST> > - extends BinaryTree - implements IBinaryTree { + extends BinaryTree + implements IBinaryTree { /** * This is the constructor function for a binary search tree class in TypeScript, which initializes * the tree with optional keysOrNodesOrEntries and options. @@ -98,7 +98,7 @@ export class BST< * @param [options] - The `options` parameter is an optional object that can contain additional * configuration options for the binary search tree. It can have the following properties: */ - constructor(keysOrNodesOrEntries: Iterable> = [], options?: BSTOptions) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: BSTOptions) { super([], options); if (options) { @@ -111,9 +111,9 @@ export class BST< if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } - protected override _root?: N; + protected override _root?: NODE; - override get root(): N | undefined { + override get root(): NODE | undefined { return this._root; } @@ -131,8 +131,8 @@ export class BST< * represents the value associated with the node in a binary search tree. * @returns a new instance of the BSTNode class with the specified key and value. */ - override createNode(key: K, value?: V): N { - return new BSTNode(key, value) as N; + override createNode(key: K, value?: V): NODE { + return new BSTNode(key, value) as NODE; } /** @@ -143,7 +143,7 @@ export class BST< * @returns a new instance of the BST class with the specified options. */ override createTree(options?: Partial>): TREE { - return new BST([], { + return new BST([], { iterationType: this.iterationType, variant: this.variant, ...options @@ -153,13 +153,13 @@ export class BST< /** * The function `keyValueOrEntryToNode` takes an keyOrNodeOrEntry and returns a node if the keyOrNodeOrEntry is valid, * otherwise it returns undefined. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`, where: + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`, where: * @param {V} [value] - The `value` parameter is an optional value that can be passed to the * `keyValueOrEntryToNode` function. It represents the value associated with the keyOrNodeOrEntry node. - * @returns a node of type N or undefined. + * @returns a node of type NODE or undefined. */ - override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): N | undefined { - let node: N | undefined; + override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): NODE | undefined { + let node: NODE | undefined; if (keyOrNodeOrEntry === null || keyOrNodeOrEntry === undefined) { return; } else if (this.isNode(keyOrNodeOrEntry)) { @@ -191,17 +191,17 @@ export class BST< * * The function `ensureNode` returns the node corresponding to the given key if it is a node key, * otherwise it returns the key itself. - * @param {K | N | undefined} keyOrNodeOrEntry - The `key` parameter can be of type `K`, `N`, or + * @param {K | NODE | undefined} keyOrNodeOrEntry - The `key` parameter can be of type `K`, `NODE`, or * `undefined`. * @param iterationType - The `iterationType` parameter is an optional parameter that specifies the * type of iteration to be performed. It has a default value of `IterationType.ITERATIVE`. - * @returns either a node object (N) or undefined. + * @returns either a node object (NODE) or undefined. */ override ensureNode( - keyOrNodeOrEntry: KeyOrNodeOrEntry, + keyOrNodeOrEntry: KeyOrNodeOrEntry, iterationType = IterationType.ITERATIVE - ): N | undefined { - let res: N | undefined; + ): NODE | undefined { + let res: NODE | undefined; if (this.isRealNode(keyOrNodeOrEntry)) { res = keyOrNodeOrEntry; } else if (this.isEntry(keyOrNodeOrEntry)) { @@ -214,10 +214,10 @@ export class BST< /** * The function checks if an keyOrNodeOrEntry is an instance of BSTNode. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is a variable of type `KeyOrNodeOrEntry`. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is a variable of type `KeyOrNodeOrEntry`. * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the BSTNode class. */ - override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is N { + override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { return keyOrNodeOrEntry instanceof BSTNode; } @@ -239,7 +239,7 @@ export class BST< * @returns The method `add` returns either the newly added node (`newNode`) or `undefined` if the * node was not added. */ - override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { + override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value); if (newNode === undefined) return false; @@ -309,10 +309,10 @@ export class BST< * @param iterationType - The `iterationType` parameter is an optional parameter that specifies the * type of iteration to use when adding multiple keys or nodes. It has a default value of * `this.iterationType`, which suggests that it is a property of the current object. - * @returns The function `addMany` returns an array of nodes (`N`) or `undefined` values. + * @returns The function `addMany` returns an array of nodes (`NODE`) or `undefined` values. */ override addMany( - keysOrNodesOrEntries: Iterable>, + keysOrNodesOrEntries: Iterable>, values?: Iterable, isBalanceAdd = true, iterationType = this.iterationType @@ -334,9 +334,9 @@ export class BST< return inserted; } - const realBTNExemplars: BTNodePureExemplar[] = []; + const realBTNExemplars: BTNodePureExemplar[] = []; - const isRealBTNExemplar = (kve: KeyOrNodeOrEntry): kve is BTNodePureExemplar => { + const isRealBTNExemplar = (kve: KeyOrNodeOrEntry): kve is BTNodePureExemplar => { if (kve === undefined || kve === null) return false; return !(this.isEntry(kve) && (kve[0] === undefined || kve[0] === null)); }; @@ -345,7 +345,7 @@ export class BST< isRealBTNExemplar(kve) && realBTNExemplars.push(kve); } - let sorted: BTNodePureExemplar[] = []; + let sorted: BTNodePureExemplar[] = []; sorted = realBTNExemplars.sort((a, b) => { let aR: number, bR: number; @@ -360,7 +360,7 @@ export class BST< return aR - bR; }); - const _dfs = (arr: BTNodePureExemplar[]) => { + const _dfs = (arr: BTNodePureExemplar[]) => { if (arr.length === 0) return; const mid = Math.floor((arr.length - 1) / 2); @@ -413,13 +413,13 @@ export class BST< * @param iterationType - The `iterationType` parameter is an optional parameter that specifies the * type of iteration to use when searching for a node in the binary tree. It can have two possible * values: - * @returns The function `getNodeByKey` returns a node (`N`) if a node with the specified key is + * @returns The function `getNodeByKey` returns a node (`NODE`) if a node with the specified key is * found in the binary tree. If no node is found, it returns `undefined`. */ - override getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): N | undefined { + override getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): NODE | undefined { if (!this.root) return undefined; if (iterationType === IterationType.RECURSIVE) { - const _dfs = (cur: N): N | undefined => { + const _dfs = (cur: NODE): NODE | undefined => { if (cur.key === key) return cur; if (!cur.left && !cur.right) return; @@ -429,7 +429,7 @@ export class BST< return _dfs(this.root); } else { - const queue = new Queue([this.root]); + const queue = new Queue([this.root]); while (queue.size > 0) { const cur = queue.shift(); if (cur) { @@ -456,33 +456,33 @@ export class BST< * @param {ReturnType | undefined} identifier - The `identifier` parameter is the value that you * want to search for in the nodes of the binary tree. It can be of any type that is returned by the * callback function `C`. - * @param {C} callback - The `callback` parameter is a function that takes a node of type `N` as its + * @param {C} callback - The `callback` parameter is a function that takes a node of type `NODE` as its * argument and returns a value of type `ReturnType`. The `C` type parameter represents a callback - * function type that extends the `BTNCallback` type. The `BTNCallback` type is + * function type that extends the `BTNCallback` type. The `BTNCallback` type is * @param [onlyOne=false] - A boolean flag indicating whether to stop searching after finding the * first node that matches the identifier. If set to true, the function will return an array * containing only the first matching node. If set to false (default), the function will continue * searching for all nodes that match the identifier and return an array containing - * @param {K | N | undefined} beginRoot - The `beginRoot` parameter represents the starting node + * @param {K | NODE | undefined} beginRoot - The `beginRoot` parameter represents the starting node * for the traversal. It can be either a key value or a node object. If it is undefined, the * traversal will start from the root of the tree. * @param iterationType - The `iterationType` parameter determines the type of iteration to be * performed on the binary tree. It can have two possible values: - * @returns The method returns an array of nodes (`N[]`). + * @returns The method returns an array of nodes (`NODE[]`). */ - override getNodes>( + override getNodes>( identifier: ReturnType | undefined, callback: C = this._defaultOneParamCallback as C, onlyOne = false, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType - ): N[] { + ): NODE[] { beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; - const ans: N[] = []; + const ans: NODE[] = []; if (iterationType === IterationType.RECURSIVE) { - const _traverse = (cur: N) => { + const _traverse = (cur: NODE) => { const callbackResult = callback(cur); if (callbackResult === identifier) { ans.push(cur); @@ -502,7 +502,7 @@ export class BST< _traverse(beginRoot); } else { - const queue = new Queue([beginRoot]); + const queue = new Queue([beginRoot]); while (queue.size > 0) { const cur = queue.shift(); if (cur) { @@ -530,7 +530,7 @@ export class BST< // * The function overrides the subTreeTraverse method and returns the result of calling the super // * method with the provided arguments. // * @param {C} callback - The `callback` parameter is a function that will be called for each node in - // * the subtree traversal. It should accept a single parameter of type `N`, which represents a node in + // * the subtree traversal. It should accept a single parameter of type `NODE`, which represents a node in // * the tree. The return type of the callback function can be any type. // * @param beginRoot - The `beginRoot` parameter is the starting point for traversing the subtree. It // * can be either a key, a node, or an entry. @@ -538,9 +538,9 @@ export class BST< // * be performed during the traversal of the subtree. It can have one of the following values: // * @returns The method is returning an array of the return type of the callback function. // */ - // override subTreeTraverse>( + // override subTreeTraverse>( // callback: C = this._defaultOneParamCallback as C, - // beginRoot: KeyOrNodeOrEntry = this.root, + // beginRoot: KeyOrNodeOrEntry = this.root, // iterationType = this.iterationType // ): ReturnType[] { // return super.subTreeTraverse(callback, beginRoot, iterationType, false); @@ -570,10 +570,10 @@ export class BST< * following values: * @returns The method is returning an array of the return type of the callback function. */ - override dfs>( + override dfs>( callback: C = this._defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType: IterationType = IterationType.ITERATIVE ): ReturnType[] { return super.dfs(callback, pattern, beginRoot, iterationType, false); @@ -601,9 +601,9 @@ export class BST< * nodes are visited. * @returns The method is returning an array of the return type of the callback function. */ - override bfs>( + override bfs>( callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType ): ReturnType[] { return super.bfs(callback, beginRoot, iterationType, false); @@ -621,7 +621,7 @@ export class BST< * The function overrides the listLevels method and returns an array of arrays containing the return * type of the callback function for each level of the tree. * @param {C} callback - The `callback` parameter is a generic type `C` that extends - * `BTNCallback`. It represents a callback function that will be called for each node in the tree + * `BTNCallback`. It represents a callback function that will be called for each node in the tree * during the level listing process. * @param beginRoot - The `beginRoot` parameter is used to specify the starting point for listing the * levels of a binary tree. It can be either a key, a node, or an entry in the binary tree. If not @@ -632,9 +632,9 @@ export class BST< * @returns The method is returning a two-dimensional array of the return type of the callback * function. */ - override listLevels>( + override listLevels>( callback: C = this._defaultOneParamCallback as C, - beginRoot: KeyOrNodeOrEntry = this.root, + beginRoot: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType ): ReturnType[][] { return super.listLevels(callback, beginRoot, iterationType, false); @@ -652,14 +652,14 @@ export class BST< * * The `lastKey` function returns the key of the rightmost node in a binary tree, or the key of the * leftmost node if the comparison result is greater than. - * @param {K | N | undefined} beginRoot - The `beginRoot` parameter is optional and can be of - * type `K`, `N`, or `undefined`. It represents the starting point for finding the last key in + * @param {K | NODE | undefined} beginRoot - The `beginRoot` parameter is optional and can be of + * type `K`, `NODE`, or `undefined`. It represents the starting point for finding the last key in * the binary tree. If not provided, it defaults to the root of the binary tree (`this.root`). * @returns the key of the rightmost node in the binary tree if the comparison result is less than, * the key of the leftmost node if the comparison result is greater than, and the key of the * rightmost node otherwise. If no node is found, it returns 0. */ - lastKey(beginRoot: KeyOrNodeOrEntry = this.root): K | undefined { + lastKey(beginRoot: KeyOrNodeOrEntry = this.root): K | undefined { let current = this.ensureNode(beginRoot); if (!current) return undefined; @@ -691,12 +691,12 @@ export class BST< * are either lesser or greater than a target node, depending on the specified comparison type. * @param {C} callback - The `callback` parameter is a function that will be called for each node * that satisfies the condition specified by the `lesserOrGreater` parameter. It takes a single - * parameter of type `N` (the node type) and returns a value of any type. + * parameter of type `NODE` (the node type) and returns a value of any type. * @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to * traverse nodes that are lesser than, greater than, or equal to the `targetNode`. It is of type * `CP`, which is a custom type representing the comparison operator. The possible values for * `lesserOrGreater` are - * @param {K | N | undefined} targetNode - The `targetNode` parameter represents the node in the + * @param {K | NODE | undefined} targetNode - The `targetNode` parameter represents the node in the * binary tree that you want to traverse from. It can be specified either by its key, by the node * object itself, or it can be left undefined to start the traversal from the root of the tree. * @param iterationType - The `iterationType` parameter determines the type of traversal to be @@ -704,21 +704,21 @@ export class BST< * @returns The function `lesserOrGreaterTraverse` returns an array of values of type * `ReturnType`, which is the return type of the callback function passed as an argument. */ - lesserOrGreaterTraverse>( + lesserOrGreaterTraverse>( callback: C = this._defaultOneParamCallback as C, lesserOrGreater: CP = CP.lt, - targetNode: KeyOrNodeOrEntry = this.root, + targetNode: KeyOrNodeOrEntry = this.root, iterationType = this.iterationType ): ReturnType[] { targetNode = this.ensureNode(targetNode); - const ans: ReturnType>[] = []; + const ans: ReturnType>[] = []; if (!targetNode) return ans; if (!this.root) return ans; const targetKey = targetNode.key; if (iterationType === IterationType.RECURSIVE) { - const _traverse = (cur: N) => { + const _traverse = (cur: NODE) => { const compared = this._compare(cur.key, targetKey); if (compared === lesserOrGreater) ans.push(callback(cur)); @@ -730,7 +730,7 @@ export class BST< _traverse(this.root); return ans; } else { - const queue = new Queue([this.root]); + const queue = new Queue([this.root]); while (queue.size > 0) { const cur = queue.shift(); if (cur) { @@ -828,7 +828,7 @@ export class BST< let balanced = true; if (iterationType === IterationType.RECURSIVE) { - const _height = (cur: N | undefined): number => { + const _height = (cur: NODE | undefined): number => { if (!cur) return 0; const leftHeight = _height(cur.left), rightHeight = _height(cur.right); @@ -837,10 +837,10 @@ export class BST< }; _height(this.root); } else { - const stack: N[] = []; - let node: N | undefined = this.root, - last: N | undefined = undefined; - const depths: Map = new Map(); + const stack: NODE[] = []; + let node: NODE | undefined = this.root, + last: NODE | undefined = undefined; + const depths: Map = new Map(); while (stack.length > 0 || node) { if (node) { @@ -866,7 +866,7 @@ export class BST< return balanced; } - protected _setRoot(v: N | undefined) { + protected _setRoot(v: NODE | undefined) { if (v) { v.parent = undefined; } diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index cc577e3..7bf3cae 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -23,8 +23,8 @@ import { IBinaryTree } from '../../interfaces'; export class RedBlackTreeNode< K = any, V = any, - N extends RedBlackTreeNode = RedBlackTreeNodeNested -> extends BSTNode { + NODE extends RedBlackTreeNode = RedBlackTreeNodeNested +> extends BSTNode { color: RBTNColor; constructor(key: K, value?: V, color: RBTNColor = RBTNColor.BLACK) { @@ -43,17 +43,17 @@ export class RedBlackTreeNode< export class RedBlackTree< K = any, V = any, - N extends RedBlackTreeNode = RedBlackTreeNode>, - TREE extends RedBlackTree = RedBlackTree> + NODE extends RedBlackTreeNode = RedBlackTreeNode>, + TREE extends RedBlackTree = RedBlackTree> > - extends BST - implements IBinaryTree { - Sentinel: N = new RedBlackTreeNode(NaN as K) as unknown as N; + extends BST + implements IBinaryTree { + Sentinel: NODE = new RedBlackTreeNode(NaN as K) as unknown as NODE; /** * This is the constructor function for a Red-Black Tree data structure in TypeScript, which * initializes the tree with optional nodes and options. - * @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry` + * @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry` * objects. It represents the initial nodes that will be added to the RBTree during its * construction. If this parameter is provided, the `addMany` method is called to add all the * nodes to the @@ -61,16 +61,16 @@ export class RedBlackTree< * behavior of the RBTree. It is of type `Partial`, which means that you can provide * only a subset of the properties defined in the `RBTreeOptions` interface. */ - constructor(keysOrNodesOrEntries: Iterable> = [], options?: RBTreeOptions) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: RBTreeOptions) { super([], options); this._root = this.Sentinel; if (keysOrNodesOrEntries) super.addMany(keysOrNodesOrEntries); } - protected _root: N; + protected _root: NODE; - get root(): N { + get root(): NODE { return this._root; } @@ -92,8 +92,8 @@ export class RedBlackTree< * @returns The method is returning a new instance of a RedBlackTreeNode with the specified key, * value, and color. */ - override createNode(key: K, value?: V, color: RBTNColor = RBTNColor.BLACK): N { - return new RedBlackTreeNode(key, value, color) as N; + override createNode(key: K, value?: V, color: RBTNColor = RBTNColor.BLACK): NODE { + return new RedBlackTreeNode(key, value, color) as NODE; } /** @@ -104,7 +104,7 @@ export class RedBlackTree< * @returns a new instance of a RedBlackTree object. */ override createTree(options?: RBTreeOptions): TREE { - return new RedBlackTree([], { + return new RedBlackTree([], { iterationType: this.iterationType, ...options }) as TREE; @@ -112,14 +112,14 @@ export class RedBlackTree< /** * The function `keyValueOrEntryToNode` takes an keyOrNodeOrEntry and converts it into a node object if possible. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`, where: + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`, where: * @param {V} [value] - The `value` parameter is an optional value that can be passed to the * `keyValueOrEntryToNode` function. It represents the value associated with the keyOrNodeOrEntry node. If a value * is provided, it will be used when creating the new node. If no value is provided, the new node - * @returns a node of type N or undefined. + * @returns a node of type NODE or undefined. */ - override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): N | undefined { - let node: N | undefined; + override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): NODE | undefined { + let node: NODE | undefined; if (keyOrNodeOrEntry === null || keyOrNodeOrEntry === undefined) { return; @@ -142,11 +142,11 @@ export class RedBlackTree< /** * The function checks if an keyOrNodeOrEntry is an instance of the RedBlackTreeNode class. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the RedBlackTreeNode * class. */ - override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is N { + override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { return keyOrNodeOrEntry instanceof RedBlackTreeNode; } @@ -155,7 +155,7 @@ export class RedBlackTree< * Space Complexity: O(1) */ - override isRealNode(node: N | undefined): node is N { + override isRealNode(node: NODE | undefined): node is NODE { if (node === this.Sentinel || node === undefined) return false; return node instanceof RedBlackTreeNode; } @@ -176,17 +176,17 @@ export class RedBlackTree< * entry. * @param {V} [value] - The `value` parameter represents the value associated with the key that is * being added to the binary search tree. - * @returns The method `add` returns either the newly added node (`N`) or `undefined`. + * @returns The method `add` returns either the newly added node (`NODE`) or `undefined`. */ - override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { + override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V): boolean { const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value); if (newNode === undefined) return false; newNode.left = this.Sentinel; newNode.right = this.Sentinel; - let y: N | undefined = undefined; - let x: N | undefined = this.root; + let y: NODE | undefined = undefined; + let x: NODE | undefined = this.root; while (x !== this.Sentinel) { y = x; @@ -245,20 +245,20 @@ export class RedBlackTree< * that you want to use to identify the node that you want to delete from the binary tree. It can be * of any type that is returned by the callback function `C`. It can also be `null` or `undefined` if * you don't want to - * @param {C} callback - The `callback` parameter is a function that takes a node of type `N` and + * @param {C} callback - The `callback` parameter is a function that takes a node of type `NODE` and * returns a value of type `ReturnType`. It is used to determine if a node should be deleted based * on its identifier. The `callback` function is optional and defaults to `this._defaultOneParam - * @returns an array of `BinaryTreeDeleteResult`. + * @returns an array of `BinaryTreeDeleteResult`. */ - delete>( + delete>( identifier: ReturnType | null | undefined, callback: C = this._defaultOneParamCallback as C - ): BinaryTreeDeleteResult[] { - const ans: BinaryTreeDeleteResult[] = []; + ): BinaryTreeDeleteResult[] { + const ans: BinaryTreeDeleteResult[] = []; if (identifier === null) return ans; - const helper = (node: N | undefined): void => { - let z: N = this.Sentinel; - let x: N | undefined, y: N; + const helper = (node: NODE | undefined): void => { + let z: NODE = this.Sentinel; + let x: NODE | undefined, y: NODE; while (node !== this.Sentinel) { if (node && callback(node) === identifier) { z = node; @@ -311,26 +311,26 @@ export class RedBlackTree< return ans; } - getNode>( + getNode>( identifier: K, callback?: C, - beginRoot?: N | undefined, + beginRoot?: NODE | undefined, iterationType?: IterationType - ): N | undefined; + ): NODE | undefined; - getNode>( - identifier: N | undefined, + getNode>( + identifier: NODE | undefined, callback?: C, - beginRoot?: N | undefined, + beginRoot?: NODE | undefined, iterationType?: IterationType - ): N | undefined; + ): NODE | undefined; - getNode>( + getNode>( identifier: ReturnType, callback: C, - beginRoot?: N | undefined, + beginRoot?: NODE | undefined, iterationType?: IterationType - ): N | undefined; + ): NODE | undefined; /** * Time Complexity: O(log n) @@ -349,21 +349,21 @@ export class RedBlackTree< * node that matches the other criteria * @param {C} callback - The `callback` parameter is a function that will be called for each node in * the binary tree. It is used to determine if a node matches the given identifier. The `callback` - * function should take a single parameter of type `N` (the type of the nodes in the binary tree) and - * @param {K | N | undefined} beginRoot - The `beginRoot` parameter is the starting point for + * function should take a single parameter of type `NODE` (the type of the nodes in the binary tree) and + * @param {K | NODE | undefined} beginRoot - The `beginRoot` parameter is the starting point for * searching for a node in a binary tree. It can be either a key value or a node object. If it is not * provided, the search will start from the root of the binary tree. * @param iterationType - The `iterationType` parameter is a variable that determines the type of * iteration to be performed when searching for nodes in the binary tree. It is used in the * `getNodes` method, which is called within the `getNode` method. - * @returns a value of type `N`, `null`, or `undefined`. + * @returns a value of type `NODE`, `null`, or `undefined`. */ - getNode>( + getNode>( identifier: ReturnType | undefined, callback: C = this._defaultOneParamCallback as C, - beginRoot: BSTNKeyOrNode = this.root, + beginRoot: BSTNKeyOrNode = this.root, iterationType = this.iterationType - ): N | null | undefined { + ): NODE | null | undefined { if ((identifier as any) instanceof RedBlackTreeNode) callback = (node => node) as C; beginRoot = this.ensureNode(beginRoot); return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined; @@ -383,12 +383,12 @@ export class RedBlackTree< * Red-Black Tree. * @returns the predecessor of the given RedBlackTreeNode 'x'. */ - override getPredecessor(x: N): N { + override getPredecessor(x: NODE): NODE { if (this.isRealNode(x.left)) { return this.getRightMost(x.left)!; } - let y: N | undefined = x.parent; + let y: NODE | undefined = x.parent; while (this.isRealNode(y) && x === y.left) { x = y!; y = y!.parent; @@ -407,7 +407,7 @@ export class RedBlackTree< this._size = 0; } - protected override _setRoot(v: N) { + protected override _setRoot(v: NODE) { if (v) { v.parent = undefined; } @@ -424,11 +424,11 @@ export class RedBlackTree< * Space Complexity: O(1) * * The function performs a left rotation on a binary tree node. - * @param {RedBlackTreeNode} x - The parameter `x` is of type `N`, which likely represents a node in a binary tree. + * @param {RedBlackTreeNode} x - The parameter `x` is of type `NODE`, which likely represents a node in a binary tree. */ - protected _leftRotate(x: N): void { + protected _leftRotate(x: NODE): void { if (x.right) { - const y: N = x.right; + const y: NODE = x.right; x.right = y.left; if (y.left !== this.Sentinel) { if (y.left) y.left.parent = x; @@ -459,9 +459,9 @@ export class RedBlackTree< * @param {RedBlackTreeNode} x - x is a RedBlackTreeNode, which represents the node that needs to be right * rotated. */ - protected _rightRotate(x: N): void { + protected _rightRotate(x: NODE): void { if (x.left) { - const y: N = x.left; + const y: NODE = x.left; x.left = y.right; if (y.right !== this.Sentinel) { if (y.right) y.right.parent = x; @@ -491,8 +491,8 @@ export class RedBlackTree< * The function `_fixDelete` is used to fix the red-black tree after a node deletion. * @param {RedBlackTreeNode} x - The parameter `x` represents a node in a Red-Black Tree (RBT). */ - protected _fixDelete(x: N): void { - let s: N | undefined; + protected _fixDelete(x: NODE): void { + let s: NODE | undefined; while (x !== this.root && x.color === RBTNColor.BLACK) { if (x.parent && x === x.parent.left) { s = x.parent.right!; @@ -564,7 +564,7 @@ export class RedBlackTree< * @param {RedBlackTreeNode} u - The parameter "u" represents a RedBlackTreeNode object. * @param {RedBlackTreeNode} v - The parameter "v" is a RedBlackTreeNode object. */ - protected _rbTransplant(u: N, v: N): void { + protected _rbTransplant(u: NODE, v: NODE): void { if (u.parent === undefined) { this._setRoot(v); } else if (u === u.parent.left) { @@ -588,8 +588,8 @@ export class RedBlackTree< * @param {RedBlackTreeNode} k - The parameter `k` is a RedBlackTreeNode object, which represents a node in a * red-black tree. */ - protected _fixInsert(k: N): void { - let u: N | undefined; + protected _fixInsert(k: NODE): void { + let u: NODE | undefined; while (k.parent && k.parent.color === 1) { if (k.parent.parent && k.parent === k.parent.parent.right) { u = k.parent.parent.left; @@ -636,14 +636,14 @@ export class RedBlackTree< /** * The function replaces an old node with a new node while preserving the color of the old node. - * @param {N} oldNode - The `oldNode` parameter represents the node that needs to be replaced in a - * data structure. It is of type `N`, which is the type of the nodes in the data structure. - * @param {N} newNode - The `newNode` parameter is the node that will replace the `oldNode` in the + * @param {NODE} oldNode - The `oldNode` parameter represents the node that needs to be replaced in a + * data structure. It is of type `NODE`, which is the type of the nodes in the data structure. + * @param {NODE} newNode - The `newNode` parameter is the node that will replace the `oldNode` in the * data structure. * @returns The method is returning the result of calling the `_replaceNode` method from the * superclass, passing in the `oldNode` and `newNode` as arguments. */ - protected _replaceNode(oldNode: N, newNode: N): N { + protected _replaceNode(oldNode: NODE, newNode: NODE): NODE { newNode.color = oldNode.color; return super._replaceNode(oldNode, newNode); diff --git a/src/data-structures/binary-tree/tree-multimap.ts b/src/data-structures/binary-tree/tree-multimap.ts index 38c8656..76d8f2a 100644 --- a/src/data-structures/binary-tree/tree-multimap.ts +++ b/src/data-structures/binary-tree/tree-multimap.ts @@ -21,8 +21,8 @@ import { AVLTree, AVLTreeNode } from './avl-tree'; export class TreeMultimapNode< K = any, V = any, - N extends TreeMultimapNode = TreeMultimapNodeNested -> extends AVLTreeNode { + NODE extends TreeMultimapNode = TreeMultimapNodeNested +> extends AVLTreeNode { count: number; /** @@ -47,12 +47,12 @@ export class TreeMultimapNode< export class TreeMultimap< K = any, V = any, - N extends TreeMultimapNode = TreeMultimapNode>, - TREE extends TreeMultimap = TreeMultimap> + NODE extends TreeMultimapNode = TreeMultimapNode>, + TREE extends TreeMultimap = TreeMultimap> > - extends AVLTree - implements IBinaryTree { - constructor(keysOrNodesOrEntries: Iterable> = [], options?: TreeMultimapOptions) { + extends AVLTree + implements IBinaryTree { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: TreeMultimapOptions) { super([], options); if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } @@ -70,17 +70,17 @@ export class TreeMultimap< * The function creates a new BSTNode with the given key, value, and count. * @param {K} key - The key parameter is the unique identifier for the binary tree node. It is used to * distinguish one node from another in the tree. - * @param {N} value - The `value` parameter represents the value that will be stored in the binary search tree node. + * @param {NODE} value - The `value` parameter represents the value that will be stored in the binary search tree node. * @param {number} [count] - The "count" parameter is an optional parameter of type number. It represents the number of * occurrences of the value in the binary search tree node. If not provided, the count will default to 1. * @returns A new instance of the BSTNode class with the specified key, value, and count (if provided). */ - override createNode(key: K, value?: V, count?: number): N { - return new TreeMultimapNode(key, value, count) as N; + override createNode(key: K, value?: V, count?: number): NODE { + return new TreeMultimapNode(key, value, count) as NODE; } override createTree(options?: TreeMultimapOptions): TREE { - return new TreeMultimap([], { + return new TreeMultimap([], { iterationType: this.iterationType, variant: this.variant, ...options @@ -89,17 +89,21 @@ export class TreeMultimap< /** * The function `keyValueOrEntryToNode` converts an keyOrNodeOrEntry object into a node object. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`, which means it + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`, which means it * can be one of the following: * @param {V} [value] - The `value` parameter is an optional argument that represents the value * associated with the node. It is of type `V`, which can be any data type. If no value is provided, * it defaults to `undefined`. * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of * times the value should be added to the node. If not provided, it defaults to 1. - * @returns a node of type `N` or `undefined`. + * @returns a node of type `NODE` or `undefined`. */ - override keyValueOrEntryToNode(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V, count = 1): N | undefined { - let node: N | undefined; + override keyValueOrEntryToNode( + keyOrNodeOrEntry: KeyOrNodeOrEntry, + value?: V, + count = 1 + ): NODE | undefined { + let node: NODE | undefined; if (keyOrNodeOrEntry === undefined || keyOrNodeOrEntry === null) { return; } else if (this.isNode(keyOrNodeOrEntry)) { @@ -121,11 +125,11 @@ export class TreeMultimap< /** * The function checks if an keyOrNodeOrEntry is an instance of the TreeMultimapNode class. - * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the TreeMultimapNode * class. */ - override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is N { + override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { return keyOrNodeOrEntry instanceof TreeMultimapNode; } @@ -151,7 +155,7 @@ export class TreeMultimap< * @returns The method is returning either the newly inserted node or `undefined` if the insertion * was not successful. */ - override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V, count = 1): boolean { + override add(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V, count = 1): boolean { const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value, count); if (newNode === undefined) return false; @@ -177,9 +181,9 @@ export class TreeMultimap< * structure. * @param keysOrNodesOrEntries - The parameter `keysOrNodesOrEntries` is an iterable that can contain * either keys, nodes, or entries. - * @returns The method is returning an array of type `N | undefined`. + * @returns The method is returning an array of type `NODE | undefined`. */ - override addMany(keysOrNodesOrEntries: Iterable>): boolean[] { + override addMany(keysOrNodesOrEntries: Iterable>): boolean[] { return super.addMany(keysOrNodesOrEntries); } @@ -261,22 +265,22 @@ export class TreeMultimap< * being deleted. If set to true, the count of the node will not be considered and the node will be * deleted regardless of its count. If set to false (default), the count of the node will be * decremented by 1 and - * @returns an array of `BinaryTreeDeleteResult`. + * @returns an array of `BinaryTreeDeleteResult`. */ - override delete>( + override delete>( identifier: ReturnType, callback: C = this._defaultOneParamCallback as C, ignoreCount = false - ): BinaryTreeDeleteResult[] { - const deletedResult: BinaryTreeDeleteResult[] = []; + ): BinaryTreeDeleteResult[] { + const deletedResult: BinaryTreeDeleteResult[] = []; if (!this.root) return deletedResult; - const curr: N | undefined = this.getNode(identifier, callback) ?? undefined; + const curr: NODE | undefined = this.getNode(identifier, callback) ?? undefined; if (!curr) return deletedResult; - const parent: N | undefined = curr?.parent ? curr.parent : undefined; - let needBalanced: N | undefined = undefined, - orgCurrent: N | undefined = curr; + const parent: NODE | undefined = curr?.parent ? curr.parent : undefined; + let needBalanced: NODE | undefined = undefined, + orgCurrent: NODE | undefined = curr; if (curr.count > 1 && !ignoreCount) { curr.count--; @@ -359,14 +363,17 @@ export class TreeMultimap< /** * The `_swapProperties` function swaps the key, value, count, and height properties between two nodes. - * @param {K | N | undefined} srcNode - The `srcNode` parameter represents the source node from - * which the values will be swapped. It can be of type `K`, `N`, or `undefined`. - * @param {K | N | undefined} destNode - The `destNode` parameter represents the destination + * @param {K | NODE | undefined} srcNode - The `srcNode` parameter represents the source node from + * which the values will be swapped. It can be of type `K`, `NODE`, or `undefined`. + * @param {K | NODE | undefined} destNode - The `destNode` parameter represents the destination * node where the values from the source node will be swapped to. * @returns either the `destNode` object if both `srcNode` and `destNode` are defined, or `undefined` * if either `srcNode` or `destNode` is undefined. */ - protected override _swapProperties(srcNode: BSTNKeyOrNode, destNode: BSTNKeyOrNode): N | undefined { + protected override _swapProperties( + srcNode: BSTNKeyOrNode, + destNode: BSTNKeyOrNode + ): NODE | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); if (srcNode && destNode) { @@ -391,7 +398,7 @@ export class TreeMultimap< return undefined; } - protected _replaceNode(oldNode: N, newNode: N): N { + protected _replaceNode(oldNode: NODE, newNode: NODE): NODE { newNode.count = oldNode.count + newNode.count; return super._replaceNode(oldNode, newNode); } diff --git a/src/data-structures/graph/abstract-graph.ts b/src/data-structures/graph/abstract-graph.ts index cb70033..3421d79 100644 --- a/src/data-structures/graph/abstract-graph.ts +++ b/src/data-structures/graph/abstract-graph.ts @@ -78,6 +78,10 @@ export abstract class AbstractGraph< return this._vertexMap; } + set vertexMap(v: Map) { + this._vertexMap = v; + } + /** * In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it. * This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden. diff --git a/src/data-structures/graph/directed-graph.ts b/src/data-structures/graph/directed-graph.ts index f9c2ff0..fbaf26d 100644 --- a/src/data-structures/graph/directed-graph.ts +++ b/src/data-structures/graph/directed-graph.ts @@ -66,12 +66,20 @@ export class DirectedGraph< return this._outEdgeMap; } + set outEdgeMap(v: Map) { + this._outEdgeMap = v; + } + protected _inEdgeMap: Map = new Map(); get inEdgeMap(): Map { return this._inEdgeMap; } + set inEdgeMap(v: Map) { + this._inEdgeMap = v; + } + /** * In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it. * This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden. @@ -599,6 +607,28 @@ export class DirectedGraph< } } + /** + * The isEmpty function checks if the graph is empty. + * + * @return A boolean value + */ + isEmpty(): boolean { + return this.vertexMap.size === 0 && this.inEdgeMap.size === 0 && this.outEdgeMap.size === 0; + } + + /** + * The clone function creates a new DirectedGraph object with the same vertices and edges as the original. + * + * @return A new instance of the directedgraph class + */ + clone(): DirectedGraph { + const cloned = new DirectedGraph(); + cloned.vertexMap = new Map(this.vertexMap); + cloned.inEdgeMap = new Map(this.inEdgeMap); + cloned.outEdgeMap = new Map(this.outEdgeMap); + return cloned; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) diff --git a/src/data-structures/graph/map-graph.ts b/src/data-structures/graph/map-graph.ts index 8979f4e..370198a 100644 --- a/src/data-structures/graph/map-graph.ts +++ b/src/data-structures/graph/map-graph.ts @@ -108,4 +108,19 @@ export class MapGraph< override createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO { return new MapEdge(src, dest, weight, value) as EO; } + + /** + * The override function is used to override the default behavior of a function. + * In this case, we are overriding the clone() function from Graph<V, E>. + * The clone() function returns a new graph that is an exact copy of the original graph. + * + * @return A mapgraph<v, e, vo, eo> + */ + override clone(): MapGraph { + const cloned = new MapGraph(this.originCoord, this.bottomRight); + cloned.vertexMap = new Map(this.vertexMap); + cloned.inEdgeMap = new Map(this.inEdgeMap); + cloned.outEdgeMap = new Map(this.outEdgeMap); + return cloned; + } } diff --git a/src/data-structures/graph/undirected-graph.ts b/src/data-structures/graph/undirected-graph.ts index 20cba16..86b29e2 100644 --- a/src/data-structures/graph/undirected-graph.ts +++ b/src/data-structures/graph/undirected-graph.ts @@ -64,6 +64,10 @@ export class UndirectedGraph< return this._edgeMap; } + set edgeMap(v: Map) { + this._edgeMap = v; + } + /** * The function creates a new vertex with an optional value and returns it. * @param {VertexKey} key - The `key` parameter is the unique identifier for the vertex. It is used to distinguish one @@ -365,6 +369,30 @@ export class UndirectedGraph< } } + /** + * The isEmpty function checks if the graph is empty. + * @return True if the graph is empty and false otherwise + */ + isEmpty(): boolean { + return this.vertexMap.size === 0 && this.edgeMap.size === 0; + } + + /** + * The clone function creates a new UndirectedGraph object and copies the + * vertexMap and edgeMap from this graph to the new one. This is done by + * assigning each of these properties to their respective counterparts in the + * cloned graph. The clone function returns a reference to this newly created, + * cloned UndirectedGraph object. + * + * @return A new instance of the undirectedgraph class + */ + clone(): UndirectedGraph { + const cloned = new UndirectedGraph(); + cloned.vertexMap = new Map(this.vertexMap); + cloned.edgeMap = new Map(this.edgeMap); + return cloned; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) diff --git a/src/data-structures/hash/hash-map.ts b/src/data-structures/hash/hash-map.ts index 17ea30d..5b98421 100644 --- a/src/data-structures/hash/hash-map.ts +++ b/src/data-structures/hash/hash-map.ts @@ -32,7 +32,7 @@ export class HashMap extends IterableEntryBase = [], options?: HashMapOptions) { + constructor(rawCollection: Iterable = [], options?: HashMapOptions) { super(); if (options) { const { hashFn, toEntryFn } = options; @@ -67,6 +67,16 @@ export class HashMap extends IterableEntryBase extends IterableEntryBase): boolean[] { + setMany(rawCollection: Iterable): boolean[] { const results: boolean[] = []; for (const rawEle of rawCollection) { - const [key, value] = this.toEntryFn(rawEle); + let key, value; + if (this.isEntry(rawEle)) { + key = rawEle[0]; + value = rawEle[1]; + } else { + const item = this.toEntryFn(rawEle); + key = item[0]; + value = item[1]; + } results.push(this.set(key, value)); } return results; @@ -203,6 +221,17 @@ export class HashMap extends IterableEntryBase { + return new HashMap(this, { hashFn: this._hashFn, toEntryFn: this.toEntryFn }); + } + /** * Time Complexity: O(n) * Space Complexity: O(n) diff --git a/src/data-structures/queue/queue.ts b/src/data-structures/queue/queue.ts index 8e7d125..8cb7d82 100644 --- a/src/data-structures/queue/queue.ts +++ b/src/data-structures/queue/queue.ts @@ -150,6 +150,26 @@ export class Queue extends IterableElementBase { return first; } + /** + * The delete function removes an element from the list. + * @param element: E Specify the element to be deleted + * @return A boolean value indicating whether the element was successfully deleted or not + */ + delete(element: E): boolean { + const index = this.elements.indexOf(element); + return this.deleteAt(index); + } + + /** + * The deleteAt function deletes the element at a given index. + * @param index: number Determine the index of the element to be deleted + * @return A boolean value + */ + deleteAt(index: number): boolean { + const spliced = this.elements.splice(index, 1); + return spliced.length === 1; + } + /** * Time Complexity: O(1) - constant time as it retrieves the value at the current offset. * Space Complexity: O(1) - no additional space is used. diff --git a/src/data-structures/stack/stack.ts b/src/data-structures/stack/stack.ts index ee11149..12cb0a5 100644 --- a/src/data-structures/stack/stack.ts +++ b/src/data-structures/stack/stack.ts @@ -129,6 +129,26 @@ export class Stack extends IterableElementBase { return this.elements.pop(); } + /** + * The delete function removes an element from the stack. + * @param element: E Specify the element to be deleted + * @return A boolean value indicating whether the element was successfully deleted or not + */ + delete(element: E): boolean { + const index = this.elements.indexOf(element); + return this.deleteAt(index); + } + + /** + * The deleteAt function deletes the element at a given index. + * @param index: number Determine the index of the element to be deleted + * @return A boolean value + */ + deleteAt(index: number): boolean { + const spliced = this.elements.splice(index, 1); + return spliced.length === 1; + } + /** * Time Complexity: O(n) * Space Complexity: O(n) diff --git a/src/data-structures/trie/trie.ts b/src/data-structures/trie/trie.ts index 6894e90..649271b 100644 --- a/src/data-structures/trie/trie.ts +++ b/src/data-structures/trie/trie.ts @@ -37,7 +37,7 @@ export class TrieNode { * 10. IP Routing: Used in certain types of IP routing algorithms. * 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data." */ -export class Trie extends IterableElementBase { +export class Trie extends IterableElementBase { /** * The constructor function for the Trie class. * @param words: Iterable string Initialize the trie with a set of words @@ -143,6 +143,14 @@ export class Trie extends IterableElementBase { return cur.isEnd; } + /** + * The isEmpty function checks if the size of the queue is 0. + * @return True if the size of the queue is 0 + */ + isEmpty(): boolean { + return this.size === 0; + } + /** * Time Complexity: O(M), where M is the length of the word being deleted. * Space Complexity: O(M) - Due to the recursive DFS approach. diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index 0d86710..1fb398a 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -3,7 +3,7 @@ import { IterationType } from "../../common"; export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type BinaryTreeNested> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BinaryTreeNested> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BinaryTreeOptions = { iterationType?: IterationType, diff --git a/src/types/utils/utils.ts b/src/types/utils/utils.ts index 1f3a505..46923fd 100644 --- a/src/types/utils/utils.ts +++ b/src/types/utils/utils.ts @@ -4,3 +4,5 @@ export type TrlFn = (...args: any[]) => any; export type TrlAsyncFn = (...args: any[]) => any; export type SpecifyOptional = Omit & Partial>; + +export type Any = string | number | boolean | object | null | undefined | symbol; diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index 90f6921..334a47e 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -235,6 +235,43 @@ describe('AVLTree APIs test', () => { const bfsRes = avl.bfs(node => node.key); expect(bfsRes[0]).toBe(2); }); + + it('should the clone method', () => { + function checkTreeStructure(avl: AVLTree) { + expect(avl.size).toBe(4); + expect(avl.root?.key).toBe('2'); + expect(avl.root?.left?.key).toBe('1'); + expect(avl.root?.left?.left?.key).toBe(undefined); + expect(avl.root?.left?.right?.key).toBe(undefined); + expect(avl.root?.right?.key).toBe('4'); + expect(avl.root?.right?.left?.key).toBe(undefined); + expect(avl.root?.right?.right?.key).toBe('5'); + } + + const avl = new AVLTree(); + avl.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(avl.size).toBe(5); + expect(avl.root?.key).toBe('2'); + expect(avl.root?.left?.key).toBe('1'); + expect(avl.root?.left?.left?.key).toBe(undefined); + expect(avl.root?.left?.right?.key).toBe(undefined); + expect(avl.root?.right?.key).toBe('4'); + expect(avl.root?.right?.left?.key).toBe('3'); + expect(avl.root?.right?.right?.key).toBe('5'); + avl.delete('3'); + checkTreeStructure(avl); + const cloned = avl.clone(); + checkTreeStructure(cloned); + cloned.delete('1'); + expect(avl.size).toBe(4); + expect(cloned.size).toBe(3); + }); }); describe('AVLTree', () => { diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index dbbd02a..b80d909 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -152,6 +152,51 @@ describe('BinaryTree', () => { expect(tree.has('3', node => node.value?.toString())).toBe(true); }); + it('should the clone method work fine', () => { + expect(tree.isEmpty()).toBe(true); + tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); + expect(tree.root?.key).toBe(4); + expect(tree.root?.left?.key).toBe(2); + expect(tree.root?.left?.left).toBe(null); + expect(tree.root?.left?.right?.key).toBe(1); + expect(tree.root?.right?.key).toBe(6); + expect(tree.root?.right?.left?.key).toBe(3); + expect(tree.root?.right?.right).toBe(null); + + const cloned = tree.clone(); + expect(cloned.root?.key).toBe(4); + expect(cloned.root?.left?.key).toBe(2); + expect(cloned.root?.left?.left).toBe(null); + expect(cloned.root?.left?.right?.key).toBe(1); + expect(cloned.root?.right?.key).toBe(6); + expect(cloned.root?.right?.left?.key).toBe(3); + expect(cloned.root?.right?.right).toBe(null); + expect(cloned.dfs(node => node.key, 'pre', cloned.getNode(6), IterationType.ITERATIVE)).toEqual([6, 3, 7]); + expect( + cloned.dfs(node => (node ? node.key : null), 'pre', cloned.getNode(6), IterationType.ITERATIVE, true) + ).toEqual([6, 3, 7, null]); + expect( + cloned.dfs(node => (node ? node.key : node), 'pre', cloned.getNode(6), IterationType.ITERATIVE, true) + ).toEqual([6, 3, 7, null]); + expect( + cloned.dfs(node => (node ? node.key : null), 'pre', cloned.getNode(6), IterationType.RECURSIVE, true) + ).toEqual([6, 3, 7, null]); + cloned.delete(6); + cloned.delete(3); + cloned.delete(7); + cloned.delete(1); + cloned.delete(5); + cloned.delete(4); + cloned.delete(2); + // cloned.delete(null); + // cloned.delete(null); + // cloned.delete(null); + expect(tree.size).toBe(10); + expect(cloned.size).toBe(3); + // expect(cloned.size).toBe(0); + // expect(cloned.isEmpty()).toBe(true); + }); + it('should be a balance tree after malicious manipulation', () => { tree.add(3); tree.add(12); @@ -238,22 +283,6 @@ describe('BinaryTree', () => { expect(tree.getNodes(tree.getNodeByKey(2), undefined, false, tree.root)).toEqual([tree.getNodeByKey(2)]); }); - // it('should subTreeTraverse', () => { - // tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); - // expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.ITERATIVE)).toEqual([6, 3, 7]); - // expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.ITERATIVE, false)).toEqual([6, 3, 7]); - // expect(tree.subTreeTraverse(node => node.key, tree.getNode(6), IterationType.RECURSIVE)).toEqual([6, 3, 7]); - // expect( - // tree.subTreeTraverse(node => (node ? node.key : null), tree.getNode(6), IterationType.ITERATIVE, true) - // ).toEqual([6, 3, 7, null]); - // expect( - // tree.subTreeTraverse(node => (node ? node.key : node), tree.getNode(6), IterationType.ITERATIVE, true) - // ).toEqual([6, 3, 7, null]); - // expect( - // tree.subTreeTraverse(node => (node ? node.key : null), tree.getNode(6), IterationType.RECURSIVE, true) - // ).toEqual([6, 3, 7, null]); - // }); - it('should sub tree traverse', () => { tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); expect(tree.dfs(node => node.key, 'pre', tree.getNode(6), IterationType.ITERATIVE)).toEqual([6, 3, 7]); @@ -613,7 +642,7 @@ describe('BinaryTree', () => { tree.delete(5); tree.delete(7); tree.delete(3); - expect(tree.root).toBe(null); + expect(tree.root).toBe(undefined); expect(tree.getHeight()).toBe(-1); }); }); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 0680666..f49c347 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -795,6 +795,70 @@ describe('BST operations test recursively', () => { expect(bfsNodes[1].key).toBe(12); expect(bfsNodes[2].key).toBe(16); }); + + it('should delete', () => { + const numBST = new BST(); + numBST.addMany([2, 4, 5, 3, 1]); + expect(numBST.size).toBe(5); + numBST.delete(1); + expect(numBST.size).toBe(4); + + numBST.delete(2); + numBST.delete(3); + numBST.delete(4); + numBST.delete(5); + expect(numBST.size).toBe(0); + numBST.delete(5); + expect(numBST.size).toBe(0); + }); + + it('should the clone method', () => { + function checkTreeStructure(bst: BST) { + expect(bst.size).toBe(4); + expect(bst.root?.key).toBe('2'); + expect(bst.root?.left?.key).toBe('1'); + expect(bst.root?.left?.left?.key).toBe(undefined); + expect(bst.root?.left?.right?.key).toBe(undefined); + expect(bst.root?.right?.key).toBe('4'); + expect(bst.root?.right?.left?.key).toBe(undefined); + expect(bst.root?.right?.right?.key).toBe('5'); + } + + const bst = new BST(); + bst.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(bst.size).toBe(5); + expect(bst.root?.key).toBe('3'); + expect(bst.root?.left?.key).toBe('1'); + expect(bst.root?.left?.left?.key).toBe(undefined); + expect(bst.root?.left?.right?.key).toBe('2'); + expect(bst.root?.right?.key).toBe('4'); + expect(bst.root?.right?.left?.key).toBe(undefined); + expect(bst.root?.right?.right?.key).toBe('5'); + bst.delete('3'); + checkTreeStructure(bst); + const cloned = bst.clone(); + checkTreeStructure(cloned); + bst.delete('2'); + bst.delete('1'); + bst.delete('4'); + bst.delete('5'); + expect(bst.size).toBe(0); + + cloned.delete('1'); + expect(bst.size).toBe(0); + expect(cloned.size).toBe(3); + cloned.delete('2'); + cloned.delete('3'); + cloned.delete('4'); + cloned.delete('5'); + expect(cloned.size).toBe(0); + }); }); describe('BST isBST', function () { @@ -865,7 +929,7 @@ describe('BST Performance test', function () { // expect(bst.lastKey()).toBe(9); }); - it('should subTreeTraverse, null should be ignored', () => { + it('should dfs as sub tree traversal, null should be ignored', () => { const bst = new BST(); bst.addMany([4, 2, 6, 1, 3, 5, 7]); expect(bst.dfs(node => node.key, 'pre', bst.getNode(6), IterationType.ITERATIVE)).toEqual([6, 5, 7]); diff --git a/test/unit/data-structures/binary-tree/rb-tree.test.ts b/test/unit/data-structures/binary-tree/rb-tree.test.ts index bfda59c..749b70d 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -138,9 +138,46 @@ describe('RedBlackTree', () => { expect(predecessorNode).toBe(tree.getNode(10)); }); }); + + it('should the clone method', () => { + function checkTreeStructure(rbTree: RedBlackTree) { + expect(rbTree.size).toBe(4); + expect(rbTree.root?.key).toBe('2'); + expect(rbTree.root?.left?.key).toBe('1'); + expect(rbTree.root?.left?.left?.key).toBe(NaN); + expect(rbTree.root?.left?.right?.key).toBe(NaN); + expect(rbTree.root?.right?.key).toBe('4'); + expect(rbTree.root?.right?.left?.key).toBe(NaN); + expect(rbTree.root?.right?.right?.key).toBe('5'); + } + + const rbTree = new RedBlackTree(); + rbTree.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(rbTree.size).toBe(5); + expect(rbTree.root?.key).toBe('2'); + expect(rbTree.root?.left?.key).toBe('1'); + expect(rbTree.root?.left?.left?.key).toBe(NaN); + expect(rbTree.root?.left?.right?.key).toBe(NaN); + expect(rbTree.root?.right?.key).toBe('4'); + expect(rbTree.root?.right?.left?.key).toBe('3'); + expect(rbTree.root?.right?.right?.key).toBe('5'); + rbTree.delete('3'); + checkTreeStructure(rbTree); + const cloned = rbTree.clone(); + checkTreeStructure(cloned); + cloned.delete('1'); + expect(rbTree.size).toBe(4); + expect(cloned.size).toBe(3); + }); }); -describe('RedBlackTree', () => { +describe('RedBlackTree 2', () => { let tree: RedBlackTree; beforeEach(() => { diff --git a/test/unit/data-structures/binary-tree/tree-multimap.test.ts b/test/unit/data-structures/binary-tree/tree-multimap.test.ts index 02c314b..db3c97f 100644 --- a/test/unit/data-structures/binary-tree/tree-multimap.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multimap.test.ts @@ -555,38 +555,6 @@ describe('TreeMultimap Performance test', function () { beforeEach(() => { treeMS.clear(); }); - it(`Observe the time consumption of TreeMultimap.add fitting O(n log n)`, function () { - // // Create a benchmark suite - // const suite = new Benchmark.Suite(); - // // Define a function to generate a random array of a given size - // function generateRandomArray(size: number): number[] { - // const arr: number[] = []; - // for (let i = 0; i < size; i++) { - // arr.push(Math.floor(Math.random() * size)); - // } - // return arr; - // } - // const inputArray = generateRandomArray(inputSize[0]); - // - // suite.add(`TreeMultimap addMany (n=${inputSize[0]})`, () => { - // treeMS.addMany([...inputArray]); - // }); - // - // // Run the benchmarks - // suite - // .on('cycle', (event: any) => { - // const benchmark = event.target; - // const n = parseInt(benchmark.name.split('=')[1]); - // const observedTime = benchmark.times.elapsed; - // const expected = expectedTime(n); - // console.log(`Input size (n): ${n}, Observed time: ${observedTime.toFixed(2)}ms, Expected time: ${expected.toFixed(2)}ms`); - // }) - // .on('complete', () => { - // console.log(`Benchmark (n=${inputSize[0]}) completed.`); - // done(); // Call done to indicate the test is complete - // }) - // .run({async: true}); - }); it(`Observe the time consumption of TreeMultimap.dfs be good`, function () { const startDFS = performance.now(); @@ -604,6 +572,43 @@ describe('TreeMultimap Performance test', function () { treeMS.lesserOrGreaterTraverse(node => (node.count += 1), CP.lt, inputSize / 2); isDebug && console.log('---lesserOrGreaterTraverse', performance.now() - startL); }); + + it('should the clone method', () => { + function checkTreeStructure(treeMultimap: TreeMultimap) { + expect(treeMultimap.size).toBe(4); + expect(treeMultimap.root?.key).toBe('2'); + expect(treeMultimap.root?.left?.key).toBe('1'); + expect(treeMultimap.root?.left?.left?.key).toBe(undefined); + expect(treeMultimap.root?.left?.right?.key).toBe(undefined); + expect(treeMultimap.root?.right?.key).toBe('4'); + expect(treeMultimap.root?.right?.left?.key).toBe(undefined); + expect(treeMultimap.root?.right?.right?.key).toBe('5'); + } + + const treeMultimap = new TreeMultimap(); + treeMultimap.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(treeMultimap.size).toBe(5); + expect(treeMultimap.root?.key).toBe('2'); + expect(treeMultimap.root?.left?.key).toBe('1'); + expect(treeMultimap.root?.left?.left?.key).toBe(undefined); + expect(treeMultimap.root?.left?.right?.key).toBe(undefined); + expect(treeMultimap.root?.right?.key).toBe('4'); + expect(treeMultimap.root?.right?.left?.key).toBe('3'); + expect(treeMultimap.root?.right?.right?.key).toBe('5'); + treeMultimap.delete('3'); + checkTreeStructure(treeMultimap); + const cloned = treeMultimap.clone(); + checkTreeStructure(cloned); + cloned.delete('1'); + expect(treeMultimap.size).toBe(4); + expect(cloned.size).toBe(3); + }); }); describe('TreeMultimap iterative methods test', () => { diff --git a/test/unit/data-structures/graph/abstract-graph.test.ts b/test/unit/data-structures/graph/abstract-graph.test.ts index 6c634b6..2baa63d 100644 --- a/test/unit/data-structures/graph/abstract-graph.test.ts +++ b/test/unit/data-structures/graph/abstract-graph.test.ts @@ -77,6 +77,14 @@ class MyGraph< return true; } + isEmpty(): boolean { + return true; + } + + clone(): any { + return {}; + } + protected _addEdge(edge: EO): boolean { return edge ? true : true; } diff --git a/test/unit/data-structures/hash/hash-map.test.ts b/test/unit/data-structures/hash/hash-map.test.ts index 47c262e..21f2f1f 100644 --- a/test/unit/data-structures/hash/hash-map.test.ts +++ b/test/unit/data-structures/hash/hash-map.test.ts @@ -85,6 +85,29 @@ describe('HashMap', () => { // expect(hashMap.table[0].length).toBe(2); }); + it('should clone', () => { + hashMap = new HashMap(); + + hashMap.set('one', 1); + hashMap.set('two', 2); + for (let i = 3; i <= 100; i++) { + hashMap.set(i.toString(), i); + } + + expect(hashMap.get('one')).toBe(1); + expect(hashMap.get('two')).toBe(2); + expect(hashMap.get('86')).toBe(86); + expect(hashMap.size).toBe(100); + hashMap.delete('two'); + expect(hashMap.size).toBe(99); + + const cloned = hashMap.clone(); + expect(cloned.get('one')).toBe(1); + expect(cloned.get('two')).toBe(undefined); + expect(cloned.get('86')).toBe(86); + expect(cloned.size).toBe(99); + }); + describe('HashMap Test2', () => { let hashMap: HashMap; diff --git a/test/unit/data-structures/heap/heap.test.ts b/test/unit/data-structures/heap/heap.test.ts index 8b49082..3cb16ed 100644 --- a/test/unit/data-structures/heap/heap.test.ts +++ b/test/unit/data-structures/heap/heap.test.ts @@ -1,4 +1,4 @@ -import { FibonacciHeap, MaxHeap, MinHeap } from '../../../../src'; +import { FibonacciHeap, Heap, MaxHeap, MinHeap } from '../../../../src'; import { logBigOMetricsWrap } from '../../../utils'; describe('Heap Operation Test', () => { @@ -26,6 +26,23 @@ describe('Heap Operation Test', () => { expect(minNumHeap.sort()).toEqual([2, 5, 6, 9]); }); + it('should clone', function () { + const minNumHeap = new Heap([], { comparator: (a, b) => Number(a) - Number(b) }); + minNumHeap.add('1'); + minNumHeap.add('6'); + minNumHeap.add('2'); + minNumHeap.add('0'); + minNumHeap.add('5'); + minNumHeap.add('9'); + minNumHeap.delete('2'); + expect([...minNumHeap]).toEqual(['0', '1', '9', '6', '5']); + const cloned = minNumHeap.clone(); + expect([...cloned]).toEqual(['0', '1', '9', '6', '5']); + minNumHeap.delete('5'); + expect([...minNumHeap]).toEqual(['0', '1', '9', '6']); + expect([...cloned]).toEqual(['0', '1', '9', '6', '5']); + }); + it('should object heap work well', function () { const minHeap = new MinHeap<{ a: string; key: number }>([], { comparator: (a, b) => a.key - b.key }); minHeap.add({ key: 1, a: 'a1' }); diff --git a/test/unit/data-structures/linked-list/doubly-linked-list.test.ts b/test/unit/data-structures/linked-list/doubly-linked-list.test.ts index bc6cd6f..584cecf 100644 --- a/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +++ b/test/unit/data-structures/linked-list/doubly-linked-list.test.ts @@ -37,6 +37,23 @@ describe('DoublyLinkedList Operation Test', () => { expect(list.tail?.value).toBe(4); }); + it('should clone', function () { + const dList = new DoublyLinkedList(); + dList.addLast('1'); + dList.addLast('6'); + dList.addLast('2'); + dList.addLast('0'); + dList.addLast('5'); + dList.addLast('9'); + dList.delete('2'); + expect([...dList]).toEqual(['1', '6', '0', '5', '9']); + const cloned = dList.clone(); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + dList.delete('5'); + expect([...dList]).toEqual(['1', '6', '0', '9']); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + }); + it('should find undefined', () => { expect(list.find(value => value === 6)).toBe(undefined); expect(list.find(value => value === 4)).toBe(4); diff --git a/test/unit/data-structures/linked-list/singly-linked-list.test.ts b/test/unit/data-structures/linked-list/singly-linked-list.test.ts index ac7e7aa..c8743a3 100644 --- a/test/unit/data-structures/linked-list/singly-linked-list.test.ts +++ b/test/unit/data-structures/linked-list/singly-linked-list.test.ts @@ -348,6 +348,23 @@ describe('SinglyLinkedList Operation Test', () => { }); }); + it('should clone', function () { + const sList = new SinglyLinkedList(); + sList.addLast('1'); + sList.addLast('6'); + sList.addLast('2'); + sList.addLast('0'); + sList.addLast('5'); + sList.addLast('9'); + sList.delete('2'); + expect([...sList]).toEqual(['1', '6', '0', '5', '9']); + const cloned = sList.clone(); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + sList.delete('5'); + expect([...sList]).toEqual(['1', '6', '0', '9']); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + }); + describe('countOccurrences', () => { it('should count occurrences of a value', () => { list.push(1); diff --git a/test/unit/data-structures/linked-list/skip-list.test.ts b/test/unit/data-structures/linked-list/skip-list.test.ts index 435758e..c8c9d34 100644 --- a/test/unit/data-structures/linked-list/skip-list.test.ts +++ b/test/unit/data-structures/linked-list/skip-list.test.ts @@ -54,7 +54,7 @@ describe('SkipList', () => { }); }); -describe('SkipList', () => { +describe('SkipList Test2', () => { let skipList: SkipList; beforeEach(() => { diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index 2e87965..e879c99 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -49,6 +49,23 @@ describe('Deque - Basic Operations', () => { deque.setAt(0, 3); expect(deque.at(0)).toBe(3); }); + + it('should clone', function () { + const deque = new Deque(); + deque.addLast('1'); + deque.addLast('6'); + deque.addLast('2'); + deque.addLast('0'); + deque.addLast('5'); + deque.addLast('9'); + deque.delete('2'); + expect([...deque]).toEqual(['1', '6', '0', '5', '9']); + const cloned = deque.clone(); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + deque.delete('5'); + expect([...deque]).toEqual(['1', '6', '0', '9']); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + }); }); describe('Deque - Complex Operations', () => { let deque: Deque; diff --git a/test/unit/data-structures/queue/queue.test.ts b/test/unit/data-structures/queue/queue.test.ts index 5502dc6..5d4b216 100644 --- a/test/unit/data-structures/queue/queue.test.ts +++ b/test/unit/data-structures/queue/queue.test.ts @@ -89,6 +89,23 @@ describe('Queue', () => { } expect(queue.isEmpty()).toBeTruthy(); }); + + it('should clone', function () { + const queue = new Queue(); + queue.push('1'); + queue.push('6'); + queue.push('2'); + queue.push('0'); + queue.push('5'); + queue.push('9'); + queue.delete('2'); + expect([...queue]).toEqual(['1', '6', '0', '5', '9']); + const cloned = queue.clone(); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + queue.delete('5'); + expect([...queue]).toEqual(['1', '6', '0', '9']); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + }); }); describe('Queue - Advanced Methods', () => { diff --git a/test/unit/data-structures/stack/stack.test.ts b/test/unit/data-structures/stack/stack.test.ts index cacd97b..4cc1725 100644 --- a/test/unit/data-structures/stack/stack.test.ts +++ b/test/unit/data-structures/stack/stack.test.ts @@ -35,6 +35,23 @@ describe('Stack', () => { expect(stack.size).toBe(2); }); + it('should clone', function () { + const stack = new Stack(); + stack.push('1'); + stack.push('6'); + stack.push('2'); + stack.push('0'); + stack.push('5'); + stack.push('9'); + stack.delete('2'); + expect([...stack]).toEqual(['1', '6', '0', '5', '9']); + const cloned = stack.clone(); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + stack.delete('5'); + expect([...stack]).toEqual(['1', '6', '0', '9']); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + }); + it('should return undefined when popping from an empty stack', () => { const poppedElement = stack.pop(); expect(poppedElement).toBe(undefined); diff --git a/test/unit/data-structures/trie/trie.test.ts b/test/unit/data-structures/trie/trie.test.ts index 32a1e19..abfef8d 100644 --- a/test/unit/data-structures/trie/trie.test.ts +++ b/test/unit/data-structures/trie/trie.test.ts @@ -56,6 +56,23 @@ describe('Trie', () => { expect(trie.hasPrefix('banana')).toBe(false); }); + it('should clone', function () { + const trie = new Trie(); + trie.add('1'); + trie.add('6'); + trie.add('2'); + trie.add('0'); + trie.add('5'); + trie.add('9'); + trie.delete('2'); + expect([...trie]).toEqual(['1', '6', '0', '5', '9']); + const cloned = trie.clone(); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + trie.delete('5'); + expect([...trie]).toEqual(['1', '6', '0', '9']); + expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + }); + it('should check if a string is a common prefix', () => { const trie = new Trie(); trie.add('apple');