From 28207b6d45ee6fe88fe73b938faf9e8cbf139b2a Mon Sep 17 00:00:00 2001 From: Revone Date: Thu, 9 Nov 2023 17:47:02 +0800 Subject: [PATCH] perf: Unified all APIs, all parameters that accept node types can also accept node keys as parameters. --- CHANGELOG.md | 2 +- README.md | 244 +++++++++--------- src/data-structures/binary-tree/avl-tree.ts | 38 +-- .../binary-tree/binary-tree.ts | 214 +++++++++------ src/data-structures/binary-tree/bst.ts | 47 +++- src/data-structures/binary-tree/rb-tree.ts | 6 +- .../binary-tree/tree-multimap.ts | 8 +- src/interfaces/binary-tree.ts | 4 +- .../binary-tree/binary-tree.ts | 2 +- 9 files changed, 326 insertions(+), 239 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f772f9..bcddaf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - [Semantic Versioning](https://semver.org/spec/v2.0.0.html) - [`auto-changelog`](https://github.com/CookPete/auto-changelog) -## [v1.42.5](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming) +## [v1.42.6](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming) ### Changes diff --git a/README.md b/README.md index 71bc476..9813be8 100644 --- a/README.md +++ b/README.md @@ -25,108 +25,6 @@ Now you can use this library in Node.js and browser environments in CommonJS(req [//]: # (![Lines](https://img.shields.io/badge/lines-68.6%25-red.svg?style=flat)) -## Built-in classic algorithms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AlgorithmFunction DescriptionIteration Type
Binary Tree DFSTraverse a binary tree in a depth-first manner, starting from the root node, first visiting the left subtree, - and then the right subtree, using recursion. - Recursion + Iteration
Binary Tree BFSTraverse a binary tree in a breadth-first manner, starting from the root node, visiting nodes level by level - from left to right. - Iteration
Graph DFSTraverse a graph in a depth-first manner, starting from a given node, exploring along one path as deeply as - possible, and backtracking to explore other paths. Used for finding connected components, paths, etc. - Recursion + Iteration
Binary Tree MorrisMorris traversal is an in-order traversal algorithm for binary trees with O(1) space complexity. It allows tree - traversal without additional stack or recursion. - Iteration
Graph BFSTraverse a graph in a breadth-first manner, starting from a given node, first visiting nodes directly connected - to the starting node, and then expanding level by level. Used for finding shortest paths, etc. - Recursion + Iteration
Graph Tarjan's AlgorithmFind strongly connected components in a graph, typically implemented using depth-first search.Recursion
Graph Bellman-Ford AlgorithmFinding the shortest paths from a single source, can handle negative weight edgesIteration
Graph Dijkstra's AlgorithmFinding the shortest paths from a single source, cannot handle negative weight edgesIteration
Graph Floyd-Warshall AlgorithmFinding the shortest paths between all pairs of nodesIteration
Graph getCyclesFind all cycles in a graph or detect the presence of cycles.Recursion
Graph getCutVertexesFind cut vertices in a graph, which are nodes that, when removed, increase the number of connected components in - the graph. - Recursion
Graph getSCCsFind strongly connected components in a graph, which are subgraphs where any two nodes can reach each other. - Recursion
Graph getBridgesFind bridges in a graph, which are edges that, when removed, increase the number of connected components in the - graph. - Recursion
Graph topologicalSortPerform topological sorting on a directed acyclic graph (DAG) to find a linear order of nodes such that all - directed edges go from earlier nodes to later nodes. - Recursion
- - ## Installation and Usage ### npm @@ -174,13 +72,6 @@ const { ![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/directed-graph-test.webp) ![](https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/examples/videos/webp_output/map-graph-test.webp) -## API docs & Examples - -[API Docs](https://data-structure-typed-docs.vercel.app) - -[Live Examples](https://vivid-algorithm.vercel.app) - -Examples Repository ## Code Snippets @@ -338,6 +229,115 @@ const dijkstraResult = graph.dijkstra('A'); Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', 'D'] ``` +## Built-in classic algorithms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AlgorithmFunction DescriptionIteration Type
Binary Tree DFSTraverse a binary tree in a depth-first manner, starting from the root node, first visiting the left subtree, + and then the right subtree, using recursion. + Recursion + Iteration
Binary Tree BFSTraverse a binary tree in a breadth-first manner, starting from the root node, visiting nodes level by level + from left to right. + Iteration
Graph DFSTraverse a graph in a depth-first manner, starting from a given node, exploring along one path as deeply as + possible, and backtracking to explore other paths. Used for finding connected components, paths, etc. + Recursion + Iteration
Binary Tree MorrisMorris traversal is an in-order traversal algorithm for binary trees with O(1) space complexity. It allows tree + traversal without additional stack or recursion. + Iteration
Graph BFSTraverse a graph in a breadth-first manner, starting from a given node, first visiting nodes directly connected + to the starting node, and then expanding level by level. Used for finding shortest paths, etc. + Recursion + Iteration
Graph Tarjan's AlgorithmFind strongly connected components in a graph, typically implemented using depth-first search.Recursion
Graph Bellman-Ford AlgorithmFinding the shortest paths from a single source, can handle negative weight edgesIteration
Graph Dijkstra's AlgorithmFinding the shortest paths from a single source, cannot handle negative weight edgesIteration
Graph Floyd-Warshall AlgorithmFinding the shortest paths between all pairs of nodesIteration
Graph getCyclesFind all cycles in a graph or detect the presence of cycles.Recursion
Graph getCutVertexesFind cut vertices in a graph, which are nodes that, when removed, increase the number of connected components in + the graph. + Recursion
Graph getSCCsFind strongly connected components in a graph, which are subgraphs where any two nodes can reach each other. + Recursion
Graph getBridgesFind bridges in a graph, which are edges that, when removed, increase the number of connected components in the + graph. + Recursion
Graph topologicalSortPerform topological sorting on a directed acyclic graph (DAG) to find a linear order of nodes such that all + directed edges go from earlier nodes to later nodes. + Recursion
+ +## API docs & Examples + +[API Docs](https://data-structure-typed-docs.vercel.app) + +[Live Examples](https://vivid-algorithm.vercel.app) + +Examples Repository + ## Data Structures @@ -376,7 +376,7 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', ' - + @@ -728,40 +728,40 @@ optimal approach to data structure design. [//]: # (No deletion!!! Start of Replace Section)
avl-tree
-
Red Black Tree AVLTreeRedBlackTree
test nametime taken (ms)executions per secsample deviation
10,000 add randomly30.5232.763.28e-4
10,000 add & delete randomly66.9614.940.00
10,000 addMany39.7825.143.67e-4
10,000 get27.3836.520.00
+
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
binary-tree
-
test nametime taken (ms)executions per secsample deviation
1,000 add randomly10.5095.202.30e-4
1,000 add & delete randomly16.1861.812.48e-4
1,000 addMany10.8092.621.83e-4
1,000 get18.0355.451.41e-4
1,000 dfs157.866.330.00
1,000 bfs56.6817.640.00
1,000 morris37.2126.882.79e-4
+
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
bst
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly27.6136.214.73e-4
10,000 add & delete randomly62.9315.895.86e-4
10,000 addMany28.7034.840.00
10,000 get27.6736.142.92e-4
+
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
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add randomly87.5111.430.01
100,000 add & delete randomly189.065.290.01
100,000 getNode35.3328.318.93e-4
+
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
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109899.918.58e-7
1,000 addEdge6.06165.021.68e-4
1,000 getVertex0.052.17e+44.22e-7
1,000 getEdge23.0543.380.00
tarjan222.594.490.01
tarjan all226.894.410.01
topologicalSort187.345.340.01
+
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
heap
-
test nametime taken (ms)executions per secsample deviation
10,000 add & pop4.66214.549.38e-5
10,000 fib add & pop364.302.740.01
+
test nametime taken (ms)executions per secsample deviation
10,000 add & pop4.61216.994.19e-5
10,000 fib add & pop354.792.820.00
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 unshift243.614.100.07
1,000,000 unshift & shift173.325.770.03
1,000,000 insertBefore315.863.170.04
+
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
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
10,000 push & pop228.064.380.03
10,000 insertBefore252.073.970.01
+
test nametime taken (ms)executions per secsample deviation
10,000 push & pop220.474.540.01
10,000 insertBefore252.593.960.00
max-priority-queue
-
test nametime taken (ms)executions per secsample deviation
10,000 refill & poll11.5386.712.27e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 refill & poll11.7285.322.97e-4
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push227.244.400.07
1,000,000 shift25.6039.070.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push233.904.280.07
1,000,000 shift25.4039.370.00
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.9821.750.01
1,000,000 push & shift81.1212.330.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push43.5422.970.00
1,000,000 push & shift83.9911.910.00
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push59.4016.830.01
100,000 getWords90.0711.100.00
+
test nametime taken (ms)executions per secsample deviation
100,000 push49.1720.340.01
100,000 getWords88.8411.260.01
[//]: # (No deletion!!! End of Replace Section) \ No newline at end of file diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 8e72d8a..a3c4390 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -6,7 +6,7 @@ * @license MIT License */ import {BST, BSTNode} from './bst'; -import type {AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeletedResult, BTNKey} from '../../types'; +import type {AVLTreeNodeNested, AVLTreeOptions, BiTreeDeleteResult, BTNKey} from '../../types'; import {BTNCallback} from '../../types'; import {IBinaryTree} from '../../interfaces'; @@ -72,12 +72,12 @@ export class AVLTree = AVLTreeNode` objects. + * @returns The method is returning an array of `BiTreeDeleteResult` objects. */ override delete>( identifier: ReturnType, callback: C = this.defaultOneParamCallback as C - ): BinaryTreeDeletedResult[] { + ): BiTreeDeleteResult[] { if ((identifier as any) instanceof AVLTreeNode) callback = (node => node) as C; const deletedResults = super.delete(identifier, callback); for (const {needBalanced} of deletedResults) { @@ -96,23 +96,29 @@ export class AVLTree = AVLTreeNode = BinaryTreeNode * @param {BinaryTreeOptions} [options] - The options for the binary tree. */ constructor(options?: BinaryTreeOptions) { - if (options !== undefined) { + if (options) { const {iterationType = IterationType.ITERATIVE} = options; this.iterationType = iterationType; } @@ -192,7 +192,7 @@ export class BinaryTree = BinaryTreeNode return; }; - let inserted: N | null | undefined, needInsert: N | null; + let inserted: N | null | undefined, needInsert: N | null | undefined; if (keyOrNode === null) { needInsert = null; @@ -204,19 +204,11 @@ export class BinaryTree = BinaryTreeNode return; } - // const key = typeof keyOrNode === 'number' ? keyOrNode : keyOrNode ? keyOrNode.key : undefined; - // const existNode = key !== undefined ? this.getNode(key, (node: N) => node.key) : undefined; - if (this.root) { - // if (existNode) { - // existNode.value = value; - // inserted = existNode; - // } else { inserted = _bfs(this.root, needInsert); - // } } else { this._setRoot(needInsert); - if (needInsert !== null) { + if (needInsert) { this._size = 1; } else { this._size = 0; @@ -236,7 +228,7 @@ export class BinaryTree = BinaryTreeNode * the value of the nodes will be `undefined`. * @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values. */ - addMany(keysOrNodes: (BTNKey | null | undefined)[] | (N | null | undefined)[], values?: V[]): (N | null | undefined)[] { + addMany(keysOrNodes: (BTNKey | N |null | undefined)[], values?: (V | undefined)[]): (N | null | undefined)[] { // TODO not sure addMany not be run multi times return keysOrNodes.map((keyOrNode, i) => { if (keyOrNode instanceof BinaryTreeNode) { @@ -256,28 +248,28 @@ export class BinaryTree = BinaryTreeNode * The `refill` function clears the binary tree and adds multiple nodes with the given IDs or nodes and optional data. * @param {(BTNKey | N)[]} keysOrNodes - The `keysOrNodes` parameter is an array that can contain either * `BTNKey` or `N` values. - * @param {N[] | Array} [data] - The `data` parameter is an optional array of values that will be assigned to + * @param {N[] | Array} [values] - The `data` parameter is an optional array of values that will be assigned to * the nodes being added. If provided, the length of the `data` array should be equal to the length of the `keysOrNodes` * 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)[], data?: Array): boolean { + refill(keysOrNodes: (BTNKey | null | undefined)[] | (N | null | undefined)[], values?: Array): boolean { this.clear(); - return keysOrNodes.length === this.addMany(keysOrNodes, data).length; + return keysOrNodes.length === this.addMany(keysOrNodes, values).length; } - delete>(identifier: BTNKey, callback?: C): BinaryTreeDeletedResult[]; + delete>(identifier: BTNKey, callback?: C): BiTreeDeleteResult[]; - delete>(identifier: N | null | undefined, callback?: C): BinaryTreeDeletedResult[]; + delete>(identifier: N | null | undefined, callback?: C): BiTreeDeleteResult[]; - delete>(identifier: ReturnType, callback: C): BinaryTreeDeletedResult[]; + delete>(identifier: ReturnType, callback: C): BiTreeDeleteResult[]; /** * The `delete` function removes a node from a binary search tree and returns the deleted node along * with the parent node that needs to be balanced. * a key (`BTNKey`). If it is a key, the function will find the corresponding node in the * binary tree. - * @returns an array of `BinaryTreeDeletedResult` objects. + * @returns an array of `BiTreeDeleteResult` objects. * @param {ReturnType} identifier - The `identifier` parameter is either a * `BTNKey` or a generic type `N`. It represents the property of the node that we are * searching for. It can be a specific key value or any other property of the node. @@ -289,17 +281,17 @@ export class BinaryTree = BinaryTreeNode delete>( identifier: ReturnType | null | undefined, callback: C = this.defaultOneParamCallback as C - ): BinaryTreeDeletedResult[] { - const bstDeletedResult: BinaryTreeDeletedResult[] = []; - if (!this.root) return bstDeletedResult; + ): BiTreeDeleteResult[] { + const deleteResult: BiTreeDeleteResult[] = []; + if (!this.root) return deleteResult; if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; const curr = this.getNode(identifier, callback); - if (!curr) return bstDeletedResult; + if (!curr) return deleteResult; const parent: N | null | undefined = curr?.parent ? curr.parent : null; - let needBalanced: N | null | undefined = null, - orgCurrent = curr; + let needBalanced: N | null | undefined = undefined; + let orgCurrent: N | undefined = curr; if (!curr.left) { if (!parent) { @@ -329,8 +321,8 @@ export class BinaryTree = BinaryTreeNode } this._size = this.size - 1; - bstDeletedResult.push({deleted: orgCurrent, needBalanced}); - return bstDeletedResult; + deleteResult.push({deleted: orgCurrent, needBalanced}); + return deleteResult; } /** @@ -347,7 +339,7 @@ export class BinaryTree = BinaryTreeNode */ getDepth(distNode: BTNKey | N | null | undefined, beginRoot: BTNKey | N | null | undefined = this.root): number { if (typeof distNode === 'number') distNode = this.getNode(distNode); - if (typeof beginRoot === 'number') beginRoot = this.getNode(beginRoot); + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); let depth = 0; while (distNode?.parent) { if (distNode === beginRoot) { @@ -372,7 +364,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 (typeof beginRoot === 'number') beginRoot = this.getNode(beginRoot); + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { @@ -420,9 +412,11 @@ export class BinaryTree = BinaryTreeNode * 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: N | null | undefined = this.root, iterationType = this.iterationType): number { + getMinHeight(beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType): number { if (!beginRoot) return -1; - + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); + if (!beginRoot) return -1; + if (iterationType === IterationType.RECURSIVE) { const _getMinHeight = (cur: N | null | undefined): number => { if (!cur) return 0; @@ -469,7 +463,7 @@ export class BinaryTree = BinaryTreeNode * either be of type `N` (representing a node in a tree) or `null` (representing an empty tree). * @returns The method is returning a boolean value. */ - isPerfectlyBalanced(beginRoot: N | null | undefined = this.root): boolean { + isPerfectlyBalanced(beginRoot: BTNKey | N | null | undefined = this.root): boolean { return this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot); } @@ -477,7 +471,7 @@ export class BinaryTree = BinaryTreeNode identifier: BTNKey, callback?: C, onlyOne?: boolean, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): N[]; @@ -485,7 +479,7 @@ export class BinaryTree = BinaryTreeNode identifier: N | null | undefined, callback?: C, onlyOne?: boolean, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): N[]; @@ -493,7 +487,7 @@ export class BinaryTree = BinaryTreeNode identifier: ReturnType, callback: C, onlyOne?: boolean, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): N[]; @@ -522,11 +516,14 @@ export class BinaryTree = BinaryTreeNode identifier: ReturnType | null | undefined, callback: C = this.defaultOneParamCallback as C, onlyOne = false, - beginRoot: N | null | undefined = this.root, + 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); + if (!beginRoot) return []; + const ans: N[] = []; if (iterationType === IterationType.RECURSIVE) { @@ -562,21 +559,21 @@ export class BinaryTree = BinaryTreeNode has>( identifier: BTNKey, callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): boolean; has>( identifier: N | null | undefined, callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): boolean; has>( identifier: ReturnType | null | undefined, callback: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): boolean; @@ -600,7 +597,7 @@ export class BinaryTree = BinaryTreeNode has>( identifier: ReturnType | null | undefined, callback: C = this.defaultOneParamCallback as C, - beginRoot = this.root, + beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): boolean { if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; @@ -611,21 +608,21 @@ export class BinaryTree = BinaryTreeNode getNode>( identifier: BTNKey, callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): N | null | undefined; getNode>( identifier: N | null | undefined, callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): N | null | undefined; getNode>( identifier: ReturnType, callback: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): N | null | undefined; @@ -647,7 +644,7 @@ export class BinaryTree = BinaryTreeNode getNode>( identifier: ReturnType | null | undefined, callback: C = this.defaultOneParamCallback as C, - beginRoot = this.root, + beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): N | null | undefined { if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; @@ -655,24 +652,49 @@ export class BinaryTree = BinaryTreeNode return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null; } + protected _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 (cur.left) return _dfs(cur.left); + if (cur.right) return _dfs(cur.right); + }; + + return _dfs(this.root); + } else { + const queue = new Queue([this.root]); + while (queue.size > 0) { + const cur = queue.shift(); + if (cur) { + if (cur.key === key) return cur; + cur.left && queue.push(cur.left); + cur.right && queue.push(cur.right); + } + } + } + } + get>( identifier: BTNKey, callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): V | undefined; get>( identifier: N | null | undefined, callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): V | undefined; get>( identifier: ReturnType, callback: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType ): V | undefined; @@ -694,14 +716,14 @@ export class BinaryTree = BinaryTreeNode get>( identifier: ReturnType | null | undefined, callback: C = this.defaultOneParamCallback as C, - beginRoot = this.root, + beginRoot:BTNKey | N | null | undefined = this.root, iterationType = this.iterationType ): V | undefined { if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; return this.getNode(identifier, callback, beginRoot, iterationType)?.value ?? undefined; } - + /** * 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. @@ -712,9 +734,13 @@ export class BinaryTree = BinaryTreeNode * reversed before returning it. If `isReverse` is set to `false` or not provided, the path will * @returns The function `getPathToRoot` returns an array of type `N[]`. */ - getPathToRoot(beginRoot: N, isReverse = true): N[] { + 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); + + if (!beginRoot) return result; + while (beginRoot.parent) { // Array.push + Array.reverse is more efficient than Array.unshift // TODO may consider using Deque, so far this is not the performance bottleneck @@ -737,7 +763,7 @@ 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 (typeof beginRoot === 'number') beginRoot = this.getNode(beginRoot); + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return beginRoot; @@ -770,8 +796,9 @@ export class BinaryTree = BinaryTreeNode * @returns The function `getRightMost` returns the rightmost node (`N`) in a binary tree. If the * `beginRoot` parameter is `null`, it returns `null`. */ - getRightMost(beginRoot: N | null | undefined = this.root, iterationType = this.iterationType): N | null | undefined { + 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); if (!beginRoot) return beginRoot; if (iterationType === IterationType.RECURSIVE) { @@ -801,8 +828,9 @@ export class BinaryTree = BinaryTreeNode * possible values: * @returns The function `isSubtreeBST` returns a boolean value. */ - isSubtreeBST(beginRoot: N | null | undefined, iterationType = this.iterationType): boolean { + isSubtreeBST(beginRoot: BTNKey | N | null | undefined, iterationType = this.iterationType): boolean { // TODO there is a bug + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return true; if (iterationType === IterationType.RECURSIVE) { @@ -886,7 +914,7 @@ export class BinaryTree = BinaryTreeNode iterationType = this.iterationType, includeNull = false ): ReturnType[] { - if (typeof beginRoot === 'number') beginRoot = this.getNode(beginRoot); + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); const ans: (ReturnType> | null | undefined)[] = []; if (!beginRoot) return ans; @@ -938,10 +966,14 @@ export class BinaryTree = BinaryTreeNode return this.isNode(node) || node === null; } + isNodeKey(potentialKey: any) : potentialKey is number { + return typeof potentialKey === 'number'; + } + dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: false ): ReturnType[]; @@ -949,7 +981,7 @@ export class BinaryTree = BinaryTreeNode dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: undefined ): ReturnType[]; @@ -957,7 +989,7 @@ export class BinaryTree = BinaryTreeNode dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -981,10 +1013,12 @@ export class BinaryTree = BinaryTreeNode dfs>( callback: C = this.defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', - beginRoot: N | null | undefined = this.root, + beginRoot: BTNKey | N | null | undefined = this.root, iterationType: IterationType = IterationType.ITERATIVE, includeNull = false ): ReturnType[] { + + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return []; const ans: ReturnType[] = []; if (iterationType === IterationType.RECURSIVE) { @@ -1074,21 +1108,21 @@ export class BinaryTree = BinaryTreeNode bfs>( callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: false ): ReturnType[]; bfs>( callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: undefined ): ReturnType[]; bfs>( callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1109,10 +1143,11 @@ export class BinaryTree = BinaryTreeNode */ bfs>( callback: C = this.defaultOneParamCallback as C, - beginRoot: N | null | undefined = this.root, + beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[] { + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return []; const ans: ReturnType>[] = []; @@ -1162,21 +1197,21 @@ export class BinaryTree = BinaryTreeNode listLevels>( callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: false ): ReturnType[][]; listLevels>( callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: undefined ): ReturnType[][]; listLevels>( callback?: C, - beginRoot?: N | null | undefined, + beginRoot?: BTNKey | N | null | undefined, iterationType?: IterationType, includeNull?: true ): ReturnType[][]; @@ -1199,10 +1234,11 @@ export class BinaryTree = BinaryTreeNode */ listLevels>( callback: C = this.defaultOneParamCallback as C, - beginRoot: N | null | undefined = this.root, + beginRoot: BTNKey | N | null | undefined = this.root, iterationType = this.iterationType, includeNull = false ): ReturnType[][] { + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return []; const levelsNodes: ReturnType[][] = []; @@ -1243,12 +1279,17 @@ export class BinaryTree = BinaryTreeNode return levelsNodes; } + getPredecessor(node: N ): N + /** * The function returns the predecessor node of a given node in a binary tree. * @param {N} node - The parameter "node" represents a node in a binary tree. * @returns The function `getPredecessor` returns the predecessor node of the given node `node`. */ - getPredecessor(node: N): N { + getPredecessor(node: BTNKey | N | null | undefined): N | undefined{ + if (this.isNodeKey(node)) node = this.getNode(node); + if (!node) return undefined; + if (node.left) { let predecessor: N | null | undefined = node.left; while (!predecessor || (predecessor.right && predecessor.right !== node)) { @@ -1269,7 +1310,10 @@ 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: N): N | null | undefined { + getSuccessor(x: BTNKey | N | null | undefined): N | null | undefined { + if (this.isNodeKey(x)) x = this.getNode(x); + if (!x) return undefined; + if (x.right) { return this.getLeftMost(x.right); } @@ -1299,8 +1343,9 @@ export class BinaryTree = BinaryTreeNode morris>( callback: C = this.defaultOneParamCallback as C, pattern: DFSOrderPattern = 'in', - beginRoot: N | null | undefined = this.root + beginRoot: BTNKey | N | null | undefined = this.root ): ReturnType[] { + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (beginRoot === null) return []; const ans: ReturnType>[] = []; @@ -1433,19 +1478,26 @@ export class BinaryTree = BinaryTreeNode * @param {N} destNode - The destination node to swap. * @returns {N} - The destination node after the swap. */ - protected _swap(srcNode: N, destNode: N): N { - const {key, value} = destNode; - const tempNode = this.createNode(key, value); + 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); - if (tempNode) { - destNode.key = srcNode.key; - destNode.value = srcNode.value; + if (srcNode && destNode) { + const {key, value} = destNode; + const tempNode = this.createNode(key, value); - srcNode.key = tempNode.key; - srcNode.value = tempNode.value; + if (tempNode) { + destNode.key = srcNode.key; + destNode.value = srcNode.value; + + srcNode.key = tempNode.key; + srcNode.value = tempNode.value; + } + + return destNode; } + return undefined; - return destNode; } /** @@ -1459,7 +1511,9 @@ export class BinaryTree = BinaryTreeNode * the binary tree. If neither the left nor right child is available, the function returns undefined. * If the parent node is null, the function also returns undefined. */ - protected _addTo(newNode: N | null | undefined, parent: N): N | null | undefined { + protected _addTo(newNode: N | null | undefined, parent: BTNKey | N | null | undefined): N | null | undefined { + if (this.isNodeKey(parent)) parent = this.getNode(parent); + if (parent) { // When all leaf nodes are null, it will no longer be possible to add new entity nodes to this binary tree. // In this scenario, null nodes serve as "sentinel nodes," "virtual nodes," or "placeholder nodes." diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 7fb27ab..13080ac 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -13,6 +13,7 @@ import {Queue} from '../queue'; export class BSTNode = BSTNodeNested> extends BinaryTreeNode { override parent: N | undefined; + constructor(key: BTNKey, value?: V) { super(key, value); this.parent = undefined; @@ -64,8 +65,7 @@ export class BSTNode = BSTNodeNested> extend export class BST = BSTNode>> extends BinaryTree - implements IBinaryTree -{ + implements IBinaryTree { /** * The constructor function initializes a binary search tree object with an optional comparator * function. @@ -82,6 +82,7 @@ export class BST = BSTNode> } } } + protected override _root: N | undefined = undefined; /** @@ -119,8 +120,8 @@ export class BST = BSTNode> } if (keyOrNode === null) return undefined; // TODO support node as a parameter - let inserted:N | undefined; - let newNode:N | undefined; + let inserted: N | undefined; + let newNode: N | undefined; if (keyOrNode instanceof BSTNode) { newNode = keyOrNode; } else if (typeof keyOrNode === 'number') { @@ -198,13 +199,13 @@ export class BST = BSTNode> */ override addMany( - keysOrNodes: (BTNKey | undefined)[] | (N | undefined)[], - data?: V[], + keysOrNodes: (BTNKey | N | undefined)[], + data?: (V | undefined)[], isBalanceAdd = true, iterationType = this.iterationType ): (N | undefined)[] { // TODO this addMany function is inefficient, it should be optimized - function hasNoNull(arr: (BTNKey | undefined)[] | (N | undefined)[]): arr is BTNKey[] | N[] { + function hasNoNull(arr: (BTNKey | N | undefined)[]): arr is (BTNKey | N)[] { return arr.indexOf(undefined) === -1; } @@ -289,12 +290,37 @@ export class BST = BSTNode> * 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: N | undefined = this.root, iterationType = this.iterationType): BTNKey { + lastKey(beginRoot: BTNKey | N | undefined = this.root, iterationType = this.iterationType): BTNKey { if (this._compare(0, 1) === CP.lt) return this.getRightMost(beginRoot, iterationType)?.key ?? 0; else if (this._compare(0, 1) === CP.gt) return this.getLeftMost(beginRoot, iterationType)?.key ?? 0; else return this.getRightMost(beginRoot, iterationType)?.key ?? 0; } + protected 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); + }; + + return _dfs(this.root); + } else { + const queue = new Queue([this.root]); + while (queue.size > 0) { + const cur = queue.shift(); + if (cur) { + if (this._compare(cur.key, key) === CP.eq) return cur; + if (this._compare(cur.key, key) === CP.gt) cur.left && queue.push(cur.left); + if (this._compare(cur.key, key) === CP.lt) cur.right && queue.push(cur.right); + } + } + } + } + /** * The function `getNodes` retrieves nodes from a binary tree based on a given node property or key, * using either recursive or iterative traversal. @@ -320,9 +346,10 @@ export class BST = BSTNode> identifier: ReturnType | undefined, callback: C = this.defaultOneParamCallback as C, onlyOne = false, - beginRoot: N | undefined = this.root, + beginRoot: BTNKey | N | undefined = this.root, iterationType = this.iterationType ): N[] { + if (this.isNodeKey(beginRoot)) beginRoot = this._getNodeByKey(beginRoot); if (!beginRoot) return []; const ans: N[] = []; @@ -509,7 +536,7 @@ export class BST = BSTNode> } else { const stack: N[] = []; let node: N | undefined = this.root, - last:N | undefined = undefined; + last: N | undefined = undefined; const depths: Map = new Map(); while (stack.length > 0 || node) { diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index c0356c0..57551fb 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -7,7 +7,7 @@ */ import { - BinaryTreeDeletedResult, + BiTreeDeleteResult, BTNCallback, BTNKey, IterationType, @@ -119,8 +119,8 @@ export class RedBlackTree = RBTreeNode>( identifier: ReturnType | null | undefined, callback: C = this.defaultOneParamCallback as C - ): BinaryTreeDeletedResult[] { - const ans: BinaryTreeDeletedResult[] = []; + ): BiTreeDeleteResult[] { + const ans: BiTreeDeleteResult[] = []; if (identifier === null) return ans; const helper = (node: N | undefined): void => { let z: N = this.NIL; diff --git a/src/data-structures/binary-tree/tree-multimap.ts b/src/data-structures/binary-tree/tree-multimap.ts index 7b13e84..9fe9820 100644 --- a/src/data-structures/binary-tree/tree-multimap.ts +++ b/src/data-structures/binary-tree/tree-multimap.ts @@ -6,7 +6,7 @@ * @license MIT License */ import type {BTNKey, TreeMultimapNodeNested, TreeMultimapOptions} from '../../types'; -import {BinaryTreeDeletedResult, BTNCallback, CP, FamilyPosition, IterationType} from '../../types'; +import {BiTreeDeleteResult, BTNCallback, CP, FamilyPosition, IterationType} from '../../types'; import {IBinaryTree} from '../../interfaces'; import {AVLTree, AVLTreeNode} from './avl-tree'; @@ -274,14 +274,14 @@ export class TreeMultimap = TreeMultim * 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 The method `delete` returns an array of `BinaryTreeDeletedResult` objects. + * @returns The method `delete` returns an array of `BiTreeDeleteResult` objects. */ override delete>( identifier: ReturnType, callback: C = this.defaultOneParamCallback as C, ignoreCount = false - ): BinaryTreeDeletedResult[] { - const bstDeletedResult: BinaryTreeDeletedResult[] = []; + ): BiTreeDeleteResult[] { + const bstDeletedResult: BiTreeDeleteResult[] = []; if (!this.root) return bstDeletedResult; const curr: N | undefined = this.getNode(identifier, callback) ?? undefined; diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index 1e71463..18bf5d4 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -1,10 +1,10 @@ import {BinaryTreeNode} from '../data-structures'; -import {BinaryTreeDeletedResult, BinaryTreeNodeNested, BTNCallback, BTNKey} from '../types'; +import {BiTreeDeleteResult, BinaryTreeNodeNested, BTNCallback, BTNKey} from '../types'; export interface IBinaryTree = BinaryTreeNodeNested> { createNode(key: BTNKey, value?: N['value']): N; add(keyOrNode: BTNKey | N | null, value?: N['value']): N | null | undefined; - delete>(identifier: ReturnType | null, callback: C): BinaryTreeDeletedResult[]; + delete>(identifier: ReturnType | null, callback: C): BiTreeDeleteResult[]; } diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index dc843c2..95e11c9 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -24,7 +24,7 @@ export enum FamilyPosition { export type BTNKey = number; -export type BinaryTreeDeletedResult = { deleted: N | null | undefined; needBalanced: N | null | undefined }; +export type BiTreeDeleteResult = { deleted: N | null | undefined; needBalanced: N | null | undefined }; export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>