From 5a492c1801d3ce22fdd97b4b3180eaca21cf7379 Mon Sep 17 00:00:00 2001 From: Revone Date: Mon, 21 Oct 2024 20:55:48 +1300 Subject: [PATCH] refactor: To standardize the _dfs method of the BinaryTree to achieve full generalization. --- README.md | 26 +-- .../binary-tree/binary-tree.ts | 175 +++++++++--------- 2 files changed, 100 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index a600003..d94e45e 100644 --- a/README.md +++ b/README.md @@ -823,43 +823,43 @@ Version 11.7.9 [//]: # (No deletion!!! Start of Replace Section)
heap
-
test nametime taken (ms)executions per secsample deviation
100,000 add7.60131.642.41e-4
100,000 add & poll44.0422.710.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add7.49133.432.10e-4
100,000 add & poll44.0822.690.00
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add77.1112.970.00
100,000 add randomly81.3812.290.00
100,000 get112.228.910.00
100,000 iterator28.6434.910.00
100,000 add & delete orderly158.806.300.02
100,000 add & delete randomly230.074.350.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add79.1612.630.00
100,000 add randomly84.3511.850.00
100,000 get111.748.950.00
100,000 iterator26.6137.580.00
100,000 add & delete orderly160.056.250.02
100,000 add & delete randomly234.554.260.01
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push42.2623.670.01
100,000 push & shift5.07197.325.97e-4
Native JS Array 100,000 push & shift2252.790.440.17
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push40.7024.570.01
100,000 push & shift5.15194.256.57e-4
Native JS Array 100,000 push & shift2143.370.470.15
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push23.3042.920.00
1,000,000 push & pop31.9031.340.00
1,000,000 push & shift32.7030.580.00
100,000 push & shift3.40293.732.64e-4
Native JS Array 100,000 push & shift2225.020.450.08
100,000 unshift & shift3.34299.682.48e-4
Native JS Array 100,000 unshift & shift3993.220.250.13
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push24.7440.430.00
1,000,000 push & pop31.3831.870.00
1,000,000 push & shift32.1231.130.00
100,000 push & shift3.39295.243.62e-4
Native JS Array 100,000 push & shift2348.520.430.21
100,000 unshift & shift3.28304.602.76e-4
Native JS Array 100,000 unshift & shift4062.430.250.12
hash-map
-
test nametime taken (ms)executions per secsample deviation
1,000,000 set106.429.400.01
Native JS Map 1,000,000 set204.684.890.01
Native JS Set 1,000,000 add166.126.020.01
1,000,000 set & get118.988.400.02
Native JS Map 1,000,000 set & get265.163.770.01
Native JS Set 1,000,000 add & has169.185.910.01
1,000,000 ObjKey set & get315.903.170.03
Native JS Map 1,000,000 ObjKey set & get291.113.440.03
Native JS Set 1,000,000 ObjKey add & has272.993.660.04
+
test nametime taken (ms)executions per secsample deviation
1,000,000 set106.659.380.02
Native JS Map 1,000,000 set201.704.960.01
Native JS Set 1,000,000 add163.556.110.01
1,000,000 set & get115.568.650.02
Native JS Map 1,000,000 set & get264.293.780.01
Native JS Set 1,000,000 add & has171.965.820.01
1,000,000 ObjKey set & get326.273.060.05
Native JS Map 1,000,000 ObjKey set & get322.173.100.06
Native JS Set 1,000,000 ObjKey add & has241.344.140.03
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push44.8122.310.00
100,000 getWords83.7511.940.00
+
test nametime taken (ms)executions per secsample deviation
100,000 push44.2122.620.00
100,000 getWords85.2211.730.00
avl-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add270.913.690.02
100,000 add randomly344.712.900.00
100,000 get128.807.760.00
100,000 iterator32.8830.410.01
100,000 add & delete orderly456.462.190.00
100,000 add & delete randomly604.251.650.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add269.923.700.01
100,000 add randomly317.133.150.00
100,000 get127.747.830.00
100,000 iterator29.9933.340.01
100,000 add & delete orderly431.272.320.00
100,000 add & delete randomly580.911.720.00
binary-tree-overall
-
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add randomly6.64150.668.06e-5
10,000 RBTree get randomly9.24108.231.40e-4
10,000 RBTree add & delete randomly18.2554.792.59e-4
10,000 AVLTree add randomly23.5742.431.78e-4
10,000 AVLTree get randomly9.69103.218.94e-5
10,000 AVLTree add & delete randomly44.3722.544.23e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add randomly6.73148.591.04e-4
10,000 RBTree get randomly9.48105.501.07e-4
10,000 RBTree add & delete randomly18.4054.332.70e-4
10,000 AVLTree add randomly23.5742.431.73e-4
10,000 AVLTree get randomly9.70103.067.92e-5
10,000 AVLTree add & delete randomly44.4322.513.33e-4
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109718.491.33e-6
1,000 addEdge6.28159.341.59e-4
1,000 getVertex0.042.57e+45.33e-7
1,000 getEdge22.4344.580.00
tarjan200.864.980.01
topologicalSort176.955.650.01
+
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109723.272.46e-6
1,000 addEdge6.26159.787.28e-4
1,000 getVertex0.042.54e+44.17e-7
1,000 getEdge22.6444.160.00
tarjan200.145.000.01
topologicalSort175.915.680.01
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push205.864.860.07
1,000,000 unshift199.965.000.03
1,000,000 unshift & shift180.945.530.02
1,000,000 addBefore308.983.240.10
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push196.175.100.03
1,000,000 unshift203.734.910.04
1,000,000 unshift & shift186.325.370.04
1,000,000 addBefore298.353.350.05
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift206.484.840.09
10,000 push & pop224.134.460.01
10,000 addBefore246.924.050.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift210.624.750.09
10,000 push & pop226.024.420.02
10,000 addBefore249.354.010.01
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add27.0237.012.69e-4
100,000 add & poll75.9713.165.61e-4
+
test nametime taken (ms)executions per secsample deviation
100,000 add27.2336.727.64e-4
100,000 add & poll76.6613.040.00
stack
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push38.5225.960.00
1,000,000 push & pop46.9521.300.01
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push37.1026.960.00
1,000,000 push & pop44.7222.360.01
[//]: # (No deletion!!! End of Replace Section) diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index bb08900..2166e8a 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -22,12 +22,12 @@ import type { NodeDisplayLayout, OptBTNOrNull } from '../../types'; +import { DFSOperation, DFSStackItem } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { trampoline } from '../../utils'; import { Queue } from '../queue'; import { IterableEntryBase } from '../base'; import * as console from 'console'; -import { DFSOperation, DFSStackItem } from '../../types'; /** * Represents a node in a binary tree. @@ -340,7 +340,7 @@ export class BinaryTree< * `BTNKeyOrNodeOrEntry`. * @returns a boolean value. */ - isNodeOrNull(node: R | BTNKeyOrNodeOrEntry): node is NODE | null { + isRealNodeOrNull(node: R | BTNKeyOrNodeOrEntry): node is NODE | null { return this.isRealNode(node) || node === null; } @@ -1487,8 +1487,8 @@ export class BinaryTree< ans.push(callback(current)); if (includeNull) { - if (current && this.isNodeOrNull(current.left)) queue.push(current.left); - if (current && this.isNodeOrNull(current.right)) queue.push(current.right); + if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left); + if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right); } else { if (this.isRealNode(current.left)) queue.push(current.left); if (this.isRealNode(current.right)) queue.push(current.right); @@ -1508,8 +1508,8 @@ export class BinaryTree< ans.push(callback(current)); if (includeNull) { - if (current && this.isNodeOrNull(current.left)) queue.push(current.left); - if (current && this.isNodeOrNull(current.right)) queue.push(current.right); + if (current && this.isRealNodeOrNull(current.left)) queue.push(current.left); + if (current && this.isRealNodeOrNull(current.right)) queue.push(current.right); } else { if (this.isRealNode(current.left)) queue.push(current.left); if (this.isRealNode(current.right)) queue.push(current.right); @@ -1637,8 +1637,8 @@ export class BinaryTree< if (!levelsNodes[level]) levelsNodes[level] = []; levelsNodes[level].push(callback(node)); if (includeNull) { - if (node && this.isNodeOrNull(node.left)) _recursive(node.left, level + 1); - if (node && this.isNodeOrNull(node.right)) _recursive(node.right, level + 1); + if (node && this.isRealNodeOrNull(node.left)) _recursive(node.left, level + 1); + if (node && this.isRealNodeOrNull(node.right)) _recursive(node.right, level + 1); } else { if (node && node.left) _recursive(node.left, level + 1); if (node && node.right) _recursive(node.right, level + 1); @@ -1657,8 +1657,8 @@ export class BinaryTree< levelsNodes[level].push(callback(node)); if (includeNull) { - if (node && this.isNodeOrNull(node.right)) stack.push([node.right, level + 1]); - if (node && this.isNodeOrNull(node.left)) stack.push([node.left, level + 1]); + if (node && this.isRealNodeOrNull(node.right)) stack.push([node.right, level + 1]); + if (node && this.isRealNodeOrNull(node.left)) stack.push([node.left, level + 1]); } else { if (node && node.right) stack.push([node.right, level + 1]); if (node && node.left) stack.push([node.left, level + 1]); @@ -1945,83 +1945,89 @@ export class BinaryTree< * Time complexity: O(n) * Space complexity: O(n) * - * The `dfs` function performs a depth-first search traversal on a binary tree, executing a callback - * function on each node according to a specified pattern and iteration type. - * @param {C} callback - The `callback` parameter is a function that will be called for each node - * visited during the depth-first search. It takes a node as an argument and returns a value. The - * return type of the callback function is determined by the generic type `C`. - * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter determines the order in which the - * nodes are visited during the depth-first search. It can have one of the following values: - * @param {R | BTNKeyOrNodeOrEntry} beginRoot - The `beginRoot` parameter is the starting - * point of the depth-first search. It can be either a node object, a key-value pair, or a key. If it - * is a key or key-value pair, the method will find the corresponding node in the tree and start the - * search from there. - * @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter determines the - * type of iteration to use during the depth-first search. It can have two possible values: - * @param [includeNull=false] - The `includeNull` parameter is a boolean value that determines - * whether or not to include null values in the depth-first search traversal. If `includeNull` is set - * to `true`, null values will be included in the traversal. If `includeNull` is set to `false`, null - * values will - * @returns an array of the return types of the callback function. + * The function `_dfs` performs a depth-first search traversal on a binary tree structure based on + * the specified order pattern and callback function. + * @param {C} callback - The `callback` parameter is a function that will be called on each node + * visited during the depth-first search. It is of type `C`, which extends + * `BTNCallback>`. The default value is set to `this._DEFAULT_CALLBACK` if not + * provided. + * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `_dfs` method specifies the + * order in which the Depth-First Search (DFS) algorithm should traverse the nodes in a binary tree. + * It can have one of the following values: + * @param {R | BTNKeyOrNodeOrEntry} beginRoot - The `beginRoot` parameter in the `_dfs` + * method is used to specify the starting point for the depth-first search traversal in a binary + * tree. It can be provided as either the root node of the tree or a key, node, or entry that exists + * in the tree. If no specific ` + * @param {IterationType} iterationType - The `iterationType` parameter in the `_dfs` method + * specifies the type of iteration to be performed during the Depth-First Search (DFS) traversal. It + * can have two possible values: + * @param [includeNull=false] - The `includeNull` parameter in the `_dfs` method is a boolean flag + * that determines whether null nodes should be included in the depth-first search traversal. If + * `includeNull` is set to `true`, the traversal will consider null nodes as valid nodes to visit and + * process. If set to ` + * @param shouldVisitLeft - The `shouldVisitLeft` parameter is a function that takes a node as input + * and returns a boolean value. It is used to determine whether the left child of a node should be + * visited during the depth-first search traversal. By default, it checks if the node is truthy (not + * null or undefined + * @param shouldVisitRight - The `shouldVisitRight` parameter is a function that takes a node as + * input and returns a boolean value. It is used to determine whether the right child of a node + * should be visited during the depth-first search traversal. The default implementation checks if + * the node is truthy before visiting the right child. + * @param shouldVisitRoot - The `shouldVisitRoot` parameter is a function that takes a node as an + * argument and returns a boolean value. It is used to determine whether a given node should be + * visited during the depth-first search traversal based on certain conditions. The default + * implementation checks if the node is a real node or null based + * @param shouldProcessRoot - The `shouldProcessRoot` parameter is a function that takes a node as + * input and returns a boolean value indicating whether the node should be processed during the + * depth-first search traversal. The default implementation of this function simply returns `true`, + * meaning that by default all nodes will be processed. However, you can + * @returns The `_dfs` method returns an array of the return type of the callback function provided + * as input. */ protected _dfs>>( callback: C = this._DEFAULT_CALLBACK as C, pattern: DFSOrderPattern = 'IN', beginRoot: R | BTNKeyOrNodeOrEntry = this.root, iterationType: IterationType = this.iterationType, - includeNull = false + includeNull = false, + shouldVisitLeft: (node: OptBTNOrNull) => boolean = node => !!node, + shouldVisitRight: (node: OptBTNOrNull) => boolean = node => !!node, + shouldVisitRoot: (node: OptBTNOrNull) => boolean = node => { + if (includeNull) return this.isRealNodeOrNull(node); + return this.isRealNode(node); + }, + shouldProcessRoot: (node: OptBTNOrNull) => boolean = node => true ): ReturnType[] { beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; const ans: ReturnType[] = []; - if (iterationType === 'RECURSIVE') { - const visitNullableLeft = (node: OptBTNOrNull) => { - if (node && this.isNodeOrNull(node.left)) dfs(node.left); - }; - const visitLeft = (node: OptBTNOrNull) => { - if (node && this.isRealNode(node.left)) dfs(node.left); - }; - const visitNullableRight = (node: OptBTNOrNull) => { - if (node && this.isNodeOrNull(node.right)) dfs(node.right); - }; - const visitRight = (node: OptBTNOrNull) => { - if (node && this.isRealNode(node.right)) dfs(node.right); - }; + if (iterationType === 'RECURSIVE') { const dfs = (node: OptBTNOrNull) => { + if (!shouldVisitRoot(node)) return; + + const visitLeft = () => { + if (shouldVisitLeft(node)) dfs(node?.left); + }; + const visitRight = () => { + if (shouldVisitRight(node)) dfs(node?.right); + }; + switch (pattern) { case 'IN': - if (includeNull) { - visitNullableLeft(node); - ans.push(callback(node)); - visitNullableRight(node); - } else { - visitLeft(node); - ans.push(callback(node)); - visitRight(node); - } + visitLeft(); + if (shouldProcessRoot(node)) ans.push(callback(node)); + visitRight(); break; case 'PRE': - if (includeNull) { - ans.push(callback(node)); - visitNullableLeft(node); - visitNullableRight(node); - } else { - ans.push(callback(node)); - visitLeft(node); - visitRight(node); - } + if (shouldProcessRoot(node)) ans.push(callback(node)); + visitLeft(); + visitRight(); break; case 'POST': - if (includeNull) { - visitNullableLeft(node); - visitNullableRight(node); - ans.push(callback(node)); - } else { - visitLeft(node); - visitRight(node); - ans.push(callback(node)); - } + visitLeft(); + visitRight(); + if (shouldProcessRoot(node)) ans.push(callback(node)); break; } }; @@ -2029,47 +2035,40 @@ export class BinaryTree< dfs(beginRoot); } else { const stack: DFSStackItem[] = [{ opt: DFSOperation.VISIT, node: beginRoot }]; + const pushLeft = (cur: DFSStackItem) => { - cur.node && stack.push({ opt: DFSOperation.VISIT, node: cur.node.left }); + if (shouldVisitLeft(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.left }); }; const pushRight = (cur: DFSStackItem) => { - cur.node && stack.push({ opt: DFSOperation.VISIT, node: cur.node.right }); + if (shouldVisitRight(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.right }); }; - const pushParent = (cur: DFSStackItem) => { - stack.push({ opt: DFSOperation.PROCESS, node: cur.node }); + const pushRoot = (cur: DFSStackItem) => { + if (shouldVisitRoot(cur.node)) stack.push({ opt: DFSOperation.PROCESS, node: cur.node }); }; + while (stack.length > 0) { const cur = stack.pop(); if (cur === undefined) continue; - if (includeNull) { - if (!this.isNodeOrNull(cur.node)) continue; - } else { - if (!this.isRealNode(cur.node)) continue; - } - if (cur.opt === 1) { - ans.push(callback(cur.node)); + if (!shouldVisitRoot(cur.node)) continue; + if (cur.opt === DFSOperation.PROCESS) { + if (shouldProcessRoot(cur.node)) ans.push(callback(cur.node)); } else { switch (pattern) { case 'IN': pushRight(cur); - pushParent(cur); + pushRoot(cur); pushLeft(cur); break; case 'PRE': pushRight(cur); pushLeft(cur); - pushParent(cur); + pushRoot(cur); break; case 'POST': - pushParent(cur); + pushRoot(cur); pushRight(cur); pushLeft(cur); break; - default: - pushRight(cur); - pushParent(cur); - pushLeft(cur); - break; } } }