diff --git a/README.md b/README.md index 9813be8..84505ad 100644 --- a/README.md +++ b/README.md @@ -728,40 +728,40 @@ optimal approach to data structure design. [//]: # (No deletion!!! Start of Replace Section)
avl-tree
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly31.9531.302.69e-4
10,000 add & delete randomly69.1814.458.01e-4
10,000 addMany41.6224.032.25e-4
10,000 get27.6736.131.96e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 add randomly45.4821.990.04
10,000 add & delete randomly132.357.560.10
10,000 addMany79.2212.620.07
10,000 get93.1310.740.06
binary-tree
-
test nametime taken (ms)executions per secsample deviation
1,000 add randomly12.2981.389.60e-5
1,000 add & delete randomly15.6064.111.04e-4
1,000 addMany10.3097.049.16e-5
1,000 get18.0255.502.21e-4
1,000 dfs175.425.700.00
1,000 bfs55.7117.952.56e-4
1,000 morris38.2926.112.10e-4
+
test nametime taken (ms)executions per secsample deviation
1,000 add randomly23.8941.850.01
1,000 add & delete randomly22.8443.780.01
1,000 addMany10.6194.224.91e-4
1,000 get18.5653.878.86e-4
1,000 dfs158.906.290.00
1,000 bfs58.5917.070.00
1,000 morris269.913.700.01
bst
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly30.5532.732.83e-4
10,000 add & delete randomly69.2214.457.57e-4
10,000 addMany29.5233.883.69e-4
10,000 get28.7134.832.69e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 add randomly31.6631.598.90e-4
10,000 add & delete randomly73.9713.520.00
10,000 addMany31.6631.580.00
10,000 get29.7533.615.49e-4
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add randomly85.6311.680.00
100,000 add & delete randomly181.315.520.01
100,000 getNode91.7710.905.02e-4
+
test nametime taken (ms)executions per secsample deviation
100,000 add randomly87.2411.460.00
100,000 add & delete randomly218.784.570.01
100,000 getNode91.3910.940.00
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109889.321.21e-6
1,000 addEdge5.97167.631.09e-4
1,000 getVertex0.052.17e+44.36e-7
1,000 getEdge23.7342.140.00
tarjan225.104.440.01
tarjan all233.474.280.02
topologicalSort183.965.440.00
+
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109631.845.21e-6
1,000 addEdge6.25160.063.97e-4
1,000 getVertex0.052.13e+41.05e-6
1,000 getEdge23.8341.970.00
tarjan217.694.590.01
tarjan all226.434.420.01
topologicalSort180.715.530.01
heap
-
test nametime taken (ms)executions per secsample deviation
10,000 add & pop4.61216.994.19e-5
10,000 fib add & pop354.792.820.00
+
test nametime taken (ms)executions per secsample deviation
10,000 add & pop4.69213.131.63e-4
10,000 fib add & pop367.492.720.01
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 unshift210.074.760.03
1,000,000 unshift & shift174.445.730.04
1,000,000 insertBefore355.362.810.10
+
test nametime taken (ms)executions per secsample deviation
1,000,000 unshift221.104.520.06
1,000,000 unshift & shift179.995.560.03
1,000,000 insertBefore331.293.020.06
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
10,000 push & pop220.474.540.01
10,000 insertBefore252.593.960.00
+
test nametime taken (ms)executions per secsample deviation
10,000 push & pop216.594.620.01
10,000 insertBefore255.083.920.01
max-priority-queue
-
test nametime taken (ms)executions per secsample deviation
10,000 refill & poll11.7285.322.97e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 refill & poll12.0083.346.62e-4
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push233.904.280.07
1,000,000 shift25.4039.370.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push222.014.500.05
1,000,000 shift25.3539.450.00
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push43.5422.970.00
1,000,000 push & shift83.9911.910.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.3122.070.01
1,000,000 push & shift83.9211.920.01
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push49.1720.340.01
100,000 getWords88.8411.260.01
+
test nametime taken (ms)executions per secsample deviation
100,000 push59.0816.920.01
100,000 getWords95.7410.440.01
[//]: # (No deletion!!! End of Replace Section) \ No newline at end of file diff --git a/package.json b/package.json index 60d3669..0569eca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.42.6", + "version": "1.42.7", "description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.", "main": "dist/cjs/src/index.js", "module": "dist/mjs/src/index.js", diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index a3c4390..f71e789 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -71,12 +71,12 @@ export class AVLTree = AVLTreeNode` objects. */ override delete>( identifier: ReturnType, - callback: C = this.defaultOneParamCallback as C + callback: C = this._defaultOneParamCallback as C ): BiTreeDeleteResult[] { if ((identifier as any) instanceof AVLTreeNode) callback = (node => node) as C; const deletedResults = super.delete(identifier, callback); @@ -97,8 +97,8 @@ export class AVLTree = AVLTreeNode = BinaryTree /** * The value stored in the node. */ - value: V | undefined; + value?: V; /** * The parent node of the current node. */ - parent: N | null | undefined; + parent?: N | null; /** * Creates a new instance of BinaryTreeNode. @@ -43,7 +43,7 @@ export class BinaryTreeNode = BinaryTree this.value = value; } - protected _left: N | null | undefined; + protected _left?: N | null; /** * Get the left child node. @@ -63,7 +63,7 @@ export class BinaryTreeNode = BinaryTree this._left = v; } - protected _right: N | null | undefined; + protected _right?: N | null; /** * Get the right child node. @@ -121,9 +121,10 @@ export class BinaryTree = BinaryTreeNode const {iterationType = IterationType.ITERATIVE} = options; this.iterationType = iterationType; } + this._size = 0; } - protected _root: N | null | undefined = undefined; + protected _root?: N | null; /** * Get the root node of the binary tree. @@ -132,7 +133,7 @@ export class BinaryTree = BinaryTreeNode return this._root; } - protected _size = 0; + protected _size: number; /** * Get the number of nodes in the binary tree. @@ -151,22 +152,6 @@ export class BinaryTree = BinaryTreeNode return new BinaryTreeNode(key, value) as N; } - /** - * Clear the binary tree, removing all nodes. - */ - clear() { - this._setRoot(undefined); - this._size = 0; - } - - /** - * Check if the binary tree is empty. - * @returns {boolean} - True if the binary tree is empty, false otherwise. - */ - isEmpty(): boolean { - return this.size === 0; - } - /** * Add a node with the given key and value to the binary tree. * @param {BTNKey | N | null} keyOrNode - The key or node to add to the binary tree. @@ -196,7 +181,7 @@ export class BinaryTree = BinaryTreeNode if (keyOrNode === null) { needInsert = null; - } else if (typeof keyOrNode === 'number') { + } else if (this.isNodeKey(keyOrNode)) { needInsert = this.createNode(keyOrNode, value); } else if (keyOrNode instanceof BinaryTreeNode) { needInsert = keyOrNode; @@ -253,7 +238,7 @@ export class BinaryTree = BinaryTreeNode * array. Each value in the `data` array will be assigned to the * @returns The method is returning a boolean value. */ - refill(keysOrNodes: (BTNKey | null | undefined)[] | (N | null | undefined)[], values?: Array): boolean { + refill(keysOrNodes: (BTNKey | N | null | undefined)[], values?: (V | undefined)[]): boolean { this.clear(); return keysOrNodes.length === this.addMany(keysOrNodes, values).length; } @@ -276,18 +261,18 @@ export class BinaryTree = BinaryTreeNode * @param callback - The `callback` parameter is a function that takes a node as input and returns a * value. This value is compared with the `identifier` parameter to determine if the node should be * included in the result. The `callback` parameter has a default value of - * `this.defaultOneParamCallback`, which + * `this._defaultOneParamCallback`, which */ delete>( identifier: ReturnType | null | undefined, - callback: C = this.defaultOneParamCallback as C + callback: C = this._defaultOneParamCallback as C ): BiTreeDeleteResult[] { - const deleteResult: BiTreeDeleteResult[] = []; - if (!this.root) return deleteResult; + const deletedResult: BiTreeDeleteResult[] = []; + if (!this.root) return deletedResult; if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; const curr = this.getNode(identifier, callback); - if (!curr) return deleteResult; + if (!curr) return deletedResult; const parent: N | null | undefined = curr?.parent ? curr.parent : null; let needBalanced: N | null | undefined = undefined; @@ -321,8 +306,8 @@ export class BinaryTree = BinaryTreeNode } this._size = this.size - 1; - deleteResult.push({deleted: orgCurrent, needBalanced}); - return deleteResult; + deletedResult.push({deleted: orgCurrent, needBalanced}); + return deletedResult; } /** @@ -338,8 +323,8 @@ export class BinaryTree = BinaryTreeNode * @returns the depth of the `distNode` relative to the `beginRoot`. */ getDepth(distNode: BTNKey | N | null | undefined, beginRoot: BTNKey | N | null | undefined = this.root): number { - if (typeof distNode === 'number') distNode = this.getNode(distNode); - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + distNode = this.ensureNotKey(distNode); + beginRoot = this.ensureNotKey(beginRoot); let depth = 0; while (distNode?.parent) { if (distNode === beginRoot) { @@ -364,7 +349,7 @@ export class BinaryTree = BinaryTreeNode * @returns the height of the binary tree. */ getHeight(beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType): number { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { @@ -387,14 +372,9 @@ export class BinaryTree = BinaryTreeNode while (stack.length > 0) { const {node, depth} = stack.pop()!; - if (node.left) { - stack.push({node: node.left, depth: depth + 1}); - } - - if (node.right) { - stack.push({node: node.right, depth: depth + 1}); - } - + if (node.left) stack.push({node: node.left, depth: depth + 1}); + if (node.right) stack.push({node: node.right, depth: depth + 1}); + maxHeight = Math.max(maxHeight, depth); } @@ -413,8 +393,7 @@ export class BinaryTree = BinaryTreeNode * @returns The function `getMinHeight` returns the minimum height of a binary tree. */ getMinHeight(beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType): number { - if (!beginRoot) return -1; - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { @@ -500,7 +479,7 @@ export class BinaryTree = BinaryTreeNode * @param callback - The `callback` parameter is a function that takes a node as input and returns a * value. This value is compared with the `identifier` parameter to determine if the node should be * included in the result. The `callback` parameter has a default value of - * `this.defaultOneParamCallback`, which + * `this._defaultOneParamCallback`, which * @param [onlyOne=false] - A boolean value indicating whether to stop searching after finding the * first node that matches the identifier. If set to true, the function will return an array with * only one element (or an empty array if no matching node is found). If set to false (default), the @@ -514,14 +493,14 @@ export class BinaryTree = BinaryTreeNode */ getNodes>( identifier: ReturnType | null | undefined, - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, onlyOne = false, beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): N[] { if (!beginRoot) return []; if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return []; const ans: N[] = []; @@ -585,7 +564,7 @@ export class BinaryTree = BinaryTreeNode * @param callback - The `callback` parameter is a function that is used to determine whether a node * matches the desired criteria. It takes a node as input and returns a boolean value indicating * whether the node matches the criteria or not. The default callback function - * `this.defaultOneParamCallback` is used if no callback function is + * `this._defaultOneParamCallback` is used if no callback function is * @param beginRoot - The `beginRoot` parameter is the starting point for the search. It specifies * the node from which the search should begin. By default, it is set to `this.root`, which means the * search will start from the root node of the binary tree. However, you can provide a different node @@ -596,7 +575,7 @@ export class BinaryTree = BinaryTreeNode */ has>( identifier: ReturnType | null | undefined, - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): boolean { @@ -634,7 +613,7 @@ export class BinaryTree = BinaryTreeNode * @param callback - The `callback` parameter is a function that is used to determine whether a node * matches the desired criteria. It takes a node as input and returns a boolean value indicating * whether the node matches the criteria or not. The default callback function - * (`this.defaultOneParamCallback`) is used if no callback function is + * (`this._defaultOneParamCallback`) is used if no callback function is * @param beginRoot - The `beginRoot` parameter is the starting point for the search. It specifies * the root node from which the search should begin. * @param iterationType - The `iterationType` parameter specifies the type of iteration to be @@ -643,7 +622,7 @@ export class BinaryTree = BinaryTreeNode */ getNode>( identifier: ReturnType | null | undefined, - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): N | null | undefined { @@ -652,7 +631,18 @@ export class BinaryTree = BinaryTreeNode return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null; } - protected _getNodeByKey(key: BTNKey, iterationType = IterationType.ITERATIVE): N | undefined { + /** + * The function `getNodeByKey` searches for a node in a binary tree by its key, using either + * recursive or iterative iteration. + * @param {BTNKey} key - The `key` parameter is the key value that we are searching for in the tree. + * It is used to find the node with the matching key value. + * @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 + * found in the binary tree. If no node is found, it returns `undefined`. + */ + getNodeByKey(key: BTNKey, iterationType = IterationType.ITERATIVE): N | undefined { if (!this.root) return undefined; if (iterationType === IterationType.RECURSIVE) { const _dfs = (cur: N): N | undefined => { @@ -677,6 +667,21 @@ export class BinaryTree = BinaryTreeNode } } + /** + * The function `ensureNotKey` returns the node corresponding to the given key if it is a valid node + * key, otherwise it returns the key itself. + * @param {BTNKey | N | null | undefined} key - The `key` parameter can be of type `BTNKey`, `N`, + * `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 + * `IterationType.ITERATIVE`. + * @returns either the node corresponding to the given key if it is a valid node key, or the key + * itself if it is not a valid node key. + */ + ensureNotKey(key: BTNKey | N | null | undefined, iterationType = IterationType.ITERATIVE): N | null | undefined { + return this.isNodeKey(key) ? this.getNodeByKey(key, iterationType) : key; + } + get>( identifier: BTNKey, callback?: C, @@ -706,7 +711,7 @@ export class BinaryTree = BinaryTreeNode * @param callback - The `callback` parameter is a function that is used to determine whether a node * matches the desired criteria. It takes a node as input and returns a boolean value indicating * whether the node matches the criteria or not. The default callback function - * (`this.defaultOneParamCallback`) is used if no callback function is + * (`this._defaultOneParamCallback`) is used if no callback function is * @param beginRoot - The `beginRoot` parameter is the starting point for the search. It specifies * the root node from which the search should begin. * @param iterationType - The `iterationType` parameter specifies the type of iteration to be @@ -715,7 +720,7 @@ export class BinaryTree = BinaryTreeNode */ get>( identifier: ReturnType | null | undefined, - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, beginRoot:BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): V | undefined { @@ -724,6 +729,22 @@ export class BinaryTree = BinaryTreeNode return this.getNode(identifier, callback, beginRoot, iterationType)?.value ?? undefined; } + /** + * Clear the binary tree, removing all nodes. + */ + clear() { + this._setRoot(undefined); + this._size = 0; + } + + /** + * Check if the binary tree is empty. + * @returns {boolean} - True if the binary tree is empty, false otherwise. + */ + isEmpty(): boolean { + return this.size === 0; + } + /** * The function `getPathToRoot` returns an array of nodes starting from a given node and traversing * up to the root node, with the option to reverse the order of the nodes. @@ -737,7 +758,7 @@ export class BinaryTree = BinaryTreeNode getPathToRoot(beginRoot: BTNKey | N | null | undefined, isReverse = true): N[] { // TODO to support get path through passing key const result: N[] = []; - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return result; @@ -763,13 +784,13 @@ export class BinaryTree = BinaryTreeNode * no leftmost node, it returns `null`. */ getLeftMost(beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType): N | null | undefined { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return beginRoot; if (iterationType === IterationType.RECURSIVE) { const _traverse = (cur: N): N => { - if (!cur.left) return cur; + if (!this.isRealNode(cur.left)) return cur; return _traverse(cur.left); }; @@ -777,7 +798,7 @@ export class BinaryTree = BinaryTreeNode } else { // Indirect implementation of iteration using tail recursion optimization const _traverse = trampoline((cur: N) => { - if (!cur.left) return cur; + if (!this.isRealNode(cur.left)) return cur; return _traverse.cont(cur.left); }); @@ -798,12 +819,12 @@ export class BinaryTree = BinaryTreeNode */ getRightMost(beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType): N | null | undefined { // TODO support get right most by passing key in - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return beginRoot; if (iterationType === IterationType.RECURSIVE) { const _traverse = (cur: N): N => { - if (!cur.right) return cur; + if (!this.isRealNode(cur.right)) return cur; return _traverse(cur.right); }; @@ -811,7 +832,7 @@ export class BinaryTree = BinaryTreeNode } else { // Indirect implementation of iteration using tail recursion optimization const _traverse = trampoline((cur: N) => { - if (!cur.right) return cur; + if (!this.isRealNode(cur.right)) return cur; return _traverse.cont(cur.right); }); @@ -830,7 +851,7 @@ export class BinaryTree = BinaryTreeNode */ isSubtreeBST(beginRoot: BTNKey | N | null | undefined, iterationType = this.iterationType): boolean { // TODO there is a bug - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return true; if (iterationType === IterationType.RECURSIVE) { @@ -909,12 +930,12 @@ export class BinaryTree = BinaryTreeNode * @returns The function `subTreeTraverse` returns an array of `ReturnType>`. */ subTreeTraverse>( - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[] { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); const ans: (ReturnType> | null | undefined)[] = []; if (!beginRoot) return ans; @@ -954,18 +975,40 @@ export class BinaryTree = BinaryTreeNode return ans; } - isNode(node: any): node is N { + /** + * The function checks if a given node is a real node by verifying if it is an instance of + * BinaryTreeNode and its key is not NaN. + * @param {any} node - The parameter `node` is of type `any`, which means it can be any data type. + * @returns a boolean value. + */ + isRealNode(node: any): node is N { return node instanceof BinaryTreeNode && node.key.toString() !== 'NaN'; } + /** + * The function checks if a given node is a BinaryTreeNode instance and has a key value of NaN. + * @param {any} node - The parameter `node` is of type `any`, which means it can be any data type. + * @returns a boolean value. + */ isNIL(node: any) { return node instanceof BinaryTreeNode && node.key.toString() === 'NaN'; } + /** + * The function checks if a given node is a real node or null. + * @param {any} node - The parameter `node` is of type `any`, which means it can be any data type. + * @returns a boolean value. + */ isNodeOrNull(node: any): node is (N | null){ - return this.isNode(node) || node === null; + return this.isRealNode(node) || node === null; } + /** + * The function "isNodeKey" checks if a potential key is a number. + * @param {any} potentialKey - The potentialKey parameter is of type any, which means it can be any + * data type. + * @returns a boolean value indicating whether the potentialKey is of type number or not. + */ isNodeKey(potentialKey: any) : potentialKey is number { return typeof potentialKey === 'number'; } @@ -999,7 +1042,7 @@ export class BinaryTree = BinaryTreeNode * function on each node according to a specified order pattern. * @param callback - The `callback` parameter is a function that will be called on each node during * the depth-first search traversal. It takes a node as input and returns a value. The default value - * is `this.defaultOneParamCallback`, which is a callback function defined elsewhere in the code. + * is `this._defaultOneParamCallback`, which is a callback function defined elsewhere in the code. * @param {DFSOrderPattern} [pattern=in] - The `pattern` parameter determines the order in which the * nodes are visited during the depth-first search. There are three possible values for `pattern`: * @param {N | null | undefined} beginRoot - The `beginRoot` parameter is the starting node for the depth-first @@ -1011,14 +1054,14 @@ export class BinaryTree = BinaryTreeNode * @returns The function `dfs` returns an array of `ReturnType>` values. */ dfs>( - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', beginRoot: BTNKey | N | null | undefined = this.root, iterationType: IterationType = IterationType.ITERATIVE, includeNull = false ): ReturnType[] { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return []; const ans: ReturnType[] = []; if (iterationType === IterationType.RECURSIVE) { @@ -1031,7 +1074,7 @@ export class BinaryTree = BinaryTreeNode if (node && this.isNodeOrNull(node.right)) _traverse(node.right); } else { if (node && node.left) _traverse(node.left); - this.isNode(node) && ans.push(callback(node)); + this.isRealNode(node) && ans.push(callback(node)); if (node && node.right) _traverse(node.right); } break; @@ -1041,7 +1084,7 @@ export class BinaryTree = BinaryTreeNode if (node && this.isNodeOrNull(node.left)) _traverse(node.left); if (node && this.isNodeOrNull(node.right)) _traverse(node.right); } else { - this.isNode(node) && ans.push(callback(node)); + this.isRealNode(node) && ans.push(callback(node)); if (node && node.left) _traverse(node.left); if (node && node.right) _traverse(node.right); } @@ -1054,7 +1097,7 @@ export class BinaryTree = BinaryTreeNode } else { if (node && node.left) _traverse(node.left); if (node && node.right) _traverse(node.right); - this.isNode(node) && ans.push(callback(node)); + this.isRealNode(node) && ans.push(callback(node)); } break; @@ -1132,7 +1175,7 @@ export class BinaryTree = BinaryTreeNode * function on each node. * @param callback - The `callback` parameter is a function that will be called for each node in the * breadth-first search. It takes a node of type `N` as its argument and returns a value of type - * `ReturnType>`. The default value for this parameter is `this.defaultOneParamCallback + * `ReturnType>`. The default value for this parameter is `this._defaultOneParamCallback * @param {N | null | undefined} beginRoot - The `beginRoot` parameter is the starting node for the breadth-first * search. It determines from which node the search will begin. If `beginRoot` is `null`, the search * will not be performed and an empty array will be returned. @@ -1142,12 +1185,12 @@ export class BinaryTree = BinaryTreeNode * @returns The function `bfs` returns an array of `ReturnType>[]`. */ bfs>( - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[] { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return []; const ans: ReturnType>[] = []; @@ -1233,14 +1276,14 @@ export class BinaryTree = BinaryTreeNode * function `C` applied to the nodes at that level. */ listLevels>( - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[][] { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); - if (!beginRoot) return []; + beginRoot = this.ensureNotKey(beginRoot); const levelsNodes: ReturnType[][] = []; + if (!beginRoot) return levelsNodes; if (iterationType === IterationType.RECURSIVE) { const _recursive = (node: N | null | undefined, level: number) => { @@ -1287,12 +1330,12 @@ export class BinaryTree = BinaryTreeNode * @returns The function `getPredecessor` returns the predecessor node of the given node `node`. */ getPredecessor(node: BTNKey | N | null | undefined): N | undefined{ - if (this.isNodeKey(node)) node = this.getNode(node); - if (!node) return undefined; + node = this.ensureNotKey(node); + if (!this.isRealNode(node)) return undefined; if (node.left) { let predecessor: N | null | undefined = node.left; - while (!predecessor || (predecessor.right && predecessor.right !== node)) { + while (!this.isRealNode(predecessor) || (this.isRealNode(predecessor.right) && predecessor.right !== node)) { if (predecessor) { predecessor = predecessor.right; } @@ -1310,8 +1353,8 @@ export class BinaryTree = BinaryTreeNode * @returns The function `getSuccessor` returns a value of type `N` (the successor node), or `null` * if there is no successor, or `undefined` if the input `x` is `undefined`. */ - getSuccessor(x: BTNKey | N | null | undefined): N | null | undefined { - if (this.isNodeKey(x)) x = this.getNode(x); + getSuccessor(x?: BTNKey | N | null): N | null | undefined { + x = this.ensureNotKey(x); if (!x) return undefined; if (x.right) { @@ -1331,7 +1374,7 @@ export class BinaryTree = BinaryTreeNode * algorithm and returns an array of values obtained by applying a callback function to each node. * @param callback - The `callback` parameter is a function that will be called on each node in the * tree. It takes a node of type `N` as input and returns a value of type `ReturnType>`. The - * default value for this parameter is `this.defaultOneParamCallback`. + * default value for this parameter is `this._defaultOneParamCallback`. * @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: @@ -1341,11 +1384,11 @@ export class BinaryTree = BinaryTreeNode * @returns The `morris` function returns an array of `ReturnType>` values. */ morris>( - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', beginRoot: BTNKey | N | null | undefined = this.root ): ReturnType[] { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (beginRoot === null) return []; const ans: ReturnType>[] = []; @@ -1470,7 +1513,7 @@ export class BinaryTree = BinaryTreeNode } } - protected defaultOneParamCallback = (node: N) => node.key; + protected _defaultOneParamCallback = (node: N) => node.key; /** * Swap the data of two nodes in the binary tree. @@ -1479,8 +1522,8 @@ export class BinaryTree = BinaryTreeNode * @returns {N} - The destination node after the swap. */ protected _swap(srcNode: BTNKey | N | null | undefined, destNode:BTNKey | N | null | undefined): N | undefined{ - if (this.isNodeKey(srcNode)) srcNode = this._getNodeByKey(srcNode); - if (this.isNodeKey(destNode)) destNode = this._getNodeByKey(destNode); + srcNode = this.ensureNotKey(srcNode); + destNode = this.ensureNotKey(destNode); if (srcNode && destNode) { const {key, value} = destNode; @@ -1550,7 +1593,16 @@ export class BinaryTree = BinaryTreeNode this._root = v; } - print(beginRoot: N | null | undefined = this.root) { + /** + * The `print` function is used to display a binary tree structure in a visually appealing way. + * @param {N | null | undefined} root - The `root` parameter in the `print` function represents the + * root node of a binary tree. It can have one of the following types: `BTNKey`, `N`, `null`, or + * `undefined`. The default value is `this.root`, which suggests that `this.root` is the + */ + print(beginRoot: BTNKey | N | null | undefined = this.root): void { + beginRoot = this.ensureNotKey(beginRoot); + if (!beginRoot) return; + const display = (root: N | null | undefined): void => { const [lines, , ,] = _displayAux(root); for (const line of lines) { diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 13080ac..729c4a5 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -12,7 +12,7 @@ import {IBinaryTree} from '../../interfaces'; import {Queue} from '../queue'; export class BSTNode = BSTNodeNested> extends BinaryTreeNode { - override parent: N | undefined; + override parent?: N ; constructor(key: BTNKey, value?: V) { super(key, value); @@ -21,7 +21,7 @@ export class BSTNode = BSTNodeNested> extend this._right = undefined; } - protected override _left: N | undefined; + protected override _left?: N ; /** * Get the left child node. @@ -42,7 +42,7 @@ export class BSTNode = BSTNodeNested> extend } - protected override _right: N | undefined; + protected override _right?: N ; /** * Get the right child node. @@ -83,7 +83,7 @@ export class BST = BSTNode> } } - protected override _root: N | undefined = undefined; + protected override _root?: N ; /** * Get the root node of the binary tree. @@ -115,16 +115,13 @@ export class BST = BSTNode> * was not added or if the parameters were invalid, it returns undefined or undefined. */ override add(keyOrNode: BTNKey | N | null | undefined, value?: V): N | undefined { - if (keyOrNode === 8) { - debugger - } if (keyOrNode === null) return undefined; // TODO support node as a parameter let inserted: N | undefined; let newNode: N | undefined; if (keyOrNode instanceof BSTNode) { newNode = keyOrNode; - } else if (typeof keyOrNode === 'number') { + } else if (this.isNodeKey(keyOrNode)) { newNode = this.createNode(keyOrNode, value); } else { newNode = undefined; @@ -197,7 +194,6 @@ export class BST = BSTNode> * It can have two possible values: * @returns The `addMany` function returns an array of `N`, `undefined`, or `undefined` values. */ - override addMany( keysOrNodes: (BTNKey | N | undefined)[], data?: (V | undefined)[], @@ -205,51 +201,53 @@ export class BST = BSTNode> iterationType = this.iterationType ): (N | undefined)[] { // TODO this addMany function is inefficient, it should be optimized - function hasNoNull(arr: (BTNKey | N | undefined)[]): arr is (BTNKey | N)[] { + function hasNoUndefined(arr: (BTNKey | N | undefined)[]): arr is (BTNKey | N)[] { return arr.indexOf(undefined) === -1; } - if (!isBalanceAdd || !hasNoNull(keysOrNodes)) { + if (!isBalanceAdd || !hasNoUndefined(keysOrNodes)) { return super.addMany(keysOrNodes, data).map(n => n ?? undefined); } + const inserted: (N | undefined)[] = []; const combinedArr: [BTNKey | N, V][] = keysOrNodes.map( (value: BTNKey | N, index) => [value, data?.[index]] as [BTNKey | N, V] ); + let sorted = []; - function isNodeOrNullTuple(arr: [BTNKey | N, V][]): arr is [N, V][] { + function _isNodeOrUndefinedTuple(arr: [BTNKey | N, V][]): arr is [N, V][] { for (const [keyOrNode] of arr) if (keyOrNode instanceof BSTNode) return true; return false; } - function isBinaryTreeKeyOrNullTuple(arr: [BTNKey | N, V][]): arr is [BTNKey, V][] { - for (const [keyOrNode] of arr) if (typeof keyOrNode === 'number') return true; + const _isBinaryTreeKeyOrNullTuple = (arr: [BTNKey | N, V][]): arr is [BTNKey, V][] => { + for (const [keyOrNode] of arr) if (this.isNodeKey(keyOrNode)) return true; return false; } let sortedKeysOrNodes: (number | N | undefined)[] = [], sortedData: (V | undefined)[] | undefined = []; - if (isNodeOrNullTuple(combinedArr)) { + if (_isNodeOrUndefinedTuple(combinedArr)) { sorted = combinedArr.sort((a, b) => a[0].key - b[0].key); - } else if (isBinaryTreeKeyOrNullTuple(combinedArr)) { + } else if (_isBinaryTreeKeyOrNullTuple(combinedArr)) { sorted = combinedArr.sort((a, b) => a[0] - b[0]); } else { throw new Error('Invalid input keysOrNodes'); } sortedKeysOrNodes = sorted.map(([keyOrNode]) => keyOrNode); sortedData = sorted.map(([, value]) => value); - const recursive = (arr: (BTNKey | undefined | N)[], data?: (V | undefined)[]) => { + const _dfs = (arr: (BTNKey | undefined | N)[], data?: (V | undefined)[]) => { if (arr.length === 0) return; const mid = Math.floor((arr.length - 1) / 2); const newNode = this.add(arr[mid], data?.[mid]); inserted.push(newNode); - recursive(arr.slice(0, mid), data?.slice(0, mid)); - recursive(arr.slice(mid + 1), data?.slice(mid + 1)); + _dfs(arr.slice(0, mid), data?.slice(0, mid)); + _dfs(arr.slice(mid + 1), data?.slice(mid + 1)); }; - const iterative = () => { + const _iterate = () => { const n = sorted.length; const stack: [[number, number]] = [[0, n - 1]]; while (stack.length > 0) { @@ -267,9 +265,9 @@ export class BST = BSTNode> } }; if (iterationType === IterationType.RECURSIVE) { - recursive(sortedKeysOrNodes, sortedData); + _dfs(sortedKeysOrNodes, sortedData); } else { - iterative(); + _iterate(); } return inserted; @@ -296,13 +294,24 @@ export class BST = BSTNode> else return this.getRightMost(beginRoot, iterationType)?.key ?? 0; } - protected override _getNodeByKey(key: BTNKey, iterationType = IterationType.ITERATIVE): N | undefined { + /** + * The function `getNodeByKey` searches for a node in a binary tree based on a given key, using + * either recursive or iterative methods. + * @param {BTNKey} key - The `key` parameter is the key value that we are searching for in the tree. + * It is used to find the node with the matching key value. + * @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 + * found in the binary tree. If no node is found, it returns `undefined`. + */ + override getNodeByKey(key: BTNKey, iterationType = IterationType.ITERATIVE): N | undefined { if (!this.root) return undefined; if (iterationType === IterationType.RECURSIVE) { const _dfs = (cur: N): N | undefined => { if (cur.key === key) return cur; - if (!cur.left && !cur.right) return; + if (this._compare(cur.key, key) === CP.gt && cur.left) return _dfs(cur.left); if (this._compare(cur.key, key) === CP.lt && cur.right) return _dfs(cur.right); }; @@ -321,6 +330,19 @@ export class BST = BSTNode> } } + /** + * The function `ensureNotKey` returns the node corresponding to the given key if it is a node key, + * otherwise it returns the key itself. + * @param {BTNKey | N | undefined} key - The `key` parameter can be of type `BTNKey`, `N`, 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. + */ + override ensureNotKey(key: BTNKey | N | undefined, iterationType = IterationType.ITERATIVE): N | undefined { + return this.isNodeKey(key) ? this.getNodeByKey(key, iterationType) : key; + } + /** * The function `getNodes` retrieves nodes from a binary tree based on a given node property or key, * using either recursive or iterative traversal. @@ -329,7 +351,7 @@ export class BST = BSTNode> * generic type `N`. * @param callback - The `callback` parameter is a function that takes a node as input and returns a * value. This value is compared with the `nodeProperty` parameter to determine if the node should be - * included in the result. The default value for `callback` is `this.defaultOneParamCallback`, which is + * included in the result. The default value for `callback` is `this._defaultOneParamCallback`, which is * a * @param [onlyOne=false] - A boolean value indicating whether to stop the traversal after finding * the first node that matches the nodeProperty. If set to true, the function will return an array @@ -344,12 +366,12 @@ export class BST = BSTNode> */ override getNodes>( identifier: ReturnType | undefined, - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, onlyOne = false, beginRoot: BTNKey | N | undefined = this.root, iterationType = this.iterationType ): N[] { - if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + beginRoot = this.ensureNotKey(beginRoot); if (!beginRoot) return []; const ans: N[] = []; @@ -363,7 +385,7 @@ export class BST = BSTNode> if (!cur.left && !cur.right) return; // TODO potential bug - if (callback === this.defaultOneParamCallback) { + if (callback === this._defaultOneParamCallback) { if (this._compare(cur.key, identifier as number) === CP.gt) cur.left && _traverse(cur.left); if (this._compare(cur.key, identifier as number) === CP.lt) cur.right && _traverse(cur.right); } else { @@ -384,7 +406,7 @@ export class BST = BSTNode> if (onlyOne) return ans; } // TODO potential bug - if (callback === this.defaultOneParamCallback) { + if (callback === this._defaultOneParamCallback) { if (this._compare(cur.key, identifier as number) === CP.gt) cur.left && queue.push(cur.left); if (this._compare(cur.key, identifier as number) === CP.lt) cur.right && queue.push(cur.right); } else { @@ -418,17 +440,18 @@ export class BST = BSTNode> * @returns The function `lesserOrGreaterTraverse` returns an array of `ReturnType>`. */ lesserOrGreaterTraverse>( - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, lesserOrGreater: CP = CP.lt, targetNode: BTNKey | N | undefined = this.root, iterationType = this.iterationType ): ReturnType[] { - if (typeof targetNode === 'number') targetNode = this.getNode(targetNode) ?? undefined; + targetNode = this.ensureNotKey(targetNode); const ans: ReturnType>[] = []; if (!targetNode) return ans; - const targetKey = targetNode.key; if (!this.root) return ans; + const targetKey = targetNode.key; + if (iterationType === IterationType.RECURSIVE) { const _traverse = (cur: N) => { const compared = this._compare(cur.key, targetKey); diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 57551fb..c7f39fe 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -12,14 +12,14 @@ import { BTNKey, IterationType, RBTNColor, - RBTreeNodeNested, + RedBlackTreeNodeNested, RBTreeOptions } from '../../types'; import {BST, BSTNode} from "./bst"; import {IBinaryTree} from "../../interfaces"; import {BinaryTreeNode} from "./binary-tree"; -export class RBTreeNode = RBTreeNodeNested> extends BSTNode { +export class RedBlackTreeNode = RedBlackTreeNodeNested> extends BSTNode { color: RBTNColor; constructor(key: BTNKey, value?: V, color: RBTNColor = RBTNColor.BLACK) { super(key, value); @@ -34,7 +34,7 @@ export class RBTreeNode = RBTreeNodeNested = RBTreeNode>> +export class RedBlackTree = RedBlackTreeNode>> extends BST implements IBinaryTree { @@ -56,13 +56,21 @@ export class RedBlackTree = RBTreeNode(NaN) as unknown as N; + NIL: N = new RedBlackTreeNode(NaN) as unknown as N; + /** + * The `add` function adds a new node to a Red-Black Tree data structure. + * @param {BTNKey | N | null | undefined} keyOrNode - The `keyOrNode` parameter can be one of the + * following types: + * @param {V} [value] - The `value` parameter is an optional value that can be associated with the + * key in the node being added to the Red-Black Tree. + * @returns The method returns either a node (`N`) or `undefined`. + */ override add(keyOrNode: BTNKey | N | null | undefined, value?: V): N | undefined { let node: N; - if (typeof keyOrNode === 'number') { + if (this.isNodeKey(keyOrNode)) { node = this.createNode(keyOrNode, value, RBTNColor.RED); - } else if(keyOrNode instanceof RBTreeNode) { + } else if(keyOrNode instanceof RedBlackTreeNode) { node = keyOrNode; } else if (keyOrNode === null) { return; @@ -112,13 +120,24 @@ export class RedBlackTree = RBTreeNode(key, value, color) as N; + return new RedBlackTreeNode(key, value, color) as N; } - - + + /** + * The `delete` function removes a node from a binary tree based on a given identifier and updates + * the tree accordingly. + * @param {ReturnType | null | undefined} identifier - The `identifier` parameter is the value + * 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 + * 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 `BiTreeDeleteResult`. + */ delete>( identifier: ReturnType | null | undefined, - callback: C = this.defaultOneParamCallback as C + callback: C = this._defaultOneParamCallback as C ): BiTreeDeleteResult[] { const ans: BiTreeDeleteResult[] = []; if (identifier === null) return ans; @@ -151,7 +170,7 @@ export class RedBlackTree = RBTreeNode = RBTreeNode = RBTreeNode = RBTreeNode>( identifier: ReturnType | undefined, - callback: C = this.defaultOneParamCallback as C, - beginRoot = this.root, + callback: C = this._defaultOneParamCallback as C, + beginRoot: BTNKey | N | undefined = this.root, iterationType = this.iterationType ): N | null | undefined { if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; - + beginRoot = this.ensureNotKey(beginRoot); return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined; } - /** - * The function returns the leftmost node in a red-black tree. - * @param {RBTreeNode} node - The parameter "node" is of type RBTreeNode, which represents a node in - * a Red-Black Tree. - * @returns The leftmost node in the given RBTreeNode. - */ - getLeftMost(node: N = this.root): N { - while (node.left !== undefined && node.left !== this.NIL) { - node = node.left; - } - return node; - } - - /** - * The function returns the rightmost node in a red-black tree. - * @param {RBTreeNode} node - The parameter "node" is of type RBTreeNode. - * @returns the rightmost node in a red-black tree. - */ - getRightMost(node: N): N { - while (node.right !== undefined && node.right !== this.NIL) { - node = node.right; - } - return node; - } - /** * The function returns the successor of a given node in a red-black tree. - * @param {RBTreeNode} x - RBTreeNode - The node for which we want to find the successor. - * @returns the successor of the given RBTreeNode. + * @param {RedBlackTreeNode} x - RedBlackTreeNode - The node for which we want to find the successor. + * @returns the successor of the given RedBlackTreeNode. */ - getSuccessor(x: N): N | undefined { + override getSuccessor(x: N): N | undefined { if (x.right !== this.NIL) { - return this.getLeftMost(x.right); + return this.getLeftMost(x.right) ?? undefined; } let y: N | undefined = x.parent; @@ -273,13 +267,13 @@ export class RedBlackTree = RBTreeNode = RBTreeNode = RBTreeNode = RBTreeNode = RBTreeNode = RBTreeNode = TreeMultimapNode>> extends AVLTree - implements IBinaryTree -{ + implements IBinaryTree { /** * The constructor function for a TreeMultimap class in TypeScript, which extends another class and sets an option to * merge duplicated values. @@ -82,8 +81,8 @@ export class TreeMultimap = TreeMultim * @returns The function `add` returns a value of type `N | undefined | undefined`. */ override add(keyOrNode: BTNKey | N | null | undefined, value?: V, count = 1): N | undefined { - if(keyOrNode === null) return undefined; - let inserted: N | undefined = undefined, + if (keyOrNode === null) return undefined; + let inserted: N | undefined = undefined, newNode: N | undefined; if (keyOrNode instanceof TreeMultimapNode) { newNode = this.createNode(keyOrNode.key, keyOrNode.value, keyOrNode.count); @@ -95,7 +94,7 @@ export class TreeMultimap = TreeMultim if (!this.root) { this._setRoot(newNode); this._size = this.size + 1; - newNode && this._setCount(this.count + newNode.count); + if (newNode) this._count += newNode.count; inserted = this.root; } else { let cur = this.root; @@ -106,7 +105,7 @@ export class TreeMultimap = TreeMultim if (this._compare(cur.key, newNode.key) === CP.eq) { cur.value = newNode.value; cur.count += newNode.count; - this._setCount(this.count + newNode.count); + this._count += newNode.count; traversing = false; inserted = cur; } else if (this._compare(cur.key, newNode.key) === CP.gt) { @@ -115,7 +114,7 @@ export class TreeMultimap = TreeMultim //Add to the left of the current node cur.left = newNode; this._size = this.size + 1; - this._setCount(this.count + newNode.count); + this._count += newNode.count; traversing = false; inserted = cur.left; @@ -129,7 +128,7 @@ export class TreeMultimap = TreeMultim //Add to the right of the current node cur.right = newNode; this._size = this.size + 1; - this._setCount(this.count + newNode.count); + this._count += newNode.count; traversing = false; inserted = cur.right; @@ -158,13 +157,14 @@ export class TreeMultimap = TreeMultim * be added as a child. * @returns The method `_addTo` returns either the `parent.left`, `parent.right`, or `undefined`. */ - override _addTo(newNode: N | undefined, parent: N): N | undefined { + protected override _addTo(newNode: N | undefined, parent: BTNKey | N | undefined): N | undefined { + parent = this.ensureNotKey(parent); if (parent) { if (parent.left === undefined) { parent.left = newNode; if (newNode !== undefined) { this._size = this.size + 1; - this._setCount(this.count + newNode.count); + this._count += newNode.count; } return parent.left; @@ -172,7 +172,7 @@ export class TreeMultimap = TreeMultim parent.right = newNode; if (newNode !== undefined) { this._size = this.size + 1; - this._setCount(this.count + newNode.count); + this._count += newNode.count; } return parent.right; } else { @@ -193,8 +193,8 @@ export class TreeMultimap = TreeMultim * each key or node. * @returns The function `addMany` returns an array of `N`, `undefined`, or `undefined` values. */ - override addMany(keysOrNodes: (BTNKey | undefined)[] | (N | undefined)[], data?: V[]): (N | undefined)[] { - const inserted: (N | undefined | undefined)[] = []; + override addMany(keysOrNodes: (BTNKey | N | undefined)[], data?: V[]): (N | undefined)[] { + const inserted: (N | undefined)[] = []; for (let i = 0; i < keysOrNodes.length; i++) { const keyOrNode = keysOrNodes[i]; @@ -269,7 +269,7 @@ export class TreeMultimap = TreeMultim * @param callback - The `callback` parameter is a function that takes a node as input and returns a * value. This value is compared with the `identifier` parameter to determine if the node should be * included in the result. The `callback` parameter has a default value of - * `this.defaultOneParamCallback` + * `this._defaultOneParamCallback` * @param [ignoreCount=false] - A boolean flag indicating whether to ignore the count of the node * 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 @@ -278,22 +278,21 @@ export class TreeMultimap = TreeMultim */ override delete>( identifier: ReturnType, - callback: C = this.defaultOneParamCallback as C, + callback: C = this._defaultOneParamCallback as C, ignoreCount = false ): BiTreeDeleteResult[] { - const bstDeletedResult: BiTreeDeleteResult[] = []; - if (!this.root) return bstDeletedResult; + const deletedResult: BiTreeDeleteResult[] = []; + if (!this.root) return deletedResult; const curr: N | undefined = this.getNode(identifier, callback) ?? undefined; - if (!curr) return bstDeletedResult; + if (!curr) return deletedResult; const parent: N | undefined = curr?.parent ? curr.parent : undefined; - let needBalanced: N | undefined = undefined, - orgCurrent = curr; + let needBalanced: N | undefined = undefined,orgCurrent: N | undefined = curr; if (curr.count > 1 && !ignoreCount) { curr.count--; - this._setCount(this.count - 1); + this._count--; } else { if (!curr.left) { if (!parent) { @@ -324,24 +323,24 @@ export class TreeMultimap = TreeMultim } this._size = this.size - 1; // TODO How to handle when the count of target node is lesser than current node's count - this._setCount(this.count - orgCurrent.count); + if (orgCurrent) this._count -= orgCurrent.count; } - bstDeletedResult.push({deleted: orgCurrent, needBalanced}); + deletedResult.push({deleted: orgCurrent, needBalanced}); if (needBalanced) { this._balancePath(needBalanced); } - return bstDeletedResult; + return deletedResult; } /** * The clear() function clears the contents of a data structure and sets the count to zero. */ - clear() { + override clear() { super.clear(); - this._setCount(0); + this._count = 0; } /** @@ -351,31 +350,28 @@ export class TreeMultimap = TreeMultim * from `srcNode` will be swapped into. * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. */ - protected override _swap(srcNode: N, destNode: N): N { - const {key, value, count, height} = destNode; - const tempNode = this.createNode(key, value, count); - if (tempNode) { - tempNode.height = height; + protected _swap(srcNode: BTNKey | N | undefined, destNode:BTNKey | N | undefined): N | undefined{ + srcNode = this.ensureNotKey(srcNode); + destNode = this.ensureNotKey(destNode); + if (srcNode && destNode) { + const {key, value, count, height} = destNode; + const tempNode = this.createNode(key, value, count); + if (tempNode) { + tempNode.height = height; - destNode.key = srcNode.key; - destNode.value = srcNode.value; - destNode.count = srcNode.count; - destNode.height = srcNode.height; + destNode.key = srcNode.key; + destNode.value = srcNode.value; + destNode.count = srcNode.count; + destNode.height = srcNode.height; - srcNode.key = tempNode.key; - srcNode.value = tempNode.value; - srcNode.count = tempNode.count; - srcNode.height = tempNode.height; + srcNode.key = tempNode.key; + srcNode.value = tempNode.value; + srcNode.count = tempNode.count; + srcNode.height = tempNode.height; + } + + return destNode; } - - return destNode; - } - - /** - * The function sets the value of the "_count" property. - * @param {number} v - number - */ - protected _setCount(v: number) { - this._count = v; + return undefined; } } diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index 95e11c9..e13172d 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -29,3 +29,9 @@ export type BiTreeDeleteResult = { deleted: N | null | undefined; needBalance export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BinaryTreeOptions = { iterationType?: IterationType } +// +// export type BTNIdentifierOrNU = BTNKey | N | null | undefined; +// +// export type BTNIdentifierOrU = BTNKey | N | undefined; +// +// export type BTNOrNU = N | null | undefined; diff --git a/src/types/data-structures/binary-tree/rb-tree.ts b/src/types/data-structures/binary-tree/rb-tree.ts index 859bdc8..bdc5bed 100644 --- a/src/types/data-structures/binary-tree/rb-tree.ts +++ b/src/types/data-structures/binary-tree/rb-tree.ts @@ -1,8 +1,8 @@ -import {RBTreeNode} from '../../../data-structures'; +import {RedBlackTreeNode} from '../../../data-structures'; import {BSTOptions} from "./bst"; export enum RBTNColor { RED = 1, BLACK = 0} -export type RBTreeNodeNested = RBTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type RedBlackTreeNodeNested = RedBlackTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type RBTreeOptions = BSTOptions & {}; \ No newline at end of file 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 afa2202..0489f1b 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -1,4 +1,4 @@ -import {IterationType, RBTNColor, RBTreeNode, RedBlackTree} from '../../../../src'; +import {IterationType, RBTNColor, RedBlackTreeNode, RedBlackTree} from '../../../../src'; import {getRandomInt} from '../../../utils'; import {isDebugTest} from '../../../config'; @@ -17,9 +17,9 @@ describe('RedBlackTree', () => { tree.add(20); tree.add(5); - expect(tree.getNode(10)).toBeInstanceOf(RBTreeNode); - expect(tree.getNode(20)).toBeInstanceOf(RBTreeNode); - expect(tree.getNode(5)).toBeInstanceOf(RBTreeNode); + expect(tree.getNode(10)).toBeInstanceOf(RedBlackTreeNode); + expect(tree.getNode(20)).toBeInstanceOf(RedBlackTreeNode); + expect(tree.getNode(5)).toBeInstanceOf(RedBlackTreeNode); expect(tree.getNode(15)).toBe(undefined); }); @@ -27,8 +27,8 @@ describe('RedBlackTree', () => { tree.add(-10); tree.add(-20); - expect(tree.getNode(-10)).toBeInstanceOf(RBTreeNode); - expect(tree.getNode(-20)).toBeInstanceOf(RBTreeNode); + expect(tree.getNode(-10)).toBeInstanceOf(RedBlackTreeNode); + expect(tree.getNode(-20)).toBeInstanceOf(RedBlackTreeNode); }); }); @@ -61,7 +61,7 @@ describe('RedBlackTree', () => { tree.add(3); const minNode = tree.getLeftMost(tree.root); - expect(minNode.key).toBe(3); + expect(minNode?.key).toBe(3); }); test('should handle an empty tree', () => { @@ -79,7 +79,7 @@ describe('RedBlackTree', () => { tree.add(25); const maxNode = tree.getRightMost(tree.root); - expect(maxNode.key).toBe(25); + expect(maxNode?.key).toBe(25); }); test('should handle an empty tree', () => { @@ -387,7 +387,7 @@ describe('RedBlackTree', () => { tree.add(15); const nodeLM = tree.getLeftMost(); - expect(nodeLM.key).toBe(1); + expect(nodeLM?.key).toBe(1); const node50 = tree.getNode(50); expect(node50?.key).toBe(50);