diff --git a/README.md b/README.md index 192e92d..c44e802 100644 --- a/README.md +++ b/README.md @@ -826,43 +826,43 @@ Version 11.7.9 [//]: # (No deletion!!! Start of Replace Section)
heap
-
test nametime taken (ms)executions per secsample deviation
100,000 add6.66150.182.19e-4
100,000 add & poll35.3528.298.55e-4
+
test nametime taken (ms)executions per secsample deviation
100,000 add6.63150.923.07e-4
100,000 add & poll35.0928.508.68e-4
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add83.7011.954.47e-4
100,000 add randomly85.0511.760.00
100,000 get112.868.860.00
100,000 iterator23.6242.340.00
100,000 add & delete orderly153.386.520.01
100,000 add & delete randomly232.754.300.00
+
test nametime taken (ms)executions per secsample deviation
100,000 add89.6511.150.00
100,000 add randomly92.7410.780.01
100,000 get1.09921.217.98e-5
100,000 iterator25.7638.820.00
100,000 add & delete orderly159.436.270.02
100,000 add & delete randomly239.834.170.00
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push46.1821.650.01
100,000 push & shift5.40185.330.00
Native JS Array 100,000 push & shift2273.180.440.20
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.0122.220.01
100,000 push & shift6.24160.170.01
Native JS Array 100,000 push & shift2227.550.450.22
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push20.3349.200.00
1,000,000 push & pop26.3537.950.00
1,000,000 push & shift28.8334.680.00
100,000 push & shift2.55392.713.48e-4
Native JS Array 100,000 push & shift2285.230.440.25
100,000 unshift & shift2.53395.884.03e-4
Native JS Array 100,000 unshift & shift4063.570.250.22
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push20.2149.470.00
1,000,000 push & pop26.8537.240.01
1,000,000 push & shift27.5736.270.00
100,000 push & shift2.61382.644.60e-4
Native JS Array 100,000 push & shift2152.900.460.22
100,000 unshift & shift2.51398.743.60e-4
Native JS Array 100,000 unshift & shift4376.450.230.30
hash-map
-
test nametime taken (ms)executions per secsample deviation
1,000,000 set210.304.760.03
Native JS Map 1,000,000 set217.284.600.03
Native JS Set 1,000,000 add175.115.710.02
1,000,000 set & get231.894.310.05
Native JS Map 1,000,000 set & get278.863.590.03
Native JS Set 1,000,000 add & has237.244.220.01
1,000,000 ObjKey set & get387.962.580.07
Native JS Map 1,000,000 ObjKey set & get348.732.870.07
Native JS Set 1,000,000 ObjKey add & has295.333.390.04
+
test nametime taken (ms)executions per secsample deviation
1,000,000 set86.7911.520.03
Native JS Map 1,000,000 set207.964.810.01
Native JS Set 1,000,000 add169.325.910.02
1,000,000 set & get81.3012.300.02
Native JS Map 1,000,000 set & get272.313.670.01
Native JS Set 1,000,000 add & has237.784.210.02
1,000,000 ObjKey set & get374.052.670.06
Native JS Map 1,000,000 ObjKey set & get345.512.890.06
Native JS Set 1,000,000 ObjKey add & has286.453.490.05
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push43.2423.136.15e-4
100,000 getWords105.069.520.01
+
test nametime taken (ms)executions per secsample deviation
100,000 push41.6024.045.38e-4
100,000 getWords81.0412.340.00
avl-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add309.913.230.01
100,000 add randomly368.422.710.00
100,000 get171.255.840.00
100,000 iterator26.1438.250.00
100,000 add & delete orderly511.401.960.00
100,000 add & delete randomly687.431.450.02
+
test nametime taken (ms)executions per secsample deviation
100,000 add299.033.340.00
100,000 add randomly364.392.740.02
100,000 get1.08921.788.08e-5
100,000 iterator27.6036.230.00
100,000 add & delete orderly488.822.050.00
100,000 add & delete randomly649.461.540.01
binary-tree-overall
-
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add randomly7.30136.949.11e-5
10,000 RBTree get randomly12.9277.411.62e-4
10,000 RBTree add & delete randomly22.4244.603.28e-4
10,000 AVLTree add randomly28.3435.293.30e-4
10,000 AVLTree get randomly12.9477.278.60e-5
10,000 AVLTree add & delete randomly52.3519.105.13e-4
+
test nametime taken (ms)executions per secsample deviation
10,000 RBTree add randomly7.76128.901.09e-4
10,000 RBTree get randomly0.119328.865.44e-6
10,000 RBTree add & delete randomly21.7246.042.07e-4
10,000 AVLTree add randomly27.4536.433.65e-4
10,000 AVLTree get randomly0.119432.494.04e-7
10,000 AVLTree add & delete randomly51.0319.606.60e-4
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.091.07e+48.73e-7
1,000 addEdge5.98167.361.15e-4
1,000 getVertex0.091.05e+48.10e-7
1,000 getEdge23.4242.690.00
tarjan210.664.750.04
topologicalSort152.366.560.01
+
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.101.03e+41.04e-6
1,000 addEdge6.01166.291.12e-4
1,000 getVertex0.101.04e+41.71e-6
1,000 getEdge23.7242.150.00
tarjan194.375.140.00
topologicalSort152.916.540.02
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push196.745.080.04
1,000,000 unshift172.875.780.01
1,000,000 unshift & shift151.326.610.01
1,000,000 addBefore262.753.810.07
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push188.445.310.04
1,000,000 unshift177.485.630.02
1,000,000 unshift & shift161.096.210.04
1,000,000 addBefore277.843.600.08
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift164.526.080.03
10,000 push & pop234.454.270.01
10,000 addBefore275.793.630.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push & shift193.995.150.05
10,000 push & pop232.824.300.01
10,000 addBefore285.443.500.02
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add30.7432.530.00
100,000 add & poll88.1611.344.14e-4
+
test nametime taken (ms)executions per secsample deviation
100,000 add30.0233.312.68e-4
100,000 add & poll89.2111.210.00
stack
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push42.8023.370.01
1,000,000 push & pop48.1320.780.01
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push42.7323.400.00
1,000,000 push & pop50.5219.790.02
[//]: # (No deletion!!! End of Replace Section) diff --git a/package-lock.json b/package-lock.json index fec06f5..6141a25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,11 +19,11 @@ "@typescript-eslint/eslint-plugin": "^8.12.1", "@typescript-eslint/parser": "^8.12.1", "auto-changelog": "^2.5.0", - "avl-tree-typed": "^1.52.8", + "avl-tree-typed": "^1.52.9", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.52.8", - "bst-typed": "^1.52.8", - "data-structure-typed": "^1.52.8", + "binary-tree-typed": "^1.52.9", + "bst-typed": "^1.52.9", + "data-structure-typed": "^1.52.9", "dependency-cruiser": "^16.5.0", "doctoc": "^2.2.1", "eslint": "^9.13.0", @@ -32,7 +32,7 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", "fast-glob": "^3.3.2", - "heap-typed": "^1.52.8", + "heap-typed": "^1.52.9", "istanbul-badges-readme": "^1.9.0", "jest": "^29.7.0", "js-sdsl": "^4.4.2", @@ -3437,13 +3437,13 @@ } }, "node_modules/avl-tree-typed": { - "version": "1.52.8", - "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.52.8.tgz", - "integrity": "sha512-8oU+KjIVtzF9U12NudYuQNBX/If1DL1bKrbqLtKk5RjEq1Jsl56/GIvuzbvKHnGgv9h7DpXHyJUCLjAz3zjhbg==", + "version": "1.52.9", + "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.52.9.tgz", + "integrity": "sha512-TfapTNrlIquhf1iwrzhc5pEejEhE0CogxiNlaI0nqkv2Qz2OkhvfmAtJOG5ndiFBp4btNZYFGHb+S/zXkEX82A==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.52.8" + "data-structure-typed": "^1.52.9" } }, "node_modules/babel-jest": { @@ -3602,13 +3602,13 @@ } }, "node_modules/binary-tree-typed": { - "version": "1.52.8", - "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.52.8.tgz", - "integrity": "sha512-tjUcMufU41Tmp+/6/cBHdSonA+gjhMUpfJqspJ06xp8A3gfWQpf9TjHdY9FxpeeTLPnREpU4SGGZqqIk372XVA==", + "version": "1.52.9", + "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.52.9.tgz", + "integrity": "sha512-/q5l3wftn485Px+L40NKjKBPukL1XjotSQVCYB+wmz1S3P4H8FobBlEFo5j0UqaW9+TgzhSwecoBbIPsvshXAQ==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.52.8" + "data-structure-typed": "^1.52.9" } }, "node_modules/brace-expansion": { @@ -3691,13 +3691,13 @@ } }, "node_modules/bst-typed": { - "version": "1.52.8", - "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.52.8.tgz", - "integrity": "sha512-YMRUaz8jNxSymvKNNyeJR8HozHRgDWUw8PO0QLSvaW/c4Ed/KhpDSyBth52NClFnbJnNV2lTsd01k/VwuerimA==", + "version": "1.52.9", + "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.52.9.tgz", + "integrity": "sha512-7jgOhrYST+HBZRGvmKx3UZRt6ddBr9Ib807gfJ9+jRVuXQIK9m3hW1MSwXMovWs8tKpF0JZMCqj+uk+S8B85qg==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.52.8" + "data-structure-typed": "^1.52.9" } }, "node_modules/buffer-from": { @@ -4069,9 +4069,9 @@ } }, "node_modules/data-structure-typed": { - "version": "1.52.8", - "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.52.8.tgz", - "integrity": "sha512-2YNk0YSjwK0IA8b+g68bmbWtc82M4IzIqo/VUSY16lO3LZ+AajxPkBK68VHPP07ckNNLhdLbh04RoQd3dMNNiw==", + "version": "1.52.9", + "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.52.9.tgz", + "integrity": "sha512-tIAf4CqOdDz5XJZBtTjWwhOgLOwiMS+1kH9dN44x12klBcKavpUDUjncvrcAqLsVP5xTZSeehJ5teNa6YNgWQQ==", "dev": true, "license": "MIT" }, @@ -5946,13 +5946,13 @@ } }, "node_modules/heap-typed": { - "version": "1.52.8", - "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.52.8.tgz", - "integrity": "sha512-Zzq+V9IQMf8o2FrIeqc0kRxRyDxOyUPX5dXRi095LnpOFRzUXZoSl2hggbHfsjKEaKwLDRoJuS9t77alqmK4NQ==", + "version": "1.52.9", + "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.52.9.tgz", + "integrity": "sha512-YdCJFzKob73n5sDovGkL2Y/jqVe+vY9dXcDyx4ToZoneJqJ83IMVDl/qlGb/ujDMlTXqJR4/jiIT5s00jxS7jQ==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.52.8" + "data-structure-typed": "^1.52.9" } }, "node_modules/html-escaper": { diff --git a/package.json b/package.json index 4cc4bae..4bfadcf 100644 --- a/package.json +++ b/package.json @@ -68,11 +68,11 @@ "@typescript-eslint/eslint-plugin": "^8.12.1", "@typescript-eslint/parser": "^8.12.1", "auto-changelog": "^2.5.0", - "avl-tree-typed": "^1.52.8", + "avl-tree-typed": "^1.52.9", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.52.8", - "bst-typed": "^1.52.8", - "data-structure-typed": "^1.52.8", + "binary-tree-typed": "^1.52.9", + "bst-typed": "^1.52.9", + "data-structure-typed": "^1.52.9", "dependency-cruiser": "^16.5.0", "doctoc": "^2.2.1", "eslint": "^9.13.0", @@ -81,7 +81,7 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", "fast-glob": "^3.3.2", - "heap-typed": "^1.52.8", + "heap-typed": "^1.52.9", "istanbul-badges-readme": "^1.9.0", "jest": "^29.7.0", "js-sdsl": "^4.4.2", diff --git a/src/data-structures/binary-tree/avl-tree-multi-map.ts b/src/data-structures/binary-tree/avl-tree-multi-map.ts index 9e76f9e..990e70f 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -10,10 +10,9 @@ import type { AVLTreeMultiMapNodeNested, AVLTreeMultiMapOptions, BinaryTreeDeleteResult, - BSTNKeyOrNode, - BTNKeyOrNodeOrEntry, - IterationType, - BTNEntry + BSTNOptKeyOrNode, + BTNRep, + IterationType } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { AVLTree, AVLTreeNode } from './avl-tree'; @@ -64,7 +63,7 @@ export class AVLTreeMultiMapNode< export class AVLTreeMultiMap< K = any, V = any, - R = BTNEntry, + R = object, NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNode>, TREE extends AVLTreeMultiMap = AVLTreeMultiMap< K, @@ -79,18 +78,18 @@ export class AVLTreeMultiMap< { /** * The constructor initializes a new AVLTreeMultiMap object with optional initial elements. - * @param keysOrNodesOrEntriesOrRaws - The `keysOrNodesOrEntriesOrRaws` parameter is an + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an * iterable object that can contain either keys, nodes, entries, or raw elements. * @param [options] - The `options` parameter is an optional object that can be used to customize the * behavior of the AVLTreeMultiMap. It can include properties such as `compareKeys` and * `compareValues` functions to define custom comparison logic for keys and values, respectively. */ constructor( - keysOrNodesOrEntriesOrRaws: Iterable> = [], + keysNodesEntriesOrRaws: Iterable> = [], options?: AVLTreeMultiMapOptions ) { super([], options); - if (keysOrNodesOrEntriesOrRaws) this.addMany(keysOrNodesOrEntriesOrRaws); + if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } protected _count = 0; @@ -143,6 +142,7 @@ export class AVLTreeMultiMap< override createTree(options?: AVLTreeMultiMapOptions): TREE { return new AVLTreeMultiMap([], { iterationType: this.iterationType, + isMapMode: this._isMapMode, comparator: this._comparator, toEntryFn: this._toEntryFn, ...options @@ -151,20 +151,20 @@ export class AVLTreeMultiMap< /** * The function checks if the input is an instance of AVLTreeMultiMapNode. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `R` or `BTNKeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the input parameter `keyOrNodeOrEntryOrRaw` is + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is * an instance of the `AVLTreeMultiMapNode` class. */ - override isNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - return keyOrNodeOrEntryOrRaw instanceof AVLTreeMultiMapNode; + override isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + return keyNodeEntryOrRaw instanceof AVLTreeMultiMapNode; } /** - * The function `keyValueOrEntryOrRawElementToNode` converts a key, value, entry, or raw element into + * The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into * a node object. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The - * `keyOrNodeOrEntryOrRaw` parameter can be of type `R` or `BTNKeyOrNodeOrEntry`. + * @param {BTNRep | R} keyNodeEntryOrRaw - The + * `keyNodeEntryOrRaw` parameter can be of type `R` or `BTNRep`. * @param {V} [value] - The `value` parameter is an optional value that can be passed to the * `override` function. It represents the value associated with the key in the data structure. If no * value is provided, it will default to `undefined`. @@ -172,28 +172,33 @@ export class AVLTreeMultiMap< * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. * @returns either a NODE object or undefined. */ - override keyValueOrEntryOrRawElementToNode( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, + override keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep | R, value?: V, count = 1 - ): NODE | undefined { - if (keyOrNodeOrEntryOrRaw === undefined || keyOrNodeOrEntryOrRaw === null) return; - if (this.isNode(keyOrNodeOrEntryOrRaw)) return keyOrNodeOrEntryOrRaw; + ): [NODE | undefined, V | undefined] { + if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - if (this.isEntry(keyOrNodeOrEntryOrRaw)) { - const [key, entryValue] = keyOrNodeOrEntryOrRaw; - if (key === undefined || key === null) return; - if (this.isKey(key)) return this.createNode(key, value ?? entryValue, count); + if (this.isEntry(keyNodeEntryOrRaw)) { + const [key, entryValue] = keyNodeEntryOrRaw; + if (key === undefined || key === null) return [undefined, undefined]; + const finalValue = value ?? entryValue; + return [this.createNode(key, finalValue, count), finalValue]; } - if (this._toEntryFn) { - const [key, entryValue] = this._toEntryFn(keyOrNodeOrEntryOrRaw as R); - if (this.isKey(key)) return this.createNode(key, value ?? entryValue, count); + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value]; + + if (this.isRaw(keyNodeEntryOrRaw)) { + if (this._toEntryFn) { + const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue]; + } + return [undefined, undefined]; } - if (this.isKey(keyOrNodeOrEntryOrRaw)) return this.createNode(keyOrNodeOrEntryOrRaw, value, count); - - return; + return [undefined, undefined]; } /** @@ -202,9 +207,9 @@ export class AVLTreeMultiMap< * * The function overrides the add method of a TypeScript class to add a new node to a data structure * and update the count. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The - * `keyOrNodeOrEntryOrRaw` parameter can accept a value of type `R`, which can be any type. It - * can also accept a value of type `BTNKeyOrNodeOrEntry`, which represents a key, node, + * @param {BTNRep | R} keyNodeEntryOrRaw - The + * `keyNodeEntryOrRaw` parameter can accept a value of type `R`, which can be any type. It + * can also accept a value of type `BTNRep`, which represents a key, node, * entry, or raw element * @param {V} [value] - The `value` parameter represents the value associated with the key in the * data structure. It is an optional parameter, so it can be omitted if not needed. @@ -213,12 +218,12 @@ export class AVLTreeMultiMap< * be added once. However, you can specify a different value for `count` if you want to add * @returns a boolean value. */ - override add(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V, count = 1): boolean { - const newNode = this.keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw, value, count); + override add(keyNodeEntryOrRaw: BTNRep | R, value?: V, count = 1): boolean { + const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count); if (newNode === undefined) return false; const orgNodeCount = newNode?.count || 0; - const inserted = super.add(newNode); + const inserted = super.add(newNode, newValue); if (inserted) { this._count += orgNodeCount; } @@ -231,7 +236,7 @@ export class AVLTreeMultiMap< * * The function overrides the delete method in a binary tree data structure, handling deletion of * nodes and maintaining balance in the tree. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `predicate` + * @param {BTNRep | R} keyNodeEntryOrRaw - The `predicate` * parameter in the `delete` method is used to specify the condition for deleting a node from the * binary tree. It can be a key, node, or entry that determines which * node(s) should be deleted. @@ -244,14 +249,11 @@ export class AVLTreeMultiMap< * method returns an array of `BinaryTreeDeleteResult` objects, each containing information about the * deleted node and whether balancing is needed in the tree. */ - override delete( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, - ignoreCount = false - ): BinaryTreeDeleteResult[] { + override delete(keyNodeEntryOrRaw: BTNRep | R, ignoreCount = false): BinaryTreeDeleteResult[] { const deletedResult: BinaryTreeDeleteResult[] = []; if (!this.root) return deletedResult; - const curr: NODE | undefined = this.getNode(keyOrNodeOrEntryOrRaw) ?? undefined; + const curr: NODE | undefined = this.getNode(keyNodeEntryOrRaw) ?? undefined; if (!curr) return deletedResult; const parent: NODE | undefined = curr?.parent ? curr.parent : undefined; @@ -339,7 +341,8 @@ export class AVLTreeMultiMap< if (l > r) return; const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add(midNode.key, midNode.value, midNode.count); + if (this._isMapMode) this.add(midNode.key, undefined, midNode.count); + else this.add(midNode.key, midNode.value, midNode.count); buildBalanceBST(l, m - 1); buildBalanceBST(m + 1, r); }; @@ -355,7 +358,8 @@ export class AVLTreeMultiMap< if (l <= r) { const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add(midNode.key, midNode.value, midNode.count); + if (this._isMapMode) this.add(midNode.key, undefined, midNode.count); + else this.add(midNode.key, midNode.value, midNode.count); stack.push([m + 1, r]); stack.push([l, m - 1]); } @@ -374,7 +378,9 @@ export class AVLTreeMultiMap< */ override clone(): TREE { const cloned = this.createTree(); - this.bfs(node => cloned.add(node.key, node.value, node.count)); + if (this._isMapMode) this.bfs(node => cloned.add(node.key, undefined, node.count)); + else this.bfs(node => cloned.add(node.key, node.value, node.count)); + if (this._isMapMode) cloned._store = this._store; return cloned; } @@ -384,16 +390,16 @@ export class AVLTreeMultiMap< * * The `_swapProperties` function swaps the properties (key, value, count, height) between two nodes * in a binary search tree. - * @param {R | BSTNKeyOrNode} srcNode - The `srcNode` parameter represents the source node + * @param {R | BSTNOptKeyOrNode} srcNode - The `srcNode` parameter represents the source node * that will be swapped with the `destNode`. - * @param {R | BSTNKeyOrNode} destNode - The `destNode` parameter represents the destination + * @param {R | BSTNOptKeyOrNode} destNode - The `destNode` parameter represents the destination * node where the properties will be swapped with the source node. * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. * If either `srcNode` or `destNode` is undefined, it returns `undefined`. */ protected override _swapProperties( - srcNode: R | BSTNKeyOrNode, - destNode: R | BSTNKeyOrNode + srcNode: R | BSTNOptKeyOrNode, + destNode: R | BSTNOptKeyOrNode ): NODE | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -404,12 +410,12 @@ export class AVLTreeMultiMap< tempNode.height = height; destNode.key = srcNode.key; - destNode.value = srcNode.value; + if (!this._isMapMode) destNode.value = srcNode.value; destNode.count = srcNode.count; destNode.height = srcNode.height; srcNode.key = tempNode.key; - srcNode.value = tempNode.value; + if (!this._isMapMode) srcNode.value = tempNode.value; srcNode.count = tempNode.count; srcNode.height = tempNode.height; } diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index ecae1a4..e889e38 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -11,9 +11,8 @@ import type { AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeleteResult, - BSTNKeyOrNode, - BTNKeyOrNodeOrEntry, - BTNEntry + BSTNOptKeyOrNode, + BTNRep } from '../../types'; import { IBinaryTree } from '../../interfaces'; @@ -67,7 +66,7 @@ export class AVLTreeNode< export class AVLTree< K = any, V = any, - R = BTNEntry, + R = object, NODE extends AVLTreeNode = AVLTreeNode>, TREE extends AVLTree = AVLTree> > @@ -77,7 +76,7 @@ export class AVLTree< /** * This is a constructor function for an AVLTree class that initializes the tree with keys, nodes, * entries, or raw elements. - * @param keysOrNodesOrEntriesOrRaws - The `keysOrNodesOrEntriesOrRaws` parameter is an + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an * iterable object that can contain either keys, nodes, entries, or raw elements. These elements will * be used to initialize the AVLTree. * @param [options] - The `options` parameter is an optional object that can be used to customize the @@ -85,12 +84,9 @@ export class AVLTree< * keys), `allowDuplicates` (a boolean indicating whether duplicate keys are allowed), and * `nodeBuilder` ( */ - constructor( - keysOrNodesOrEntriesOrRaws: Iterable> = [], - options?: AVLTreeOptions - ) { + constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: AVLTreeOptions) { super([], options); - if (keysOrNodesOrEntriesOrRaws) super.addMany(keysOrNodesOrEntriesOrRaws); + if (keysNodesEntriesOrRaws) super.addMany(keysNodesEntriesOrRaws); } /** @@ -116,6 +112,7 @@ export class AVLTree< override createTree(options?: AVLTreeOptions): TREE { return new AVLTree([], { iterationType: this.iterationType, + isMapMode: this._isMapMode, comparator: this._comparator, toEntryFn: this._toEntryFn, ...options @@ -124,13 +121,13 @@ export class AVLTree< /** * The function checks if the input is an instance of AVLTreeNode. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `R` or `BTNKeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the input parameter `keyOrNodeOrEntryOrRaw` is + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is * an instance of the `AVLTreeNode` class. */ - override isNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - return keyOrNodeOrEntryOrRaw instanceof AVLTreeNode; + override isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + return keyNodeEntryOrRaw instanceof AVLTreeNode; } /** @@ -139,17 +136,17 @@ export class AVLTree< * * The function overrides the add method of a class and inserts a key-value pair into a data * structure, then balances the path. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can accept values of type `R`, `BTNKeyOrNodeOrEntry`, or + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can accept values of type `R`, `BTNRep`, or * `RawElement`. * @param {V} [value] - The `value` parameter is an optional value that you want to associate with * the key or node being added to the data structure. * @returns The method is returning a boolean value. */ - override add(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V): boolean { - if (keyOrNodeOrEntryOrRaw === null) return false; - const inserted = super.add(keyOrNodeOrEntryOrRaw, value); - if (inserted) this._balancePath(keyOrNodeOrEntryOrRaw); + override add(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + if (keyNodeEntryOrRaw === null) return false; + const inserted = super.add(keyNodeEntryOrRaw, value); + if (inserted) this._balancePath(keyNodeEntryOrRaw); return inserted; } @@ -159,15 +156,15 @@ export class AVLTree< * * The function overrides the delete method in a TypeScript class, performs deletion, and then * balances the tree if necessary. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `keyOrNodeOrEntryOrRaw` + * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` * parameter in the `override delete` method can be one of the following types: * @returns The `delete` method is being overridden in this code snippet. It first calls the `delete` * method from the superclass (presumably a parent class) with the provided `predicate`, which could * be a key, node, entry, or a custom predicate. The result of this deletion operation is stored in * `deletedResults`, which is an array of `BinaryTreeDeleteResult` objects. */ - override delete(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): BinaryTreeDeleteResult[] { - const deletedResults = super.delete(keyOrNodeOrEntryOrRaw); + override delete(keyNodeEntryOrRaw: BTNRep | R): BinaryTreeDeleteResult[] { + const deletedResults = super.delete(keyNodeEntryOrRaw); for (const { needBalanced } of deletedResults) { if (needBalanced) { this._balancePath(needBalanced); @@ -182,16 +179,16 @@ export class AVLTree< * * The `_swapProperties` function swaps the key, value, and height properties between two nodes in a * binary search tree. - * @param {R | BSTNKeyOrNode} srcNode - The `srcNode` parameter represents either a node + * @param {R | BSTNOptKeyOrNode} srcNode - The `srcNode` parameter represents either a node * object (`NODE`) or a key-value pair (`R`) that is being swapped with another node. - * @param {R | BSTNKeyOrNode} destNode - The `destNode` parameter is either an instance of - * `R` or an instance of `BSTNKeyOrNode`. + * @param {R | BSTNOptKeyOrNode} destNode - The `destNode` parameter is either an instance of + * `R` or an instance of `BSTNOptKeyOrNode`. * @returns The method is returning the `destNodeEnsured` object if both `srcNodeEnsured` and * `destNodeEnsured` are truthy. Otherwise, it returns `undefined`. */ protected override _swapProperties( - srcNode: R | BSTNKeyOrNode, - destNode: R | BSTNKeyOrNode + srcNode: R | BSTNOptKeyOrNode, + destNode: R | BSTNOptKeyOrNode ): NODE | undefined { const srcNodeEnsured = this.ensureNode(srcNode); const destNodeEnsured = this.ensureNode(destNode); @@ -204,11 +201,11 @@ export class AVLTree< tempNode.height = height; destNodeEnsured.key = srcNodeEnsured.key; - destNodeEnsured.value = srcNodeEnsured.value; + if (!this._isMapMode) destNodeEnsured.value = srcNodeEnsured.value; destNodeEnsured.height = srcNodeEnsured.height; srcNodeEnsured.key = tempNode.key; - srcNodeEnsured.value = tempNode.value; + if (!this._isMapMode) srcNodeEnsured.value = tempNode.value; srcNodeEnsured.height = tempNode.height; } @@ -432,10 +429,10 @@ export class AVLTree< * * The `_balancePath` function is used to update the heights of nodes and perform rotation operations * to restore balance in an AVL tree after inserting a node. - * @param {BTNKeyOrNodeOrEntry | R} node - The `node` parameter can be of type `R` or - * `BTNKeyOrNodeOrEntry`. + * @param {BTNRep | R} node - The `node` parameter can be of type `R` or + * `BTNRep`. */ - protected _balancePath(node: BTNKeyOrNodeOrEntry | R): void { + protected _balancePath(node: BTNRep | R): void { node = this.ensureNode(node); const path = this.getPathToRoot(node => node, node, false); // first O(log n) + O(log n) for (let i = 0; i < path.length; i++) { diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index d43c078..95dae8d 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -6,23 +6,24 @@ * @license MIT License */ -import type { +import { BinaryTreeDeleteResult, BinaryTreeNested, BinaryTreeNodeNested, BinaryTreeOptions, BinaryTreePrintOptions, - BTNCallback, BTNEntry, - BTNKeyOrNodeOrEntry, - BTNPredicate, + BTNRep, DFSOrderPattern, DFSStackItem, EntryCallback, FamilyPosition, IterationType, + NodeCallback, NodeDisplayLayout, - OptBTNOrNull + NodePredicate, + OptNodeOrNull, + ToEntryFn } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { isComparable, trampoline } from '../../utils'; @@ -51,26 +52,26 @@ export class BinaryTreeNode< this.value = value; } - protected _left?: OptBTNOrNull; + protected _left?: OptNodeOrNull; - get left(): OptBTNOrNull { + get left(): OptNodeOrNull { return this._left; } - set left(v: OptBTNOrNull) { + set left(v: OptNodeOrNull) { if (v) { v.parent = this as unknown as NODE; } this._left = v; } - protected _right?: OptBTNOrNull; + protected _right?: OptNodeOrNull; - get right(): OptBTNOrNull { + get right(): OptNodeOrNull { return this._right; } - set right(v: OptBTNOrNull) { + set right(v: OptNodeOrNull) { if (v) { v.parent = this as unknown as NODE; } @@ -103,7 +104,7 @@ export class BinaryTreeNode< export class BinaryTree< K = any, V = any, - R = BTNEntry, + R = object, NODE extends BinaryTreeNode = BinaryTreeNode>, TREE extends BinaryTree = BinaryTree> > @@ -115,30 +116,40 @@ export class BinaryTree< /** * The constructor initializes a binary tree with optional options and adds keys, nodes, entries, or * raw data if provided. - * @param keysOrNodesOrEntriesOrRaws - The `keysOrNodesOrEntriesOrRaws` parameter in the constructor - * is an iterable that can contain elements of type `BTNKeyOrNodeOrEntry` or `R`. It is + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor + * is an iterable that can contain elements of type `BTNRep` or `R`. It is * initialized with an empty array `[]` by default. * @param [options] - The `options` parameter in the constructor is an object that can contain the * following properties: */ - constructor( - keysOrNodesOrEntriesOrRaws: Iterable | R> = [], - options?: BinaryTreeOptions - ) { + constructor(keysNodesEntriesOrRaws: Iterable | R> = [], options?: BinaryTreeOptions) { super(); if (options) { - const { iterationType, toEntryFn } = options; + const { iterationType, toEntryFn, isMapMode } = options; if (iterationType) this.iterationType = iterationType; + if (isMapMode !== undefined) this._isMapMode = isMapMode; if (typeof toEntryFn === 'function') this._toEntryFn = toEntryFn; else if (toEntryFn) throw TypeError('toEntryFn must be a function type'); } - if (keysOrNodesOrEntriesOrRaws) this.addMany(keysOrNodesOrEntriesOrRaws); + if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } - protected _root?: OptBTNOrNull; + protected _isMapMode = false; - get root(): OptBTNOrNull { + get isMapMode() { + return this._isMapMode; + } + + protected _store = new Map(); + + get store() { + return this._store; + } + + protected _root?: OptNodeOrNull; + + get root(): OptNodeOrNull { return this._root; } @@ -154,7 +165,7 @@ export class BinaryTree< return this._NIL; } - protected _toEntryFn?: (rawElement: R) => BTNEntry; + protected _toEntryFn?: ToEntryFn; get toEntryFn() { return this._toEntryFn; @@ -184,51 +195,56 @@ export class BinaryTree< createTree(options?: BinaryTreeOptions): TREE { return new BinaryTree([], { iterationType: this.iterationType, + isMapMode: this._isMapMode, toEntryFn: this._toEntryFn, ...options }) as TREE; } /** - * The function `keyValueOrEntryOrRawElementToNode` converts various input types into a node object + * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object * or returns null. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The - * `keyValueOrEntryOrRawElementToNode` function takes in a parameter `keyOrNodeOrEntryOrRaw`, which - * can be of type `BTNKeyOrNodeOrEntry` or `R`. This parameter represents either a key, a + * @param {BTNRep | R} keyNodeEntryOrRaw - The + * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeEntryOrRaw`, which + * can be of type `BTNRep` or `R`. This parameter represents either a key, a * node, an entry - * @param {V} [value] - The `value` parameter in the `keyValueOrEntryOrRawElementToNode` function is + * @param {V} [value] - The `value` parameter in the `keyValueNodeEntryRawToNodeAndValue` function is * an optional parameter of type `V`. It represents the value associated with the key in the node * being created. If a `value` is provided, it will be used when creating the node. If - * @returns The `keyValueOrEntryOrRawElementToNode` function returns an optional node - * (`OptBTNOrNull`) based on the input parameters provided. The function checks the type of the - * input parameter (`keyOrNodeOrEntryOrRaw`) and processes it accordingly to return a node or null + * @returns The `keyValueNodeEntryRawToNodeAndValue` function returns an optional node + * (`OptNodeOrNull`) based on the input parameters provided. The function checks the type of the + * input parameter (`keyNodeEntryOrRaw`) and processes it accordingly to return a node or null * value. */ - keyValueOrEntryOrRawElementToNode( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, + keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep | R, value?: V - ): OptBTNOrNull { - if (keyOrNodeOrEntryOrRaw === undefined) return; - if (keyOrNodeOrEntryOrRaw === null) return null; + ): [OptNodeOrNull, V | undefined] { + if (keyNodeEntryOrRaw === undefined) return [undefined, undefined]; + if (keyNodeEntryOrRaw === null) return [null, undefined]; - if (this.isNode(keyOrNodeOrEntryOrRaw)) return keyOrNodeOrEntryOrRaw; + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - if (this.isEntry(keyOrNodeOrEntryOrRaw)) { - const [key, entryValue] = keyOrNodeOrEntryOrRaw; - if (key === undefined) return; - else if (key === null) return null; - if (this.isKey(key)) return this.createNode(key, value ?? entryValue); + if (this.isEntry(keyNodeEntryOrRaw)) { + const [key, entryValue] = keyNodeEntryOrRaw; + if (key === undefined) return [undefined, undefined]; + else if (key === null) return [null, undefined]; + const finalValue = value ?? entryValue; + return [this.createNode(key, finalValue), finalValue]; } - if (this._toEntryFn) { - const [key, entryValue] = this._toEntryFn(keyOrNodeOrEntryOrRaw as R); - if (this.isKey(key)) return this.createNode(key, value ?? entryValue); - else return; + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value]; + + if (this.isRaw(keyNodeEntryOrRaw)) { + if (this._toEntryFn) { + const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue]; + } + return [undefined, undefined]; } - if (this.isKey(keyOrNodeOrEntryOrRaw)) return this.createNode(keyOrNodeOrEntryOrRaw, value); - - return; + return [undefined, undefined]; } /** @@ -237,8 +253,8 @@ export class BinaryTree< * * The function `ensureNode` in TypeScript checks if a given input is a node, entry, key, or raw * value and returns the corresponding node or null. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `keyOrNodeOrEntryOrRaw` - * parameter in the `ensureNode` function can be of type `BTNKeyOrNodeOrEntry` or `R`. It + * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * parameter in the `ensureNode` function can be of type `BTNRep` or `R`. It * is used to determine whether the input is a key, node, entry, or raw data. The * @param {IterationType} iterationType - The `iterationType` parameter in the `ensureNode` function * is used to specify the type of iteration to be performed. It has a default value of @@ -247,114 +263,117 @@ export class BinaryTree< * conditions specified in the code snippet. */ ensureNode( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, + keyNodeEntryOrRaw: BTNRep | R, iterationType: IterationType = this.iterationType - ): OptBTNOrNull { - if (keyOrNodeOrEntryOrRaw === null) return null; - if (keyOrNodeOrEntryOrRaw === undefined) return; - if (keyOrNodeOrEntryOrRaw === this._NIL) return; - if (this.isNode(keyOrNodeOrEntryOrRaw)) return keyOrNodeOrEntryOrRaw; + ): OptNodeOrNull { + if (keyNodeEntryOrRaw === null) return null; + if (keyNodeEntryOrRaw === undefined) return; + if (keyNodeEntryOrRaw === this._NIL) return; + if (this.isNode(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw; - if (this.isEntry(keyOrNodeOrEntryOrRaw)) { - const key = keyOrNodeOrEntryOrRaw[0]; + if (this.isEntry(keyNodeEntryOrRaw)) { + const key = keyNodeEntryOrRaw[0]; if (key === null) return null; if (key === undefined) return; return this.getNodeByKey(key, iterationType); } if (this._toEntryFn) { - const [key] = this._toEntryFn(keyOrNodeOrEntryOrRaw as R); + const [key] = this._toEntryFn(keyNodeEntryOrRaw as R); if (this.isKey(key)) return this.getNodeByKey(key); } - if (this.isKey(keyOrNodeOrEntryOrRaw)) return this.getNodeByKey(keyOrNodeOrEntryOrRaw, iterationType); + if (this.isKey(keyNodeEntryOrRaw)) return this.getNodeByKey(keyNodeEntryOrRaw, iterationType); return; } /** * The function isNode checks if the input is an instance of BinaryTreeNode. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be either a key, a node, an entry, or raw data. The function is + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be either a key, a node, an entry, or raw data. The function is * checking if the input is an instance of a `BinaryTreeNode` and returning a boolean value * accordingly. - * @returns The function `isNode` is checking if the input `keyOrNodeOrEntryOrRaw` is an instance of + * @returns The function `isNode` is checking if the input `keyNodeEntryOrRaw` is an instance of * `BinaryTreeNode`. If it is, the function returns `true`, indicating that the input is a node. If * it is not an instance of `BinaryTreeNode`, the function returns `false`, indicating that the input * is not a node. */ - isNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - return keyOrNodeOrEntryOrRaw instanceof BinaryTreeNode; + isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + return keyNodeEntryOrRaw instanceof BinaryTreeNode; + } + + isRaw(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is R { + return typeof keyNodeEntryOrRaw === 'object'; } /** * The function `isRealNode` checks if a given input is a valid node in a binary tree. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `keyOrNodeOrEntryOrRaw` - * parameter in the `isRealNode` function can be of type `BTNKeyOrNodeOrEntry` or `R`. + * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * parameter in the `isRealNode` function can be of type `BTNRep` or `R`. * The function checks if the input parameter is a `NODE` type by verifying if it is not equal - * @returns The function `isRealNode` is checking if the input `keyOrNodeOrEntryOrRaw` is a valid + * @returns The function `isRealNode` is checking if the input `keyNodeEntryOrRaw` is a valid * node by comparing it to `this._NIL`, `null`, and `undefined`. If the input is not one of these * values, it then calls the `isNode` method to further determine if the input is a node. The * function will return a boolean value indicating whether the */ - isRealNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - if (keyOrNodeOrEntryOrRaw === this._NIL || keyOrNodeOrEntryOrRaw === null || keyOrNodeOrEntryOrRaw === undefined) - return false; - return this.isNode(keyOrNodeOrEntryOrRaw); + isRealNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + if (keyNodeEntryOrRaw === this._NIL || keyNodeEntryOrRaw === null || keyNodeEntryOrRaw === undefined) return false; + return this.isNode(keyNodeEntryOrRaw); } /** * The function checks if a given input is a valid node or null. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` in the `isRealNodeOrNull` function can be of type `BTNKeyOrNodeOrEntry | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` in the `isRealNodeOrNull` function can be of type `BTNRep` or `R`. It is a union type that can either be a key, a node, an entry, or * @returns The function `isRealNodeOrNull` is returning a boolean value. It checks if the input - * `keyOrNodeOrEntryOrRaw` is either `null` or a real node, and returns `true` if it is a node or + * `keyNodeEntryOrRaw` is either `null` or a real node, and returns `true` if it is a node or * `null`, and `false` otherwise. */ - isRealNodeOrNull(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE | null { - return keyOrNodeOrEntryOrRaw === null || this.isRealNode(keyOrNodeOrEntryOrRaw); + isRealNodeOrNull(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE | null { + return keyNodeEntryOrRaw === null || this.isRealNode(keyNodeEntryOrRaw); } /** * The function isNIL checks if a given key, node, entry, or raw value is equal to the _NIL value. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - BTNKeyOrNodeOrEntry | R} keyNodeEntryOrRaw - BTNRep | R - * @returns The function is checking if the `keyOrNodeOrEntryOrRaw` parameter is equal to the `_NIL` + * @returns The function is checking if the `keyNodeEntryOrRaw` parameter is equal to the `_NIL` * property of the current object and returning a boolean value based on that comparison. */ - isNIL(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): boolean { - return keyOrNodeOrEntryOrRaw === this._NIL; + isNIL(keyNodeEntryOrRaw: BTNRep | R): boolean { + return keyNodeEntryOrRaw === this._NIL; } /** * The function determines whether a given key, node, entry, or raw data is a leaf node in a binary * tree. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `BTNKeyOrNodeOrEntry` or `R`. It represents a + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `BTNRep` or `R`. It represents a * key, node, entry, or raw data in a binary tree structure. The function `isLeaf` checks whether the * provided * @returns The function `isLeaf` returns a boolean value indicating whether the input - * `keyOrNodeOrEntryOrRaw` is a leaf node in a binary tree. + * `keyNodeEntryOrRaw` is a leaf node in a binary tree. */ - isLeaf(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): boolean { - keyOrNodeOrEntryOrRaw = this.ensureNode(keyOrNodeOrEntryOrRaw); - if (keyOrNodeOrEntryOrRaw === undefined) return false; - if (keyOrNodeOrEntryOrRaw === null) return true; - return !this.isRealNode(keyOrNodeOrEntryOrRaw.left) && !this.isRealNode(keyOrNodeOrEntryOrRaw.right); + isLeaf(keyNodeEntryOrRaw: BTNRep | R): boolean { + keyNodeEntryOrRaw = this.ensureNode(keyNodeEntryOrRaw); + if (keyNodeEntryOrRaw === undefined) return false; + if (keyNodeEntryOrRaw === null) return true; + return !this.isRealNode(keyNodeEntryOrRaw.left) && !this.isRealNode(keyNodeEntryOrRaw.right); } /** * The function `isEntry` checks if the input is a BTNEntry object by verifying if it is an array * with a length of 2. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `keyOrNodeOrEntryOrRaw` - * parameter in the `isEntry` function can be of type `BTNKeyOrNodeOrEntry` or type `R`. - * The function checks if the provided `keyOrNodeOrEntryOrRaw` is of type `BTN - * @returns The `isEntry` function is checking if the `keyOrNodeOrEntryOrRaw` parameter is an array + * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * parameter in the `isEntry` function can be of type `BTNRep` or type `R`. + * The function checks if the provided `keyNodeEntryOrRaw` is of type `BTN + * @returns The `isEntry` function is checking if the `keyNodeEntryOrRaw` parameter is an array * with a length of 2. If it is, then it returns `true`, indicating that the parameter is of type * `BTNEntry`. If the condition is not met, it returns `false`. */ - isEntry(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is BTNEntry { - return Array.isArray(keyOrNodeOrEntryOrRaw) && keyOrNodeOrEntryOrRaw.length === 2; + isEntry(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is BTNEntry { + return Array.isArray(keyNodeEntryOrRaw) && keyNodeEntryOrRaw.length === 2; } /** @@ -379,8 +398,8 @@ export class BinaryTree< * * The `add` function in TypeScript adds a new node to a binary tree while handling duplicate keys * and finding the correct insertion position. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `add` method you provided - * seems to be for adding a new node to a binary tree structure. The `keyOrNodeOrEntryOrRaw` + * @param {BTNRep | R} keyNodeEntryOrRaw - The `add` method you provided + * seems to be for adding a new node to a binary tree structure. The `keyNodeEntryOrRaw` * parameter in the method can accept different types of values: * @param {V} [value] - The `value` parameter in the `add` method represents the value associated * with the key that you want to add to the binary tree. When adding a key-value pair to the binary @@ -390,13 +409,14 @@ export class BinaryTree< * node was successful, and `false` if the insertion position could not be found or if a duplicate * key was found and the node was replaced instead of inserted. */ - add(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V): boolean { - const newNode = this.keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw, value); + add(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); if (newNode === undefined) return false; // If the tree is empty, directly set the new node as the root node if (!this._root) { this._setRoot(newNode); + if (this._isMapMode) this._setValue(newNode?.key, newValue); this._size = 1; return true; } @@ -412,6 +432,7 @@ export class BinaryTree< // Check for duplicate keys when newNode is not null if (newNode !== null && cur.key === newNode.key) { this._replaceNode(cur, newNode); + if (this._isMapMode) this._setValue(cur.key, newValue); return true; // If duplicate keys are found, no insertion is performed } @@ -436,6 +457,7 @@ export class BinaryTree< } else if (potentialParent.right === undefined) { potentialParent.right = newNode; } + if (this._isMapMode) this._setValue(newNode?.key, newValue); this._size++; return true; } @@ -450,21 +472,18 @@ export class BinaryTree< * The `addMany` function takes in multiple keys or nodes or entries or raw values along with * optional values, and adds them to a data structure while returning an array indicating whether * each insertion was successful. - * @param keysOrNodesOrEntriesOrRaws - `keysOrNodesOrEntriesOrRaws` is an iterable that can contain a + * @param keysNodesEntriesOrRaws - `keysNodesEntriesOrRaws` is an iterable that can contain a * mix of keys, nodes, entries, or raw values. Each element in this iterable can be of type - * `BTNKeyOrNodeOrEntry` or `R`. + * `BTNRep` or `R`. * @param [values] - The `values` parameter in the `addMany` function is an optional parameter that * accepts an iterable of values. These values correspond to the keys or nodes being added in the - * `keysOrNodesOrEntriesOrRaws` parameter. If provided, the function will iterate over the values and + * `keysNodesEntriesOrRaws` parameter. If provided, the function will iterate over the values and * assign them * @returns The `addMany` method returns an array of boolean values indicating whether each key, * node, entry, or raw value was successfully added to the data structure. Each boolean value * corresponds to the success of adding the corresponding key or value in the input iterable. */ - addMany( - keysOrNodesOrEntriesOrRaws: Iterable | R>, - values?: Iterable - ): boolean[] { + addMany(keysNodesEntriesOrRaws: Iterable | R>, values?: Iterable): boolean[] { // TODO not sure addMany not be run multi times const inserted: boolean[] = []; @@ -473,7 +492,7 @@ export class BinaryTree< valuesIterator = values[Symbol.iterator](); } - for (const keyOrNodeOrEntryOrRaw of keysOrNodesOrEntriesOrRaws) { + for (const keyNodeEntryOrRaw of keysNodesEntriesOrRaws) { let value: V | undefined | null = undefined; if (valuesIterator) { @@ -483,7 +502,7 @@ export class BinaryTree< } } - inserted.push(this.add(keyOrNodeOrEntryOrRaw, value)); + inserted.push(this.add(keyNodeEntryOrRaw, value)); } return inserted; @@ -495,18 +514,15 @@ export class BinaryTree< * * The `refill` function clears the existing data structure and then adds new key-value pairs based * on the provided input. - * @param keysOrNodesOrEntriesOrRaws - The `keysOrNodesOrEntriesOrRaws` parameter in the `refill` - * method can accept an iterable containing a mix of `BTNKeyOrNodeOrEntry` objects or `R` + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the `refill` + * method can accept an iterable containing a mix of `BTNRep` objects or `R` * objects. * @param [values] - The `values` parameter in the `refill` method is an optional parameter that * accepts an iterable of values of type `V` or `undefined`. */ - refill( - keysOrNodesOrEntriesOrRaws: Iterable | R>, - values?: Iterable - ): void { + refill(keysNodesEntriesOrRaws: Iterable | R>, values?: Iterable): void { this.clear(); - this.addMany(keysOrNodesOrEntriesOrRaws, values); + this.addMany(keysNodesEntriesOrRaws, values); } /** @@ -515,7 +531,7 @@ export class BinaryTree< * * The function `delete` in TypeScript implements the deletion of a node in a binary tree and returns * the deleted node along with information for tree balancing. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw + * @param {BTNRep | R} keyNodeEntryOrRaw * - The `delete` method you provided is used to delete a node from a binary tree based on the key, * node, entry or raw data. The method returns an array of * `BinaryTreeDeleteResult` objects containing information about the deleted node and whether @@ -524,11 +540,11 @@ export class BinaryTree< * the array contains information about the node that was deleted (`deleted`) and the node that may * need to be balanced (`needBalanced`). */ - delete(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): BinaryTreeDeleteResult[] { + delete(keyNodeEntryOrRaw: BTNRep | R): BinaryTreeDeleteResult[] { const deletedResult: BinaryTreeDeleteResult[] = []; if (!this._root) return deletedResult; - const curr = this.getNode(keyOrNodeOrEntryOrRaw); + const curr = this.getNode(keyNodeEntryOrRaw); if (!curr) return deletedResult; const parent: NODE | undefined = curr?.parent; @@ -565,6 +581,7 @@ export class BinaryTree< this._size = this._size - 1; deletedResult.push({ deleted: orgCurrent, needBalanced }); + if (this._isMapMode && orgCurrent) this._store.delete(orgCurrent.key); return deletedResult; } @@ -574,12 +591,12 @@ export class BinaryTree< * * The function `getNodes` retrieves nodes from a binary tree based on a key, node, entry, raw data, * or predicate, with options for recursive or iterative traversal. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} keyOrNodeOrEntryOrRawOrPredicate + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate * - The `getNodes` function you provided takes several parameters: * @param [onlyOne=false] - The `onlyOne` parameter in the `getNodes` function is a boolean flag that * determines whether to return only the first node that matches the criteria specified by the - * `keyOrNodeOrEntryOrRawOrPredicate` parameter. If `onlyOne` is set to `true`, the function will - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * `keyNodeEntryRawOrPredicate` parameter. If `onlyOne` is set to `true`, the function will + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getNodes` function is used to specify the starting point for traversing the binary tree. It * represents the root node of the binary tree or the node from which the traversal should begin. If * not provided, the default value is set to `this._root @@ -590,16 +607,16 @@ export class BinaryTree< * based on the input parameters and the iteration type specified. */ getNodes( - keyOrNodeOrEntryOrRawOrPredicate: BTNKeyOrNodeOrEntry | R | BTNPredicate, + keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, onlyOne = false, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): NODE[] { - if (keyOrNodeOrEntryOrRawOrPredicate === undefined) return []; - if (keyOrNodeOrEntryOrRawOrPredicate === null) return []; - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return []; - const callback = this._ensurePredicate(keyOrNodeOrEntryOrRawOrPredicate); + if (keyNodeEntryRawOrPredicate === undefined) return []; + if (keyNodeEntryRawOrPredicate === null) return []; + startNode = this.ensureNode(startNode); + if (!startNode) return []; + const callback = this._ensurePredicate(keyNodeEntryRawOrPredicate); const ans: NODE[] = []; @@ -614,9 +631,9 @@ export class BinaryTree< if (this.isRealNode(cur.right)) dfs(cur.right); }; - dfs(beginRoot); + dfs(startNode); } else { - const stack = [beginRoot]; + const stack = [startNode]; while (stack.length > 0) { const cur = stack.pop(); if (this.isRealNode(cur)) { @@ -639,10 +656,10 @@ export class BinaryTree< * * The `getNode` function retrieves a node based on the provided key, node, entry, raw data, or * predicate. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} keyOrNodeOrEntryOrRawOrPredicate - * - The `keyOrNodeOrEntryOrRawOrPredicate` parameter in the `getNode` function can accept a key, + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * - The `keyNodeEntryRawOrPredicate` parameter in the `getNode` function can accept a key, * node, entry, raw data, or a predicate function. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getNode` function is used to specify the starting point for searching for a node in a binary * tree. If no specific starting point is provided, the default value is set to `this._root`, which * is typically the root node of the binary tree. @@ -654,11 +671,11 @@ export class BinaryTree< * or `null` if no matching node is found. */ getNode( - keyOrNodeOrEntryOrRawOrPredicate: BTNKeyOrNodeOrEntry | R | BTNPredicate, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType - ): OptBTNOrNull { - return this.getNodes(keyOrNodeOrEntryOrRawOrPredicate, true, beginRoot, iterationType)[0] ?? null; + ): OptNodeOrNull { + return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? null; } /** @@ -672,9 +689,9 @@ export class BinaryTree< * specifies how the tree nodes should be traversed when searching for a node with the given key. It * is an optional parameter with a default value of `this.iterationType`. * @returns The `getNodeByKey` function is returning an optional binary tree node - * (`OptBTNOrNull`). + * (`OptNodeOrNull`). */ - getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptBTNOrNull { + getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNodeOrNull { return this.getNode(key, this._root, iterationType); } @@ -684,10 +701,10 @@ export class BinaryTree< * * This function overrides the `get` method to retrieve the value associated with a specified key, * node, entry, raw data, or predicate in a data structure. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} keyOrNodeOrEntryOrRawOrPredicate - * - The `keyOrNodeOrEntryOrRawOrPredicate` parameter in the `get` method can accept one of the + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * - The `keyNodeEntryRawOrPredicate` parameter in the `get` method can accept one of the * following types: - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `get` + * @param {BTNRep | R} startNode - The `startNode` parameter in the `get` * method is used to specify the starting point for searching for a key or node in the binary tree. * If no specific starting point is provided, the default starting point is the root of the binary * tree (`this._root`). @@ -701,11 +718,16 @@ export class BinaryTree< * `undefined`. */ override get( - keyOrNodeOrEntryOrRawOrPredicate: BTNKeyOrNodeOrEntry | R | BTNPredicate, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + keyNodeEntryRawOrPredicate: BTNRep | R, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): V | undefined { - return this.getNode(keyOrNodeOrEntryOrRawOrPredicate, beginRoot, iterationType)?.value; + if (this._isMapMode) { + const key = this._getKey(keyNodeEntryRawOrPredicate); + if (key === null || key === undefined) return; + return this._store.get(key); + } + return this.getNode(keyNodeEntryRawOrPredicate, startNode, iterationType)?.value; } /** @@ -714,10 +736,10 @@ export class BinaryTree< * * The `has` function in TypeScript checks if a specified key, node, entry, raw data, or predicate * exists in the data structure. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} keyOrNodeOrEntryOrRawOrPredicate - * - The `keyOrNodeOrEntryOrRawOrPredicate` parameter in the `override has` method can accept one of + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * - The `keyNodeEntryRawOrPredicate` parameter in the `override has` method can accept one of * the following types: - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `override` method is used to specify the starting point for the search operation within the data * structure. It defaults to `this._root` if not provided explicitly. * @param {IterationType} iterationType - The `iterationType` parameter in the `override has` method @@ -730,11 +752,11 @@ export class BinaryTree< * Otherwise, it returns `false`. */ override has( - keyOrNodeOrEntryOrRawOrPredicate: BTNKeyOrNodeOrEntry | R | BTNPredicate, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): boolean { - return this.getNodes(keyOrNodeOrEntryOrRawOrPredicate, true, beginRoot, iterationType).length > 0; + return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType).length > 0; } /** @@ -744,8 +766,8 @@ export class BinaryTree< * The `clear` function resets the root node and size of a data structure to empty. */ clear() { - this._setRoot(undefined); - this._size = 0; + this._clearNodes(); + if (this._isMapMode) this._clearValues(); } /** @@ -767,17 +789,17 @@ export class BinaryTree< * * The function checks if a binary tree is perfectly balanced by comparing its minimum height with * its height. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter is the starting + * @param {BTNRep | R} startNode - The `startNode` parameter is the starting * point for checking if the binary tree is perfectly balanced. It represents the root node of the * binary tree or a specific node from which the balance check should begin. * @returns The method `isPerfectlyBalanced` is returning a boolean value, which indicates whether - * the tree starting from the `beginRoot` node is perfectly balanced or not. The return value is + * the tree starting from the `startNode` node is perfectly balanced or not. The return value is * determined by comparing the minimum height of the tree with the height of the tree. If the minimum * height plus 1 is greater than or equal to the height of the tree, then it is considered perfectly * balanced and */ - isPerfectlyBalanced(beginRoot: BTNKeyOrNodeOrEntry | R = this._root): boolean { - return this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot); + isPerfectlyBalanced(startNode: BTNRep | R = this._root): boolean { + return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode); } /** @@ -786,7 +808,7 @@ export class BinaryTree< * * The function `isBST` in TypeScript checks if a binary search tree is valid using either recursive * or iterative methods. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `isBST` + * @param {BTNRep | R} startNode - The `startNode` parameter in the `isBST` * function represents the starting point for checking whether a binary search tree (BST) is valid. * It can be a node in the BST or a reference to the root of the BST. If no specific node is * provided, the function will default to @@ -798,31 +820,28 @@ export class BinaryTree< * the tree satisfies the BST property, where for every node, all nodes in its left subtree have keys * less than the node's key, and all nodes in its right subtree have keys greater than the node's */ - isBST( - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, - iterationType: IterationType = this.iterationType - ): boolean { + isBST(startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType): boolean { // TODO there is a bug - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return true; + startNode = this.ensureNode(startNode); + if (!startNode) return true; if (iterationType === 'RECURSIVE') { - const dfs = (cur: OptBTNOrNull, min: number, max: number): boolean => { + const dfs = (cur: OptNodeOrNull, min: number, max: number): boolean => { if (!this.isRealNode(cur)) return true; const numKey = Number(cur.key); if (numKey <= min || numKey >= max) return false; return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max); }; - const isStandardBST = dfs(beginRoot, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); - const isInverseBST = dfs(beginRoot, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); + const isStandardBST = dfs(startNode, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); + const isInverseBST = dfs(startNode, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER); return isStandardBST || isInverseBST; } else { const checkBST = (checkMax = false) => { const stack = []; let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER; // @ts-ignore - let curr: OptBTNOrNull = beginRoot; + let curr: OptNodeOrNull = startNode; while (this.isRealNode(curr) || stack.length > 0) { while (this.isRealNode(curr)) { stack.push(curr); @@ -847,23 +866,20 @@ export class BinaryTree< * Space Complexity: O(1) * * The `getDepth` function calculates the depth between two nodes in a binary tree. - * @param {BTNKeyOrNodeOrEntry | R} dist - The `dist` parameter in the `getDepth` + * @param {BTNRep | R} dist - The `dist` parameter in the `getDepth` * function represents the node or entry in a binary tree map, or a reference to a node in the tree. - * It is the target node for which you want to calculate the depth from the `beginRoot` node. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * It is the target node for which you want to calculate the depth from the `startNode` node. + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getDepth` function represents the starting point from which you want to calculate the depth of a * given node or entry in a binary tree. If no specific starting point is provided, the default value - * for `beginRoot` is set to the root of the binary + * for `startNode` is set to the root of the binary * @returns The `getDepth` method returns the depth of a given node `dist` relative to the - * `beginRoot` node in a binary tree. If the `dist` node is not found in the path to the `beginRoot` + * `startNode` node in a binary tree. If the `dist` node is not found in the path to the `startNode` * node, it returns the depth of the `dist` node from the root of the tree. */ - getDepth( - dist: BTNKeyOrNodeOrEntry | R, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root - ): number { + getDepth(dist: BTNRep | R, startNode: BTNRep | R = this._root): number { let distEnsured = this.ensureNode(dist); - const beginRootEnsured = this.ensureNode(beginRoot); + const beginRootEnsured = this.ensureNode(startNode); let depth = 0; while (distEnsured?.parent) { if (distEnsured === beginRootEnsured) { @@ -881,7 +897,7 @@ export class BinaryTree< * * The `getHeight` function calculates the maximum height of a binary tree using either a recursive * or iterative approach in TypeScript. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter is the starting + * @param {BTNRep | R} startNode - The `startNode` parameter is the starting * point from which the height of the binary tree will be calculated. It can be a node in the binary * tree or a reference to the root of the tree. If not provided, it defaults to the root of the * binary tree data structure. @@ -892,24 +908,21 @@ export class BinaryTree< * root node. The height is calculated based on the maximum depth of the tree, considering either a * recursive approach or an iterative approach depending on the `iterationType` parameter. */ - getHeight( - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, - iterationType: IterationType = this.iterationType - ): number { - beginRoot = this.ensureNode(beginRoot); - if (!this.isRealNode(beginRoot)) return -1; + getHeight(startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType): number { + startNode = this.ensureNode(startNode); + if (!this.isRealNode(startNode)) return -1; if (iterationType === 'RECURSIVE') { - const _getMaxHeight = (cur: OptBTNOrNull): number => { + const _getMaxHeight = (cur: OptNodeOrNull): number => { if (!this.isRealNode(cur)) return -1; const leftHeight = _getMaxHeight(cur.left); const rightHeight = _getMaxHeight(cur.right); return Math.max(leftHeight, rightHeight) + 1; }; - return _getMaxHeight(beginRoot); + return _getMaxHeight(startNode); } else { - const stack: { node: NODE; depth: number }[] = [{ node: beginRoot, depth: 0 }]; + const stack: { node: NODE; depth: number }[] = [{ node: startNode, depth: 0 }]; let maxHeight = 0; while (stack.length > 0) { @@ -931,7 +944,7 @@ export class BinaryTree< * * The `getMinHeight` function calculates the minimum height of a binary tree using either a * recursive or iterative approach in TypeScript. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getMinHeight` function represents the starting node from which the minimum height of the binary * tree will be calculated. It is either a node in the binary tree or a reference to the root of the * tree. If not provided, the default value is the root @@ -944,14 +957,14 @@ export class BinaryTree< * a stack) based on the `iterationType` parameter. */ getMinHeight( - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): number { - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return -1; + startNode = this.ensureNode(startNode); + if (!startNode) return -1; if (iterationType === 'RECURSIVE') { - const _getMinHeight = (cur: OptBTNOrNull): number => { + const _getMinHeight = (cur: OptNodeOrNull): number => { if (!this.isRealNode(cur)) return 0; if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return 0; const leftMinHeight = _getMinHeight(cur.left); @@ -959,11 +972,11 @@ export class BinaryTree< return Math.min(leftMinHeight, rightMinHeight) + 1; }; - return _getMinHeight(beginRoot); + return _getMinHeight(startNode); } else { const stack: NODE[] = []; - let node: OptBTNOrNull = beginRoot, - last: OptBTNOrNull = null; + let node: OptNodeOrNull = startNode, + last: OptNodeOrNull = null; const depths: Map = new Map(); while (stack.length > 0 || node) { @@ -985,7 +998,7 @@ export class BinaryTree< } } - return depths.get(beginRoot)!; + return depths.get(startNode)!; } } @@ -999,7 +1012,7 @@ export class BinaryTree< * the path to the root. It is expected to be a function that takes a node as an argument and returns * a value based on that node. The return type of the callback function is determined by the generic * type `C - * @param {BTNKeyOrNodeOrEntry | R} beginNode - The `beginNode` parameter in the + * @param {BTNRep | R} beginNode - The `beginNode` parameter in the * `getPathToRoot` function can be either a key, a node, an entry, or any other value of type `R`. * @param [isReverse=true] - The `isReverse` parameter in the `getPathToRoot` function determines * whether the resulting path from the given `beginNode` to the root should be in reverse order or @@ -1009,9 +1022,9 @@ export class BinaryTree< * array is either in reverse order or in the original order based on the value of the `isReverse` * parameter. */ - getPathToRoot>>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginNode: BTNKeyOrNodeOrEntry | R, + getPathToRoot>>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + beginNode: BTNRep | R, isReverse = true ): ReturnType[] { const result: ReturnType[] = []; @@ -1036,8 +1049,8 @@ export class BinaryTree< * tail-recursive iteration. * @param {C} callback - The `callback` parameter is a function that will be called with the leftmost * node of a binary tree or with `undefined` if the tree is empty. It is provided with a default - * value of `_DEFAULT_BTN_CALLBACK` if not specified. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * value of `_DEFAULT_NODE_CALLBACK` if not specified. + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getLeftMost` function represents the starting point for finding the leftmost node in a binary * tree. It can be either a key, a node, or an entry in the binary tree structure. If no specific * starting point is provided, the function will default @@ -1045,19 +1058,19 @@ export class BinaryTree< * specifies the type of iteration to be used when traversing the binary tree nodes. It can have two * possible values: * @returns The `getLeftMost` function returns the result of the callback function `C` applied to the - * leftmost node in the binary tree starting from the `beginRoot` node. If the `beginRoot` node is - * `NIL`, it returns the result of the callback function applied to `undefined`. If the `beginRoot` + * leftmost node in the binary tree starting from the `startNode` node. If the `startNode` node is + * `NIL`, it returns the result of the callback function applied to `undefined`. If the `startNode` * node is not a real node, it returns the result of the callback */ - getLeftMost>>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + getLeftMost>>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType { - if (this.isNIL(beginRoot)) return callback(undefined); - beginRoot = this.ensureNode(beginRoot); + if (this.isNIL(startNode)) return callback(undefined); + startNode = this.ensureNode(startNode); - if (!this.isRealNode(beginRoot)) return callback(beginRoot); + if (!this.isRealNode(startNode)) return callback(startNode); if (iterationType === 'RECURSIVE') { const dfs = (cur: NODE): NODE => { @@ -1065,7 +1078,7 @@ export class BinaryTree< return dfs(cur.left); }; - return callback(dfs(beginRoot)); + return callback(dfs(startNode)); } else { // Indirect implementation of iteration using tail recursion optimization const dfs = trampoline((cur: NODE): NODE => { @@ -1073,7 +1086,7 @@ export class BinaryTree< return dfs.cont(cur.left); }); - return callback(dfs(beginRoot)); + return callback(dfs(startNode)); } } @@ -1084,10 +1097,10 @@ export class BinaryTree< * The function `getRightMost` retrieves the rightmost node in a binary tree using either recursive * or iterative traversal methods. * @param {C} callback - The `callback` parameter is a function that will be called with the result - * of finding the rightmost node in a binary tree. It is of type `BTNCallback>`, + * of finding the rightmost node in a binary tree. It is of type `NodeCallback>`, * which means it is a callback function that can accept either an optional binary tree node or null * as - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getRightMost` function represents the starting point for finding the rightmost node in a binary * tree. It can be either a key, a node, or an entry in the binary tree structure. If no specific * starting point is provided, the function will default @@ -1099,15 +1112,15 @@ export class BinaryTree< * the binary tree structure, determined based on the specified iteration type ('RECURSIVE' or * other). */ - getRightMost>>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + getRightMost>>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType { - if (this.isNIL(beginRoot)) return callback(undefined); + if (this.isNIL(startNode)) return callback(undefined); // TODO support get right most by passing key in - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return callback(beginRoot); + startNode = this.ensureNode(startNode); + if (!startNode) return callback(startNode); if (iterationType === 'RECURSIVE') { const dfs = (cur: NODE): NODE => { @@ -1115,7 +1128,7 @@ export class BinaryTree< return dfs(cur.right); }; - return callback(dfs(beginRoot)); + return callback(dfs(startNode)); } else { // Indirect implementation of iteration using tail recursion optimization const dfs = trampoline((cur: NODE) => { @@ -1123,7 +1136,7 @@ export class BinaryTree< return dfs.cont(cur.right); }); - return callback(dfs(beginRoot)); + return callback(dfs(startNode)); } } @@ -1142,7 +1155,7 @@ export class BinaryTree< */ getPredecessor(node: NODE): NODE { if (this.isRealNode(node.left)) { - let predecessor: OptBTNOrNull = node.left; + let predecessor: OptNodeOrNull = node.left; while (!this.isRealNode(predecessor) || (this.isRealNode(predecessor.right) && predecessor.right !== node)) { if (this.isRealNode(predecessor)) { predecessor = predecessor.right; @@ -1167,7 +1180,7 @@ export class BinaryTree< * have a right child, the function traverses up the parent nodes until it finds a node that is not * the right child of its parent, and returns that node */ - getSuccessor(x?: K | NODE | null): OptBTNOrNull { + getSuccessor(x?: K | NODE | null): OptNodeOrNull { x = this.ensureNode(x); if (!this.isRealNode(x)) return undefined; @@ -1175,7 +1188,7 @@ export class BinaryTree< return this.getLeftMost(node => node, x.right); } - let y: OptBTNOrNull = x.parent; + let y: OptNodeOrNull = x.parent; while (this.isRealNode(y) && x === y.right) { x = y; y = y.parent; @@ -1183,17 +1196,17 @@ export class BinaryTree< return y; } - dfs>( + dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: BTNKeyOrNodeOrEntry | R, + startNode?: BTNRep | R, iterationType?: IterationType ): ReturnType[]; - dfs>( + dfs>( callback?: C, pattern?: DFSOrderPattern, - beginRoot?: BTNKeyOrNodeOrEntry | R, + startNode?: BTNRep | R, iterationType?: IterationType, includeNull?: boolean ): ReturnType[]; @@ -1205,14 +1218,14 @@ export class BinaryTree< * The function `dfs` performs a depth-first search traversal on a binary tree structure based on the * specified parameters. * @param {C} callback - The `callback` parameter is a generic type `C` that extends the - * `BTNCallback` interface with a type parameter of `OptBTNOrNull`. It has a default value of - * `this._DEFAULT_BTN_CALLBACK as C`. + * `NodeCallback` interface with a type parameter of `OptNodeOrNull`. It has a default value of + * `this._DEFAULT_NODE_CALLBACK as C`. * @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 the tree. The * possible values for the `pattern` parameter are: - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `dfs` + * @param {BTNRep | R} startNode - The `startNode` parameter in the `dfs` * method is used to specify the starting point for the Depth-First Search traversal. It can be - * either a `BTNKeyOrNodeOrEntry` object representing a key, node, or entry in the binary tree map, + * either a `BTNRep` object representing a key, node, or entry in the binary tree map, * or it can be a * @param {IterationType} iterationType - The `iterationType` parameter in the `dfs` method specifies * the type of iteration to be performed during the depth-first search traversal. It is used to @@ -1224,28 +1237,28 @@ export class BinaryTree< * @returns The `dfs` method is returning an array of the return type specified by the generic type * parameter `C`. The return type is determined by the callback function provided to the method. */ - dfs>>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, + dfs>>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return []; - return this._dfs(callback, pattern, beginRoot, iterationType, includeNull); + startNode = this.ensureNode(startNode); + if (!startNode) return []; + return this._dfs(callback, pattern, startNode, iterationType, includeNull); } - bfs>( + bfs>( callback?: C, - beginRoot?: BTNKeyOrNodeOrEntry | R, + startNode?: BTNRep | R, iterationType?: IterationType, includeNull?: false ): ReturnType[]; - bfs>( + bfs>( callback?: C, - beginRoot?: BTNKeyOrNodeOrEntry | R, + startNode?: BTNRep | R, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1258,8 +1271,8 @@ export class BinaryTree< * tree, executing a specified callback function on each node visited. * @param {C} callback - The `callback` parameter in the `bfs` function is a function that will be * called on each node visited during the breadth-first search traversal. It is a generic type `C` - * that extends the `BTNCallback` type, which takes a parameter of type `NODE` or `null`. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `bfs` + * that extends the `NodeCallback` type, which takes a parameter of type `NODE` or `null`. + * @param {BTNRep | R} startNode - The `startNode` parameter in the `bfs` * function represents the starting point for the breadth-first search traversal in a binary tree. It * can be specified as a key, node, or entry in the binary tree structure. If not provided, the * default value is the root node of the binary @@ -1273,19 +1286,19 @@ export class BinaryTree< * @returns The `bfs` function returns an array of values that are the result of applying the * provided callback function to each node in the binary tree in a breadth-first search manner. */ - bfs>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + bfs>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return []; + startNode = this.ensureNode(startNode); + if (!startNode) return []; - const ans: ReturnType>[] = []; + const ans: ReturnType>[] = []; if (iterationType === 'RECURSIVE') { - const queue: Queue> = new Queue>([beginRoot]); + const queue: Queue> = new Queue>([startNode]); const dfs = (level: number) => { if (queue.size === 0) return; @@ -1306,7 +1319,7 @@ export class BinaryTree< dfs(0); } else { - const queue = new Queue>([beginRoot]); + const queue = new Queue>([startNode]); while (queue.size > 0) { const levelSize = queue.size; @@ -1335,7 +1348,7 @@ export class BinaryTree< * structure based on a specified callback and iteration type. * @param {C} callback - The `callback` parameter is a function that will be called on each leaf node * in the binary tree. It is optional and defaults to a default callback function if not provided. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `leaves` + * @param {BTNRep | R} startNode - The `startNode` parameter in the `leaves` * method is used to specify the starting point for finding and processing the leaves of a binary * tree. It can be provided as either a key, a node, or an entry in the binary tree structure. If not * explicitly provided, the default value @@ -1345,14 +1358,14 @@ export class BinaryTree< * @returns The `leaves` method returns an array of values that are the result of applying the * provided callback function to each leaf node in the binary tree. */ - leaves>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + leaves>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { - beginRoot = this.ensureNode(beginRoot); - const leaves: ReturnType>[] = []; - if (!this.isRealNode(beginRoot)) return []; + startNode = this.ensureNode(startNode); + const leaves: ReturnType>[] = []; + if (!this.isRealNode(startNode)) return []; if (iterationType === 'RECURSIVE') { const dfs = (cur: NODE) => { @@ -1364,9 +1377,9 @@ export class BinaryTree< if (this.isRealNode(cur.right)) dfs(cur.right); }; - dfs(beginRoot); + dfs(startNode); } else { - const queue = new Queue([beginRoot]); + const queue = new Queue([startNode]); while (queue.size > 0) { const cur = queue.shift(); if (this.isRealNode(cur)) { @@ -1382,16 +1395,16 @@ export class BinaryTree< return leaves; } - listLevels>( + listLevels>( callback?: C, - beginRoot?: BTNKeyOrNodeOrEntry | R, + startNode?: BTNRep | R, iterationType?: IterationType, includeNull?: false ): ReturnType[][]; - listLevels>( + listLevels>( callback?: C, - beginRoot?: BTNKeyOrNodeOrEntry | R, + startNode?: BTNRep | R, iterationType?: IterationType, includeNull?: true ): ReturnType[][]; @@ -1405,7 +1418,7 @@ export class BinaryTree< * @param {C} callback - The `callback` parameter is a function that will be applied to each node in * the binary tree during the traversal. It is used to process each node and determine what * information to include in the output for each level of the tree. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `listLevels` function represents the starting point for traversing the binary tree. It can be * either a key, a node, or an entry in the binary tree. If not provided, the default value is the * root of the binary tree. @@ -1420,15 +1433,15 @@ export class BinaryTree< * level in a binary tree. Each inner array contains the return value of the provided callback * function applied to the nodes at that level. */ - listLevels>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + listLevels>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[][] { - beginRoot = this.ensureNode(beginRoot); + startNode = this.ensureNode(startNode); const levelsNodes: ReturnType[][] = []; - if (!beginRoot) return levelsNodes; + if (!startNode) return levelsNodes; if (iterationType === 'RECURSIVE') { const _recursive = (node: NODE | null, level: number) => { @@ -1443,9 +1456,9 @@ export class BinaryTree< } }; - _recursive(beginRoot, 0); + _recursive(startNode, 0); } else { - const stack: [NODE | null, number][] = [[beginRoot, 0]]; + const stack: [NODE | null, number][] = [[startNode, 0]]; while (stack.length > 0) { const head = stack.pop()!; @@ -1475,11 +1488,11 @@ export class BinaryTree< * Morris Traversal algorithm with different order patterns. * @param {C} callback - The `callback` parameter in the `morris` function is a function that will be * called on each node in the binary tree during the traversal. It is of type `C`, which extends the - * `BTNCallback` type. The default value for `callback` is `this._DEFAULT + * `NodeCallback` type. The default value for `callback` is `this._DEFAULT * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `morris` function specifies * the type of Depth-First Search (DFS) order pattern to traverse the binary tree. The possible * values for the `pattern` parameter are: - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `morris` + * @param {BTNRep | R} startNode - The `startNode` parameter in the `morris` * function is the starting point for the Morris traversal algorithm. It represents the root node of * the binary tree or the node from which the traversal should begin. It can be provided as either a * key, a node, an entry, or a reference @@ -1487,19 +1500,19 @@ export class BinaryTree< * provided callback function to each node in the binary tree in the specified order pattern (IN, * PRE, or POST). */ - morris>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, + morris>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - beginRoot: BTNKeyOrNodeOrEntry | R = this._root + startNode: BTNRep | R = this._root ): ReturnType[] { - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return []; - const ans: ReturnType>[] = []; + startNode = this.ensureNode(startNode); + if (!startNode) return []; + const ans: ReturnType>[] = []; - let cur: OptBTNOrNull = beginRoot; - const _reverseEdge = (node: OptBTNOrNull) => { - let pre: OptBTNOrNull = null; - let next: OptBTNOrNull = null; + let cur: OptNodeOrNull = startNode; + const _reverseEdge = (node: OptNodeOrNull) => { + let pre: OptNodeOrNull = null; + let next: OptNodeOrNull = null; while (node) { next = node.right; node.right = pre; @@ -1508,9 +1521,9 @@ export class BinaryTree< } return pre; }; - const _printEdge = (node: OptBTNOrNull) => { - const tail: OptBTNOrNull = _reverseEdge(node); - let cur: OptBTNOrNull = tail; + const _printEdge = (node: OptNodeOrNull) => { + const tail: OptNodeOrNull = _reverseEdge(node); + let cur: OptNodeOrNull = tail; while (cur) { ans.push(callback(cur)); cur = cur.right; @@ -1567,7 +1580,7 @@ export class BinaryTree< } cur = cur.right; } - _printEdge(beginRoot); + _printEdge(startNode); break; } return ans; @@ -1589,12 +1602,16 @@ export class BinaryTree< this.bfs( node => { if (node === null) cloned.add(null); - else cloned.add([node.key, node.value]); + else { + if (this._isMapMode) cloned.add([node.key, this._store.get(node.key)]); + else cloned.add([node.key, node.value]); + } }, this._root, this.iterationType, true ); + if (this._isMapMode) cloned._store = this._store; return cloned; } @@ -1665,7 +1682,7 @@ export class BinaryTree< * * The function `toVisual` in TypeScript overrides the visual representation of a binary tree with * customizable options for displaying undefined, null, and sentinel nodes. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `toVisual` method is used to specify the starting point for visualizing the binary tree structure. * It can be a node, key, entry, or the root of the tree. If no specific starting point is provided, * the default is set to the root @@ -1677,20 +1694,17 @@ export class BinaryTree< * the lines to the output string. The final output string contains the visual representation of the * binary tree with the specified options. */ - override toVisual( - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, - options?: BinaryTreePrintOptions - ): string { + override toVisual(startNode: BTNRep | R = this._root, options?: BinaryTreePrintOptions): string { const opts = { isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false, ...options }; - beginRoot = this.ensureNode(beginRoot); + startNode = this.ensureNode(startNode); let output = ''; - if (!beginRoot) return output; + if (!startNode) return output; if (opts.isShowUndefined) output += `U for undefined\n`; if (opts.isShowNull) output += `N for null\n`; if (opts.isShowRedBlackNIL) output += `S for Sentinel Node(NIL)\n`; - const display = (root: OptBTNOrNull): void => { + const display = (root: OptNodeOrNull): void => { const [lines, , ,] = this._displayAux(root, opts); let paragraph = ''; for (const line of lines) { @@ -1699,7 +1713,7 @@ export class BinaryTree< output += paragraph; }; - display(beginRoot); + display(startNode); return output; } @@ -1713,13 +1727,13 @@ export class BinaryTree< * printing options for the binary tree. It is an optional parameter that allows you to customize how * the binary tree is printed, such as choosing between different traversal orders or formatting * options. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `override print` method is used to specify the starting point for printing the binary tree. It can * be either a key, a node, an entry, or the root of the tree. If no specific starting point is * provided, the default value is set to */ - override print(options?: BinaryTreePrintOptions, beginRoot: BTNKeyOrNodeOrEntry | R = this._root) { - console.log(this.toVisual(beginRoot, options)); + override print(options?: BinaryTreePrintOptions, startNode: BTNRep | R = this._root) { + console.log(this.toVisual(startNode, options)); } /** @@ -1730,13 +1744,13 @@ export class BinaryTree< * the specified order pattern and callback function. * @param {C} callback - The `callback` parameter in the `_dfs` method is a function that will be * called on each node visited during the depth-first search traversal. It is of type `C`, which - * extends `BTNCallback>`. The default value for this parameter is `this._DEFAULT + * extends `NodeCallback>`. The default value for this parameter is `this._DEFAULT * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `_dfs` method specifies the * order in which the nodes are visited during the Depth-First Search traversal. It can have one of * the following values: - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the `_dfs` + * @param {BTNRep | R} startNode - The `startNode` 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 a `BTNKeyOrNodeOrEntry` object or a reference to the root node + * tree. It can be provided as either a `BTNRep` object or a reference to the root node * of 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 of a @@ -1764,26 +1778,26 @@ export class BinaryTree< * @returns The function `_dfs` returns an array of the return type of the callback function provided * as input. */ - protected _dfs>>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, + protected _dfs>>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false, - shouldVisitLeft: (node: OptBTNOrNull) => boolean = node => !!node, - shouldVisitRight: (node: OptBTNOrNull) => boolean = node => !!node, - shouldVisitRoot: (node: OptBTNOrNull) => boolean = node => { + shouldVisitLeft: (node: OptNodeOrNull) => boolean = node => !!node, + shouldVisitRight: (node: OptNodeOrNull) => boolean = node => !!node, + shouldVisitRoot: (node: OptNodeOrNull) => boolean = node => { if (includeNull) return this.isRealNodeOrNull(node); return this.isRealNode(node); }, - shouldProcessRoot: (node: OptBTNOrNull) => boolean = node => this.isRealNodeOrNull(node) + shouldProcessRoot: (node: OptNodeOrNull) => boolean = node => this.isRealNodeOrNull(node) ): ReturnType[] { - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return []; + startNode = this.ensureNode(startNode); + if (!startNode) return []; const ans: ReturnType[] = []; if (iterationType === 'RECURSIVE') { - const dfs = (node: OptBTNOrNull) => { + const dfs = (node: OptNodeOrNull) => { if (!shouldVisitRoot(node)) return; const visitLeft = () => { @@ -1812,9 +1826,9 @@ export class BinaryTree< } }; - dfs(beginRoot); + dfs(startNode); } else { - const stack: DFSStackItem[] = [{ opt: DFSOperation.VISIT, node: beginRoot }]; + const stack: DFSStackItem[] = [{ opt: DFSOperation.VISIT, node: startNode }]; const pushLeft = (cur: DFSStackItem) => { if (shouldVisitLeft(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.left }); @@ -1876,8 +1890,8 @@ export class BinaryTree< if (!node) return; if (this.iterationType === 'ITERATIVE') { - const stack: OptBTNOrNull[] = []; - let current: OptBTNOrNull = node; + const stack: OptNodeOrNull[] = []; + let current: OptNodeOrNull = node; while (current || stack.length > 0) { while (this.isRealNode(current)) { @@ -1888,7 +1902,8 @@ export class BinaryTree< current = stack.pop(); if (this.isRealNode(current)) { - yield [current.key, current.value]; + if (this._isMapMode) yield [current.key, this._store.get(current.key)]; + else yield [current.key, current.value]; current = current.right; } } @@ -1896,7 +1911,8 @@ export class BinaryTree< if (node.left && this.isRealNode(node)) { yield* this[Symbol.iterator](node.left); } - yield [node.key, node.value]; + if (this._isMapMode) yield [node.key, this._store.get(node.key)]; + else yield [node.key, node.value]; if (node.right && this.isRealNode(node)) { yield* this[Symbol.iterator](node.right); } @@ -1918,7 +1934,7 @@ export class BinaryTree< * information about how to display a node in a binary tree. The `NodeDisplayLayout` consists of four * elements: */ - protected _displayAux(node: OptBTNOrNull, options: BinaryTreePrintOptions): NodeDisplayLayout { + protected _displayAux(node: OptNodeOrNull, options: BinaryTreePrintOptions): NodeDisplayLayout { const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options; const emptyDisplayLayout = [['─'], 1, 0, 0]; @@ -1986,27 +2002,24 @@ export class BinaryTree< } } - protected _DEFAULT_BTN_CALLBACK = (node: OptBTNOrNull) => (node ? node.key : undefined); + protected _DEFAULT_NODE_CALLBACK = (node: OptNodeOrNull) => (node ? node.key : undefined); /** * Time Complexity: O(1) * Space Complexity: O(1) * * The _swapProperties function swaps key and value properties between two nodes in a binary tree. - * @param {BTNKeyOrNodeOrEntry | R} srcNode - The `srcNode` parameter in the - * `_swapProperties` method can be either a BTNKeyOrNodeOrEntry object containing key and value + * @param {BTNRep | R} srcNode - The `srcNode` parameter in the + * `_swapProperties` method can be either a BTNRep object containing key and value * properties, or it can be of type R. - * @param {BTNKeyOrNodeOrEntry | R} destNode - The `destNode` parameter in the + * @param {BTNRep | R} destNode - The `destNode` parameter in the * `_swapProperties` method represents the node or entry where the properties will be swapped with - * the `srcNode`. It can be of type `BTNKeyOrNodeOrEntry` or `R`. The method ensures that + * the `srcNode`. It can be of type `BTNRep` or `R`. The method ensures that * both `srcNode * @returns The `_swapProperties` method returns either the `destNode` with its key and value swapped * with the `srcNode`, or `undefined` if either `srcNode` or `destNode` is falsy. */ - protected _swapProperties( - srcNode: BTNKeyOrNodeOrEntry | R, - destNode: BTNKeyOrNodeOrEntry | R - ): NODE | undefined { + protected _swapProperties(srcNode: BTNRep | R, destNode: BTNRep | R): NODE | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -2016,10 +2029,10 @@ export class BinaryTree< if (tempNode) { destNode.key = srcNode.key; - destNode.value = srcNode.value; + if (!this._isMapMode) destNode.value = srcNode.value; srcNode.key = tempNode.key; - srcNode.value = tempNode.value; + if (!this._isMapMode) srcNode.value = tempNode.value; } return destNode; @@ -2065,10 +2078,10 @@ export class BinaryTree< * * The function _setRoot sets the root node of a data structure while updating the parent reference * of the previous root node. - * @param v - The parameter `v` in the `_setRoot` method is of type `OptBTNOrNull`, which means + * @param v - The parameter `v` in the `_setRoot` method is of type `OptNodeOrNull`, which means * it can either be an optional `NODE` type or `null`. */ - protected _setRoot(v: OptBTNOrNull) { + protected _setRoot(v: OptNodeOrNull) { if (v) { v.parent = undefined; } @@ -2081,49 +2094,116 @@ export class BinaryTree< * * The function `_ensurePredicate` in TypeScript ensures that the input is converted into a valid * predicate function for a binary tree node. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} keyOrEntryOrRawOrPredicate - The + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate - The * `_ensurePredicate` method in the provided code snippet is responsible for ensuring that the input - * parameter `keyOrEntryOrRawOrPredicate` is transformed into a valid predicate function that can be + * parameter `keyNodeEntryRawOrPredicate` is transformed into a valid predicate function that can be * used for filtering nodes in a binary tree. - * @returns A BTNPredicate function is being returned. + * @returns A NodePredicate function is being returned. */ protected _ensurePredicate( - keyOrEntryOrRawOrPredicate: BTNKeyOrNodeOrEntry | R | BTNPredicate - ): BTNPredicate { - if (keyOrEntryOrRawOrPredicate === null || keyOrEntryOrRawOrPredicate === undefined) + keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate + ): NodePredicate { + if (keyNodeEntryRawOrPredicate === null || keyNodeEntryRawOrPredicate === undefined) return (node: NODE) => (node ? false : false); - if (this._isPredicated(keyOrEntryOrRawOrPredicate)) return keyOrEntryOrRawOrPredicate; + if (this._isPredicate(keyNodeEntryRawOrPredicate)) return keyNodeEntryRawOrPredicate; - if (this.isRealNode(keyOrEntryOrRawOrPredicate)) return (node: NODE) => node === keyOrEntryOrRawOrPredicate; + if (this.isRealNode(keyNodeEntryRawOrPredicate)) return (node: NODE) => node === keyNodeEntryRawOrPredicate; - if (this.isEntry(keyOrEntryOrRawOrPredicate)) { - const [key] = keyOrEntryOrRawOrPredicate; + if (this.isEntry(keyNodeEntryRawOrPredicate)) { + const [key] = keyNodeEntryRawOrPredicate; return (node: NODE) => node.key === key; } - if (this.isKey(keyOrEntryOrRawOrPredicate)) return (node: NODE) => node.key === keyOrEntryOrRawOrPredicate; + if (this.isKey(keyNodeEntryRawOrPredicate)) return (node: NODE) => node.key === keyNodeEntryRawOrPredicate; if (this._toEntryFn) { - const [key] = this._toEntryFn(keyOrEntryOrRawOrPredicate); + const [key] = this._toEntryFn(keyNodeEntryRawOrPredicate); return (node: NODE) => node.key === key; } - return (node: NODE) => node.key === keyOrEntryOrRawOrPredicate; + return (node: NODE) => node.key === keyNodeEntryRawOrPredicate; } /** * Time Complexity: O(1) * Space Complexity: O(1) * - * The function `_isPredicated` checks if a given parameter is a function. + * The function `_isPredicate` checks if a given parameter is a function. * @param {any} p - The parameter `p` is a variable of type `any`, which means it can hold any type - * of value. In this context, the function `_isPredicated` is checking if `p` is a function that - * satisfies the type `BTNPredicate`. + * of value. In this context, the function `_isPredicate` is checking if `p` is a function that + * satisfies the type `NodePredicate`. * @returns The function is checking if the input `p` is a function and returning a boolean value * based on that check. If `p` is a function, it will return `true`, indicating that `p` is a * predicate function for a binary tree node. If `p` is not a function, it will return `false`. */ - protected _isPredicated(p: any): p is BTNPredicate { + protected _isPredicate(p: any): p is NodePredicate { return typeof p === 'function'; } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function `_getKey` in TypeScript returns the key from a given input, which can be a node, + * entry, raw data, or null/undefined. + * @param {BTNRep | R} keyNodeEntryOrRaw - The `_getKey` method you provided is a + * TypeScript method that takes in a parameter `keyNodeEntryOrRaw` of type `BTNRep | R`, + * where `BTNRep` is a generic type with keys `K`, `V`, and `NODE`, and ` + * @returns The `_getKey` method returns the key value extracted from the `keyNodeEntryOrRaw` + * parameter. The return value can be a key value of type `K`, `null`, or `undefined`, depending on + * the conditions checked in the method. + */ + protected _getKey(keyNodeEntryOrRaw: BTNRep | R): K | null | undefined { + if (keyNodeEntryOrRaw === null) return null; + if (keyNodeEntryOrRaw === undefined) return; + if (keyNodeEntryOrRaw === this._NIL) return; + if (this.isNode(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw.key; + + if (this.isEntry(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw[0]; + + if (this.isRaw(keyNodeEntryOrRaw)) { + if (this._toEntryFn) { + const [key] = this._toEntryFn(keyNodeEntryOrRaw); + return key; + } + return; + } + + return keyNodeEntryOrRaw; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function `_setValue` sets a value in a store based on a key, handling cases where the key or + * value is null or undefined. + * @param {K | null | undefined} key - The `key` parameter can be of type `K`, `null`, or + * `undefined`. + * @param {V | undefined} value - The `value` parameter in the `_setValue` method can be of type `V` + * or `undefined`. + * @returns The method `_setValue` returns `false` if either the `key` is `null` or `undefined`, or + * if the `value` is `undefined`. Otherwise, it returns the result of calling the `set` method on the + * `_store` object with the `key` and `value` arguments. + */ + protected _setValue(key: K | null | undefined, value: V | undefined) { + if (key === null || key === undefined) return false; + if (value === undefined) return false; + return this._store.set(key, value); + } + + /** + * The _clearNodes function sets the root node to undefined and resets the size to 0. + */ + protected _clearNodes() { + this._setRoot(undefined); + this._size = 0; + } + + /** + * The _clearValues function clears all values stored in the _store object. + */ + protected _clearValues() { + this._store.clear(); + } } diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 51ea753..de70378 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -7,18 +7,17 @@ */ import type { BSTNested, - BSTNKeyOrNode, BSTNodeNested, + BSTNOptKeyOrNode, BSTOptions, - BTNCallback, - BTNEntry, - BTNKeyOrNodeOrEntry, - BTNPredicate, + BTNRep, Comparator, CP, DFSOrderPattern, IterationType, - OptBSTN + NodeCallback, + NodePredicate, + OptNode } from '../../types'; import { BinaryTree, BinaryTreeNode } from './binary-tree'; import { IBinaryTree } from '../../interfaces'; @@ -45,16 +44,16 @@ export class BSTNode = BSTNod * The function returns the value of the `_left` property. * @returns The `_left` property of the current object is being returned. */ - override get left(): OptBSTN { + override get left(): OptNode { return this._left; } /** * The function sets the left child of a node and updates the parent reference of the child. - * @param {OptBSTN} v - The parameter `v` is of type `OptBSTN`. It can either be an + * @param {OptNode} v - The parameter `v` is of type `OptNode`. It can either be an * instance of the `NODE` class or `undefined`. */ - override set left(v: OptBSTN) { + override set left(v: OptNode) { if (v) { v.parent = this as unknown as NODE; } @@ -68,16 +67,16 @@ export class BSTNode = BSTNod * @returns The method is returning the value of the `_right` property, which is of type `NODE` or * `undefined`. */ - override get right(): OptBSTN { + override get right(): OptNode { return this._right; } /** * The function sets the right child of a node and updates the parent reference of the child. - * @param {OptBSTN} v - The parameter `v` is of type `OptBSTN`. It can either be a + * @param {OptNode} v - The parameter `v` is of type `OptNode`. It can either be a * `NODE` object or `undefined`. */ - override set right(v: OptBSTN) { + override set right(v: OptNode) { if (v) { v.parent = this as unknown as NODE; } @@ -97,7 +96,7 @@ export class BSTNode = BSTNod export class BST< K = any, V = any, - R = BTNEntry, + R = object, NODE extends BSTNode = BSTNode>, TREE extends BST = BST> > @@ -106,16 +105,13 @@ export class BST< { /** * This is the constructor function for a Binary Search Tree class in TypeScript. - * @param keysOrNodesOrEntriesOrRaws - The `keysOrNodesOrEntriesOrRaws` parameter is an + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an * iterable that can contain either keys, nodes, entries, or raw elements. These elements will be * added to the binary search tree during the construction of the object. * @param [options] - An optional object that contains additional options for the Binary Search Tree. * It can include a comparator function that defines the order of the elements in the tree. */ - constructor( - keysOrNodesOrEntriesOrRaws: Iterable> = [], - options?: BSTOptions - ) { + constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: BSTOptions) { super([], options); if (options) { @@ -123,7 +119,7 @@ export class BST< if (comparator) this._comparator = comparator; } - if (keysOrNodesOrEntriesOrRaws) this.addMany(keysOrNodesOrEntriesOrRaws); + if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } protected override _root?: NODE = undefined; @@ -132,7 +128,7 @@ export class BST< * The function returns the root node of a tree structure. * @returns The `_root` property of the object, which is of type `NODE` or `undefined`. */ - override get root(): OptBSTN { + override get root(): OptNode { return this._root; } @@ -158,6 +154,7 @@ export class BST< override createTree(options?: BSTOptions): TREE { return new BST([], { iterationType: this.iterationType, + isMapMode: this._isMapMode, comparator: this._comparator, toEntryFn: this._toEntryFn, ...options @@ -166,18 +163,20 @@ export class BST< /** * The function overrides a method and converts a key, value pair or entry or raw element to a node. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - A variable that can be of - * type R or BTNKeyOrNodeOrEntry. It represents either a key, a node, an entry, or a raw + * @param {BTNRep | R} keyNodeEntryOrRaw - A variable that can be of + * type R or BTNRep. It represents either a key, a node, an entry, or a raw * element. * @param {V} [value] - The `value` parameter is an optional value of type `V`. It represents the * value associated with a key in a key-value pair. * @returns either a NODE object or undefined. */ - override keyValueOrEntryOrRawElementToNode( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, + override keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep | R, value?: V - ): OptBSTN { - return super.keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw, value) ?? undefined; + ): [OptNode, V | undefined] { + const [node, tValue] = super.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + if (node === null) return [undefined, undefined]; + return [node, tValue ?? value]; } /** @@ -186,8 +185,8 @@ export class BST< * * The function ensures the existence of a node in a data structure and returns it, or undefined if * it doesn't exist. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can accept a value of type `R`, which represents the key, node, + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can accept a value of type `R`, which represents the key, node, * entry, or raw element that needs to be ensured in the tree. * @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter is an optional * parameter that specifies the type of iteration to be used when ensuring a node. It has a default @@ -196,21 +195,21 @@ export class BST< * not be ensured. */ override ensureNode( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, + keyNodeEntryOrRaw: BTNRep | R, iterationType: IterationType = this.iterationType - ): OptBSTN { - return super.ensureNode(keyOrNodeOrEntryOrRaw, iterationType) ?? undefined; + ): OptNode { + return super.ensureNode(keyNodeEntryOrRaw, iterationType) ?? undefined; } /** * The function checks if the input is an instance of the BSTNode class. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `R` or `BTNKeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the input parameter `keyOrNodeOrEntryOrRaw` is + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is * an instance of the `BSTNode` class. */ - override isNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - return keyOrNodeOrEntryOrRaw instanceof BSTNode; + override isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + return keyNodeEntryOrRaw instanceof BSTNode; } /** @@ -230,18 +229,19 @@ export class BST< * Space Complexity: O(1) * * The `add` function in TypeScript adds a new node to a binary search tree based on the key value. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can accept a value of type `R` or `BTNKeyOrNodeOrEntry`. + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep`. * @param {V} [value] - The `value` parameter is an optional value that can be associated with the * key in the binary search tree. If provided, it will be stored in the node along with the key. * @returns a boolean value. */ - override add(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V): boolean { - const newNode = this.keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw, value); + override add(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); if (newNode === undefined) return false; if (this._root === undefined) { this._setRoot(newNode); + if (this._isMapMode) this._setValue(newNode?.key, newValue); this._size++; return true; } @@ -254,6 +254,7 @@ export class BST< } else if (this.comparator(current.key, newNode.key) > 0) { if (current.left === undefined) { current.left = newNode; + if (this._isMapMode) this._setValue(newNode?.key, newValue); this._size++; return true; } @@ -261,6 +262,7 @@ export class BST< } else { if (current.right === undefined) { current.right = newNode; + if (this._isMapMode) this._setValue(newNode?.key, newValue); this._size++; return true; } @@ -277,7 +279,7 @@ export class BST< * * The `addMany` function in TypeScript adds multiple keys or nodes to a data structure and returns * an array indicating whether each key or node was successfully inserted. - * @param keysOrNodesOrEntriesOrRaws - An iterable containing keys, nodes, entries, or raw + * @param keysNodesEntriesOrRaws - An iterable containing keys, nodes, entries, or raw * elements to be added to the data structure. * @param [values] - An optional iterable of values to be associated with the keys or nodes being * added. If provided, the values will be assigned to the corresponding keys or nodes in the same @@ -293,7 +295,7 @@ export class BST< * successfully inserted into the data structure. */ override addMany( - keysOrNodesOrEntriesOrRaws: Iterable>, + keysNodesEntriesOrRaws: Iterable>, values?: Iterable, isBalanceAdd = true, iterationType: IterationType = this.iterationType @@ -307,7 +309,7 @@ export class BST< } if (!isBalanceAdd) { - for (const kve of keysOrNodesOrEntriesOrRaws) { + for (const kve of keysNodesEntriesOrRaws) { const value = valuesIterator?.next().value; inserted.push(this.add(kve, value)); } @@ -315,18 +317,18 @@ export class BST< } const realBTNExemplars: { - key: R | BTNKeyOrNodeOrEntry; + key: R | BTNRep; value: V | undefined; orgIndex: number; }[] = []; let i = 0; - for (const kve of keysOrNodesOrEntriesOrRaws) { + for (const kve of keysNodesEntriesOrRaws) { realBTNExemplars.push({ key: kve, value: valuesIterator?.next().value, orgIndex: i }); i++; } - let sorted: { key: R | BTNKeyOrNodeOrEntry; value: V | undefined; orgIndex: number }[] = []; + let sorted: { key: R | BTNRep; value: V | undefined; orgIndex: number }[] = []; sorted = realBTNExemplars.sort(({ key: a }, { key: b }) => { let keyA: K | undefined | null, keyB: K | undefined | null; @@ -352,7 +354,7 @@ export class BST< return 0; }); - const _dfs = (arr: { key: R | BTNKeyOrNodeOrEntry; value: V | undefined; orgIndex: number }[]) => { + const _dfs = (arr: { key: R | BTNRep; value: V | undefined; orgIndex: number }[]) => { if (arr.length === 0) return; const mid = Math.floor((arr.length - 1) / 2); @@ -394,35 +396,35 @@ export class BST< * Space Complexity: O(k + log n) * * The function `getNodes` in TypeScript overrides the base class method to retrieve nodes based on a - * given predicate and iteration type. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} predicate - The `predicate` + * given keyNodeEntryRawOrPredicate and iteration type. + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate - The `keyNodeEntryRawOrPredicate` * parameter in the `getNodes` method is used to filter the nodes that will be returned. It can be a - * key, a node, an entry, or a custom predicate function that determines whether a node should be + * key, a node, an entry, or a custom keyNodeEntryRawOrPredicate function that determines whether a node should be * included in the result. * @param [onlyOne=false] - The `onlyOne` parameter in the `getNodes` method is a boolean flag that - * determines whether to return only the first node that matches the predicate (`true`) or all nodes - * that match the predicate (`false`). If `onlyOne` is set to `true`, the method will stop iterating + * determines whether to return only the first node that matches the keyNodeEntryRawOrPredicate (`true`) or all nodes + * that match the keyNodeEntryRawOrPredicate (`false`). If `onlyOne` is set to `true`, the method will stop iterating * and - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter in the + * @param {BTNRep | R} startNode - The `startNode` parameter in the * `getNodes` method is used to specify the starting point for traversing the tree when searching for - * nodes that match a given predicate. It represents the root node of the subtree where the search + * nodes that match a given keyNodeEntryRawOrPredicate. It represents the root node of the subtree where the search * should begin. If not explicitly provided, the default value for `begin * @param {IterationType} iterationType - The `iterationType` parameter in the `getNodes` method * specifies the type of iteration to be performed when traversing the nodes of a binary tree. It can * have two possible values: - * @returns The `getNodes` method returns an array of nodes that satisfy the given predicate. + * @returns The `getNodes` method returns an array of nodes that satisfy the given keyNodeEntryRawOrPredicate. */ override getNodes( - predicate: BTNKeyOrNodeOrEntry | R | BTNPredicate, + keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, onlyOne = false, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): NODE[] { - if (predicate === undefined) return []; - if (predicate === null) return []; - beginRoot = this.ensureNode(beginRoot); - if (!beginRoot) return []; - const callback = this._ensurePredicate(predicate); + if (keyNodeEntryRawOrPredicate === undefined) return []; + if (keyNodeEntryRawOrPredicate === null) return []; + startNode = this.ensureNode(startNode); + if (!startNode) return []; + const callback = this._ensurePredicate(keyNodeEntryRawOrPredicate); const ans: NODE[] = []; if (iterationType === 'RECURSIVE') { @@ -433,27 +435,53 @@ export class BST< } if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return; - if (this.isKey(predicate)) { - if (this.isRealNode(cur.left) && this.comparator(cur.key, predicate) > 0) dfs(cur.left); - if (this.isRealNode(cur.right) && this.comparator(cur.key, predicate) < 0) dfs(cur.right); + if (!this._isPredicate(keyNodeEntryRawOrPredicate)) { + const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate); + if ( + this.isRealNode(cur.left) && + benchmarkKey !== null && + benchmarkKey !== undefined && + this.comparator(cur.key, benchmarkKey) > 0 + ) + dfs(cur.left); + if ( + this.isRealNode(cur.right) && + benchmarkKey !== null && + benchmarkKey !== undefined && + this.comparator(cur.key, benchmarkKey) < 0 + ) + dfs(cur.right); } else { if (this.isRealNode(cur.left)) dfs(cur.left); if (this.isRealNode(cur.right)) dfs(cur.right); } }; - dfs(beginRoot); + dfs(startNode); } else { - const stack = [beginRoot]; + const stack = [startNode]; while (stack.length > 0) { const cur = stack.pop()!; if (callback(cur)) { ans.push(cur); if (onlyOne) return ans; } - if (this.isKey(predicate)) { - if (this.isRealNode(cur.right) && this.comparator(cur.key, predicate) < 0) stack.push(cur.right); - if (this.isRealNode(cur.left) && this.comparator(cur.key, predicate) > 0) stack.push(cur.left); + if (!this._isPredicate(keyNodeEntryRawOrPredicate)) { + const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate); + if ( + this.isRealNode(cur.right) && + benchmarkKey !== null && + benchmarkKey !== undefined && + this.comparator(cur.key, benchmarkKey) < 0 + ) + stack.push(cur.right); + if ( + this.isRealNode(cur.left) && + benchmarkKey !== null && + benchmarkKey !== undefined && + this.comparator(cur.key, benchmarkKey) > 0 + ) + stack.push(cur.left); } else { if (this.isRealNode(cur.right)) stack.push(cur.right); if (this.isRealNode(cur.left)) stack.push(cur.left); @@ -468,10 +496,10 @@ export class BST< * Time Complexity: O(log n) * Space Complexity: O(1) * - * This function retrieves a node based on a given predicate within a binary search tree structure. - * @param {BTNKeyOrNodeOrEntry | R | BTNPredicate} predicate - The `predicate` - * parameter can be of type `BTNKeyOrNodeOrEntry`, `R`, or `BTNPredicate`. - * @param {R | BSTNKeyOrNode} beginRoot - The `beginRoot` parameter in the `getNode` method + * This function retrieves a node based on a given keyNodeEntryRawOrPredicate within a binary search tree structure. + * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate - The `keyNodeEntryRawOrPredicate` + * parameter can be of type `BTNRep`, `R`, or `NodePredicate`. + * @param {R | BSTNOptKeyOrNode} startNode - The `startNode` parameter in the `getNode` method * is used to specify the starting point for searching nodes in the binary search tree. If no * specific starting point is provided, the default value is set to `this._root`, which is the root * node of the binary search tree. @@ -479,17 +507,17 @@ export class BST< * parameter that specifies the type of iteration to be used. It has a default value of * `this.iterationType`, which means it will use the iteration type defined in the class instance if * no value is provided when calling the method. - * @returns The `getNode` method is returning an optional binary search tree node (`OptBSTN`). - * It is using the `getNodes` method to find the node based on the provided predicate, beginning at - * the specified root node (`beginRoot`) and using the specified iteration type. The method then + * @returns The `getNode` method is returning an optional binary search tree node (`OptNode`). + * It is using the `getNodes` method to find the node based on the provided keyNodeEntryRawOrPredicate, beginning at + * the specified root node (`startNode`) and using the specified iteration type. The method then * returns the first node found or `undefined` if no node is found. */ override getNode( - predicate: BTNKeyOrNodeOrEntry | R | BTNPredicate, - beginRoot: R | BSTNKeyOrNode = this._root, + keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, + startNode: R | BSTNOptKeyOrNode = this._root, iterationType: IterationType = this.iterationType - ): OptBSTN { - return this.getNodes(predicate, true, beginRoot, iterationType)[0] ?? undefined; + ): OptNode { + return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined; } /** @@ -505,7 +533,7 @@ export class BST< * It has a default value of `'ITERATIVE'`. * @returns The method is returning a NODE object or undefined. */ - override getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptBSTN { + override getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNode { return this.getNode(key, this._root, iterationType); } @@ -517,11 +545,11 @@ export class BST< * the callback function. * @param {C} callback - The `callback` parameter is a function that will be called for each node * during the depth-first search traversal. It is an optional parameter and defaults to - * `this._DEFAULT_BTN_CALLBACK`. The type `C` represents the type of the callback function. + * `this._DEFAULT_NODE_CALLBACK`. The type `C` represents the type of the callback function. * @param {DFSOrderPattern} [pattern=IN] - The "pattern" parameter in the code snippet refers to the * order in which the Depth-First Search (DFS) algorithm visits the nodes in a tree or graph. It can * take one of the following values: - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter is the starting + * @param {BTNRep | R} startNode - The `startNode` parameter is the starting * point for the depth-first search traversal. It can be either a root node, a key-value pair, or a * node entry. If not specified, the default value is the root of the tree. * @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter specifies the @@ -529,13 +557,13 @@ export class BST< * following values: * @returns The method is returning an array of the return type of the callback function. */ - override dfs>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, + override dfs>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { - return super.dfs(callback, pattern, beginRoot, iterationType); + return super.dfs(callback, pattern, startNode, iterationType); } /** @@ -547,7 +575,7 @@ export class BST< * @param {C} callback - The `callback` parameter is a function that will be called for each node * visited during the breadth-first search. It should take a single argument, which is the current * node being visited, and it can return a value of any type. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter is the starting + * @param {BTNRep | R} startNode - The `startNode` parameter is the starting * point for the breadth-first search. It can be either a root node, a key-value pair, or an entry * object. If no value is provided, the default value is the root of the tree. * @param {IterationType} iterationType - The `iterationType` parameter is used to specify the type @@ -555,12 +583,12 @@ export class BST< * the following values: * @returns an array of the return type of the callback function. */ - override bfs>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + override bfs>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { - return super.bfs(callback, beginRoot, iterationType, false); + return super.bfs(callback, startNode, iterationType, false); } /** @@ -570,9 +598,9 @@ export class BST< * The function overrides the listLevels method from the superclass and returns an array of arrays * containing the results of the callback function applied to each level of the tree. * @param {C} callback - The `callback` parameter is a generic type `C` that extends - * `BTNCallback`. It represents a callback function that will be called for each node in the + * `NodeCallback`. It represents a callback function that will be called for each node in the * tree during the iteration process. - * @param {BTNKeyOrNodeOrEntry | R} beginRoot - The `beginRoot` parameter is the starting + * @param {BTNRep | R} startNode - The `startNode` parameter is the starting * point for listing the levels of the binary tree. It can be either a root node of the tree, a * key-value pair representing a node in the tree, or a key representing a node in the tree. If no * value is provided, the root of @@ -581,12 +609,12 @@ export class BST< * @returns The method is returning a two-dimensional array of the return type of the callback * function. */ - override listLevels>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, - beginRoot: BTNKeyOrNodeOrEntry | R = this._root, + override listLevels>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, + startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[][] { - return super.listLevels(callback, beginRoot, iterationType, false); + return super.listLevels(callback, startNode, iterationType, false); } /** @@ -601,7 +629,7 @@ export class BST< * @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to * traverse nodes that are lesser, greater, or both than the `targetNode`. It accepts the values -1, * 0, or 1, where: - * @param {BTNKeyOrNodeOrEntry | R} targetNode - The `targetNode` parameter is the node in + * @param {BTNRep | R} targetNode - The `targetNode` parameter is the node in * the binary tree that you want to start traversing from. It can be specified either by providing * the key of the node, the node itself, or an entry containing the key and value of the node. If no * `targetNode` is provided, @@ -610,14 +638,14 @@ export class BST< * @returns The function `lesserOrGreaterTraverse` returns an array of values of type * `ReturnType`, which is the return type of the callback function passed as an argument. */ - lesserOrGreaterTraverse>( - callback: C = this._DEFAULT_BTN_CALLBACK as C, + lesserOrGreaterTraverse>( + callback: C = this._DEFAULT_NODE_CALLBACK as C, lesserOrGreater: CP = -1, - targetNode: BTNKeyOrNodeOrEntry | R = this._root, + targetNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { const targetNodeEnsured = this.ensureNode(targetNode); - const ans: ReturnType>[] = []; + const ans: ReturnType>[] = []; if (!this._root) return ans; if (!targetNodeEnsured) return ans; @@ -665,7 +693,7 @@ export class BST< perfectlyBalance(iterationType: IterationType = this.iterationType): boolean { const sorted = this.dfs(node => node, 'IN'), n = sorted.length; - this.clear(); + this._clearNodes(); if (sorted.length < 1) return false; if (iterationType === 'RECURSIVE') { @@ -673,7 +701,8 @@ export class BST< if (l > r) return; const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add([midNode.key, midNode.value]); + if (this._isMapMode) this.add(midNode.key); + else this.add([midNode.key, midNode.value]); buildBalanceBST(l, m - 1); buildBalanceBST(m + 1, r); }; @@ -689,7 +718,8 @@ export class BST< if (l <= r) { const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add([midNode.key, midNode.value]); + if (this._isMapMode) this.add(midNode.key); + else this.add([midNode.key, midNode.value]); stack.push([m + 1, r]); stack.push([l, m - 1]); } @@ -717,7 +747,7 @@ export class BST< let balanced = true; if (iterationType === 'RECURSIVE') { - const _height = (cur: OptBSTN): number => { + const _height = (cur: OptNode): number => { if (!cur) return 0; const leftHeight = _height(cur.left), rightHeight = _height(cur.right); @@ -727,8 +757,8 @@ export class BST< _height(this._root); } else { const stack: NODE[] = []; - let node: OptBSTN = this._root, - last: OptBSTN = undefined; + let node: OptNode = this._root, + last: OptNode = undefined; const depths: Map = new Map(); while (stack.length > 0 || node) { @@ -779,9 +809,9 @@ export class BST< /** * The function sets the root of a tree-like structure and updates the parent property of the new * root. - * @param {OptBSTN} v - v is a parameter of type NODE or undefined. + * @param {OptNode} v - v is a parameter of type NODE or undefined. */ - protected override _setRoot(v: OptBSTN) { + protected override _setRoot(v: OptNode) { if (v) { v.parent = undefined; } diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index e0eeb95..9f52c17 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,13 +1,12 @@ import type { BinaryTreeDeleteResult, - BTNKeyOrNodeOrEntry, + BTNRep, CRUD, - OptBSTN, + OptNode, RBTNColor, RBTreeOptions, RedBlackTreeNested, - RedBlackTreeNodeNested, - BTNEntry + RedBlackTreeNodeNested } from '../../types'; import { BST, BSTNode } from './bst'; import { IBinaryTree } from '../../interfaces'; @@ -55,7 +54,7 @@ export class RedBlackTreeNode< export class RedBlackTree< K = any, V = any, - R = BTNEntry, + R = object, NODE extends RedBlackTreeNode = RedBlackTreeNode>, TREE extends RedBlackTree = RedBlackTree> > @@ -64,7 +63,7 @@ export class RedBlackTree< { /** * This is the constructor function for a Red-Black Tree data structure in TypeScript. - * @param keysOrNodesOrEntriesOrRaws - The `keysOrNodesOrEntriesOrRaws` parameter is an + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an * iterable object that can contain either keys, nodes, entries, or raw elements. It is used to * initialize the RBTree with the provided elements. * @param [options] - The `options` parameter is an optional object that can be passed to the @@ -72,16 +71,13 @@ export class RedBlackTree< * configuring the behavior of the Red-Black Tree. The specific properties and their meanings would * depend on the implementation */ - constructor( - keysOrNodesOrEntriesOrRaws: Iterable> = [], - options?: RBTreeOptions - ) { + constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: RBTreeOptions) { super([], options); this._root = this.NIL; - if (keysOrNodesOrEntriesOrRaws) { - this.addMany(keysOrNodesOrEntriesOrRaws); + if (keysNodesEntriesOrRaws) { + this.addMany(keysNodesEntriesOrRaws); } } @@ -122,6 +118,7 @@ export class RedBlackTree< override createTree(options?: RBTreeOptions): TREE { return new RedBlackTree([], { iterationType: this.iterationType, + isMapMode: this._isMapMode, comparator: this._comparator, toEntryFn: this._toEntryFn, ...options @@ -133,13 +130,13 @@ export class RedBlackTree< * Space Complexity: O(1) * * The function checks if the input is an instance of the RedBlackTreeNode class. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `R` or `BTNKeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the input parameter `keyOrNodeOrEntryOrRaw` is + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is * an instance of the `RedBlackTreeNode` class. */ - override isNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - return keyOrNodeOrEntryOrRaw instanceof RedBlackTreeNode; + override isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + return keyNodeEntryOrRaw instanceof RedBlackTreeNode; } // /** @@ -151,29 +148,29 @@ export class RedBlackTree< // * Time Complexity: O(1) // * Space Complexity: O(1) // * - // * The function `keyValueOrEntryOrRawElementToNode` takes a key, value, or entry and returns a node if it is + // * The function `keyValueNodeEntryRawToNodeAndValue` takes a key, value, or entry and returns a node if it is // * valid, otherwise it returns undefined. - // * @param {BTNKeyOrNodeOrEntry} keyOrNodeOrEntryOrRaw - The key, value, or entry to convert. - // * @param {V} [value] - The value associated with the key (if `keyOrNodeOrEntryOrRaw` is a key). + // * @param {BTNRep} keyNodeEntryOrRaw - The key, value, or entry to convert. + // * @param {V} [value] - The value associated with the key (if `keyNodeEntryOrRaw` is a key). // * @returns {NODE | undefined} - The corresponding Red-Black Tree node, or `undefined` if conversion fails. // */ - // override keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V): NODE | undefined { + // override keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw: BTNRep | R, value?: V): NODE | undefined { // - // if (keyOrNodeOrEntryOrRaw === null || keyOrNodeOrEntryOrRaw === undefined) return; - // if (this.isNode(keyOrNodeOrEntryOrRaw)) return keyOrNodeOrEntryOrRaw; + // if (keyNodeEntryOrRaw === null || keyNodeEntryOrRaw === undefined) return; + // if (this.isNode(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw; // // if (this._toEntryFn) { - // const [key, entryValue] = this._toEntryFn(keyOrNodeOrEntryOrRaw as R); - // if (this.isKey(key)) return this.createNode(key, entryValue ?? value, 'RED'); + // const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R); + // if (this.isKey(key)) return this.createNode(key, value ?? entryValue, 'RED'); // } // - // if (this.isEntry(keyOrNodeOrEntryOrRaw)) { - // const [key, value] = keyOrNodeOrEntryOrRaw; + // if (this.isEntry(keyNodeEntryOrRaw)) { + // const [key, value] = keyNodeEntryOrRaw; // if (key === undefined || key === null) return; // else return this.createNode(key, value, 'RED'); // } // - // if (this.isKey(keyOrNodeOrEntryOrRaw)) return this.createNode(keyOrNodeOrEntryOrRaw, value, 'RED'); + // if (this.isKey(keyNodeEntryOrRaw)) return this.createNode(keyNodeEntryOrRaw, value, 'RED'); // // return ; // } @@ -196,8 +193,8 @@ export class RedBlackTree< * * The function adds a new node to a binary search tree and returns true if the node was successfully * added. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can accept a value of type `R` or `BTNKeyOrNodeOrEntry`. + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep`. * @param {V} [value] - The `value` parameter is an optional value that you want to associate with * the key in the data structure. It represents the value that you want to add or update in the data * structure. @@ -205,8 +202,8 @@ export class RedBlackTree< * the method returns true. If the node already exists and its value is updated, the method also * returns true. If the node cannot be added or updated, the method returns false. */ - override add(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V): boolean { - const newNode = this.keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw, value); + override add(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); if (!this.isRealNode(newNode)) return false; const insertStatus = this._insert(newNode); @@ -218,6 +215,7 @@ export class RedBlackTree< } else { return false; } + if (this._isMapMode) this._setValue(newNode.key, newValue); this._size++; return true; } else return insertStatus === 'UPDATED'; @@ -229,7 +227,7 @@ export class RedBlackTree< * * The function overrides the delete method in a binary tree data structure to remove a node based on * a given predicate and maintain the binary search tree properties. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `keyOrNodeOrEntryOrRaw` + * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` * parameter in the `override delete` method is used to specify the condition or key based on which a * node should be deleted from the binary tree. It can be a key, a node, an entry, or a predicate * function that determines which node(s) should be deleted. @@ -237,16 +235,13 @@ export class RedBlackTree< * objects. Each object in the array contains information about the deleted node and whether * balancing is needed. */ - override delete(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): BinaryTreeDeleteResult[] { - if (keyOrNodeOrEntryOrRaw === null) return []; + override delete(keyNodeEntryOrRaw: BTNRep | R): BinaryTreeDeleteResult[] { + if (keyNodeEntryOrRaw === null) return []; const results: BinaryTreeDeleteResult[] = []; - let nodeToDelete: OptBSTN; - if (this._isPredicated(keyOrNodeOrEntryOrRaw)) nodeToDelete = this.getNode(keyOrNodeOrEntryOrRaw); - else - nodeToDelete = this.isRealNode(keyOrNodeOrEntryOrRaw) - ? keyOrNodeOrEntryOrRaw - : this.getNode(keyOrNodeOrEntryOrRaw); + let nodeToDelete: OptNode; + if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw); + else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw); if (!nodeToDelete) { return results; @@ -287,6 +282,7 @@ export class RedBlackTree< successor.color = nodeToDelete.color; } } + if (this._isMapMode) this._store.delete(nodeToDelete.key); this._size--; // If the original color was black, fix the tree diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index fc248bd..59e8488 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -7,15 +7,14 @@ */ import type { BinaryTreeDeleteResult, - BSTNKeyOrNode, - BTNKeyOrNodeOrEntry, + BSTNOptKeyOrNode, + BTNRep, IterationType, - OptBSTN, + OptNode, RBTNColor, TreeMultiMapNested, TreeMultiMapNodeNested, - TreeMultiMapOptions, - BTNEntry + TreeMultiMapOptions } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { RedBlackTree, RedBlackTreeNode } from './rb-tree'; @@ -65,7 +64,7 @@ export class TreeMultiMapNode< export class TreeMultiMap< K = any, V = any, - R = BTNEntry, + R = object, NODE extends TreeMultiMapNode = TreeMultiMapNode>, TREE extends TreeMultiMap = TreeMultiMap> > @@ -74,19 +73,16 @@ export class TreeMultiMap< { /** * The constructor function initializes a TreeMultiMap object with optional initial data. - * @param keysOrNodesOrEntriesOrRaws - The parameter `keysOrNodesOrEntriesOrRaws` is an + * @param keysNodesEntriesOrRaws - The parameter `keysNodesEntriesOrRaws` is an * iterable that can contain keys, nodes, entries, or raw elements. It is used to initialize the * TreeMultiMap with initial data. * @param [options] - The `options` parameter is an optional object that can be used to customize the * behavior of the `TreeMultiMap` constructor. It can include properties such as `compareKeys` and * `compareValues`, which are functions used to compare keys and values respectively. */ - constructor( - keysOrNodesOrEntriesOrRaws: Iterable> = [], - options?: TreeMultiMapOptions - ) { + constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: TreeMultiMapOptions) { super([], options); - if (keysOrNodesOrEntriesOrRaws) this.addMany(keysOrNodesOrEntriesOrRaws); + if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } protected _count = 0; @@ -142,6 +138,7 @@ export class TreeMultiMap< override createTree(options?: TreeMultiMapOptions): TREE { return new TreeMultiMap([], { iterationType: this.iterationType, + isMapMode: this._isMapMode, comparator: this._comparator, toEntryFn: this._toEntryFn, ...options @@ -149,10 +146,10 @@ export class TreeMultiMap< } /** - * The function `keyValueOrEntryOrRawElementToNode` takes in a key, value, and count and returns a + * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a * node based on the input. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `R` or `BTNKeyOrNodeOrEntry`. + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. * @param {V} [value] - The `value` parameter is an optional value that represents the value * associated with the key in the node. It is used when creating a new node or updating the value of * an existing node. @@ -160,40 +157,42 @@ export class TreeMultiMap< * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. * @returns either a NODE object or undefined. */ - override keyValueOrEntryOrRawElementToNode( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, + override keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep | R, value?: V, count = 1 - ): NODE | undefined { - if (keyOrNodeOrEntryOrRaw === undefined || keyOrNodeOrEntryOrRaw === null) return; + ): [NODE | undefined, V | undefined] { + if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; - if (this.isNode(keyOrNodeOrEntryOrRaw)) return keyOrNodeOrEntryOrRaw; + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - if (this.isEntry(keyOrNodeOrEntryOrRaw)) { - const [key, entryValue] = keyOrNodeOrEntryOrRaw; - if (key === undefined || key === null) return; - if (this.isKey(key)) return this.createNode(key, value ?? entryValue, 'BLACK', count); + if (this.isEntry(keyNodeEntryOrRaw)) { + const [key, entryValue] = keyNodeEntryOrRaw; + if (key === undefined || key === null) return [undefined, undefined]; + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; } if (this._toEntryFn) { - const [key, entryValue] = this._toEntryFn(keyOrNodeOrEntryOrRaw as R); - if (this.isKey(key)) return this.createNode(key, value ?? entryValue, 'BLACK', count); + const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; } - if (this.isKey(keyOrNodeOrEntryOrRaw)) return this.createNode(keyOrNodeOrEntryOrRaw, value, 'BLACK', count); + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value]; - return; + return [undefined, undefined]; } /** * The function checks if the input is an instance of the TreeMultiMapNode class. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The parameter - * `keyOrNodeOrEntryOrRaw` can be of type `R` or `BTNKeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the input parameter `keyOrNodeOrEntryOrRaw` is + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is * an instance of the `TreeMultiMapNode` class. */ - override isNode(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R): keyOrNodeOrEntryOrRaw is NODE { - return keyOrNodeOrEntryOrRaw instanceof TreeMultiMapNode; + override isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + return keyNodeEntryOrRaw instanceof TreeMultiMapNode; } /** @@ -202,8 +201,8 @@ export class TreeMultiMap< * * The function overrides the add method of a class and adds a new node to a data structure, updating * the count and returning a boolean indicating success. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The - * `keyOrNodeOrEntryOrRaw` parameter can accept one of the following types: + * @param {BTNRep | R} keyNodeEntryOrRaw - The + * `keyNodeEntryOrRaw` parameter can accept one of the following types: * @param {V} [value] - The `value` parameter represents the value associated with the key in the * data structure. It is an optional parameter, so it can be omitted if not needed. * @param [count=1] - The `count` parameter represents the number of times the key-value pair should @@ -212,10 +211,10 @@ export class TreeMultiMap< * @returns The method is returning a boolean value. It returns true if the addition of the new node * was successful, and false otherwise. */ - override add(keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, value?: V, count = 1): boolean { - const newNode = this.keyValueOrEntryOrRawElementToNode(keyOrNodeOrEntryOrRaw, value, count); + override add(keyNodeEntryOrRaw: BTNRep | R, value?: V, count = 1): boolean { + const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count); const orgCount = newNode?.count || 0; - const isSuccessAdded = super.add(newNode); + const isSuccessAdded = super.add(newNode, newValue); if (isSuccessAdded) { this._count += orgCount; @@ -231,7 +230,7 @@ export class TreeMultiMap< * * The function `delete` in TypeScript overrides the deletion operation in a binary tree data * structure, handling cases where nodes have children and maintaining balance in the tree. - * @param {BTNKeyOrNodeOrEntry | R} keyOrNodeOrEntryOrRaw - The `predicate` + * @param {BTNRep | R} keyNodeEntryOrRaw - The `predicate` * parameter in the `delete` method is used to specify the condition or key based on which a node * should be deleted from the binary tree. It can be a key, a node, or an entry. * @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a @@ -240,20 +239,14 @@ export class TreeMultiMap< * `ignoreCount` is `false * @returns The `override delete` method returns an array of `BinaryTreeDeleteResult` objects. */ - override delete( - keyOrNodeOrEntryOrRaw: BTNKeyOrNodeOrEntry | R, - ignoreCount = false - ): BinaryTreeDeleteResult[] { - if (keyOrNodeOrEntryOrRaw === null) return []; + override delete(keyNodeEntryOrRaw: BTNRep | R, ignoreCount = false): BinaryTreeDeleteResult[] { + if (keyNodeEntryOrRaw === null) return []; const results: BinaryTreeDeleteResult[] = []; - let nodeToDelete: OptBSTN; - if (this._isPredicated(keyOrNodeOrEntryOrRaw)) nodeToDelete = this.getNode(keyOrNodeOrEntryOrRaw); - else - nodeToDelete = this.isRealNode(keyOrNodeOrEntryOrRaw) - ? keyOrNodeOrEntryOrRaw - : this.getNode(keyOrNodeOrEntryOrRaw); + let nodeToDelete: OptNode; + if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw); + else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw); if (!nodeToDelete) { return results; @@ -374,7 +367,8 @@ export class TreeMultiMap< if (l > r) return; const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add(midNode.key, midNode.value, midNode.count); + if (this._isMapMode) this.add(midNode.key, undefined, midNode.count); + else this.add(midNode.key, midNode.value, midNode.count); buildBalanceBST(l, m - 1); buildBalanceBST(m + 1, r); }; @@ -390,7 +384,8 @@ export class TreeMultiMap< if (l <= r) { const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add(midNode.key, midNode.value, midNode.count); + if (this._isMapMode) this.add(midNode.key, undefined, midNode.count); + else this.add(midNode.key, midNode.value, midNode.count); stack.push([m + 1, r]); stack.push([l, m - 1]); } @@ -409,7 +404,8 @@ export class TreeMultiMap< */ override clone(): TREE { const cloned = this.createTree(); - this.bfs(node => cloned.add(node.key, node.value, node.count)); + this.bfs(node => cloned.add(node.key, undefined, node.count)); + if (this._isMapMode) cloned._store = this._store; return cloned; } @@ -419,17 +415,17 @@ export class TreeMultiMap< * * The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes * in a binary search tree. - * @param {R | BSTNKeyOrNode} srcNode - The `srcNode` parameter represents the source node + * @param {R | BSTNOptKeyOrNode} srcNode - The `srcNode` parameter represents the source node * that will be swapped with the `destNode`. It can be either an instance of the `R` class or an - * instance of the `BSTNKeyOrNode` class. - * @param {R | BSTNKeyOrNode} destNode - The `destNode` parameter represents the destination + * instance of the `BSTNOptKeyOrNode` class. + * @param {R | BSTNOptKeyOrNode} destNode - The `destNode` parameter represents the destination * node where the properties will be swapped with the source node. * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. * If either `srcNode` or `destNode` is undefined, it returns undefined. */ protected override _swapProperties( - srcNode: R | BSTNKeyOrNode, - destNode: R | BSTNKeyOrNode + srcNode: R | BSTNOptKeyOrNode, + destNode: R | BSTNOptKeyOrNode ): NODE | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -440,12 +436,12 @@ export class TreeMultiMap< tempNode.color = color; destNode.key = srcNode.key; - destNode.value = srcNode.value; + if (!this._isMapMode) destNode.value = srcNode.value; destNode.count = srcNode.count; destNode.color = srcNode.color; srcNode.key = tempNode.key; - srcNode.value = tempNode.value; + if (!this._isMapMode) srcNode.value = tempNode.value; srcNode.count = tempNode.count; srcNode.color = tempNode.color; } diff --git a/src/data-structures/trie/trie.ts b/src/data-structures/trie/trie.ts index ca4b4f2..96532e5 100644 --- a/src/data-structures/trie/trie.ts +++ b/src/data-structures/trie/trie.ts @@ -266,9 +266,9 @@ export class Trie extends IterableElementBase> { * */ getHeight(): number { - const beginRoot = this.root; + const startNode = this.root; let maxDepth = 0; - if (beginRoot) { + if (startNode) { const bfs = (node: TrieNode, level: number) => { if (level > maxDepth) { maxDepth = level; @@ -280,7 +280,7 @@ export class Trie extends IterableElementBase> { } } }; - bfs(beginRoot, 0); + bfs(startNode, 0); } return maxDepth; } diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index 0f39032..7c9678e 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -4,14 +4,14 @@ import type { BinaryTreeNested, BinaryTreeNodeNested, BinaryTreeOptions, - BTNKeyOrNodeOrEntry, - BTNPredicate + BTNRep, + NodePredicate } from '../types'; export interface IBinaryTree< K = any, V = any, - R = [K, V], + R = object, NODE extends BinaryTreeNode = BinaryTreeNodeNested, TREE extends BinaryTree = BinaryTreeNested > { @@ -19,9 +19,9 @@ export interface IBinaryTree< createTree(options?: Partial>): TREE; - add(keyOrNodeOrEntryOrRawElement: BTNKeyOrNodeOrEntry, value?: V, count?: number): boolean; + add(keyOrNodeOrEntryOrRawElement: BTNRep, value?: V, count?: number): boolean; - addMany(nodes: Iterable>, values?: Iterable): boolean[]; + addMany(nodes: Iterable>, values?: Iterable): boolean[]; - delete(predicate: R | BTNKeyOrNodeOrEntry | BTNPredicate): BinaryTreeDeleteResult[]; + delete(predicate: R | BTNRep | NodePredicate): BinaryTreeDeleteResult[]; } diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index 702a054..0139825 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -6,31 +6,30 @@ export type BinaryTreeNodeNested = BinaryTreeNode> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type ToEntryFn = (rawElement: R) => BTNEntry; + export type BinaryTreeOptions = { - iterationType?: IterationType; - toEntryFn?: (rawElement: R) => BTNEntry; + iterationType?: IterationType; + toEntryFn?: ToEntryFn; + isMapMode?: boolean; } export type BinaryTreePrintOptions = { isShowUndefined?: boolean; isShowNull?: boolean; isShowRedBlackNIL?: boolean }; -export type OptBTNOrNull = NODE | null | undefined; +export type OptNodeOrNull = NODE | null | undefined; -export type OptBTNKeyOrNull = K | null | undefined; +export type BTNOptKeyOrNull = K | null | undefined; -export type BTNEntry = [OptBTNKeyOrNull, OptValue]; +export type BTNEntry = [BTNOptKeyOrNull, OptValue]; -export type BTNKeyOrNode = OptBTNKeyOrNull | NODE; +export type BTNOptKeyNodeOrNull = BTNOptKeyOrNull | NODE; -export type BTNKeyOrNodeOrEntry = BTNEntry | BTNKeyOrNode; +export type BTNRep = BTNEntry | BTNOptKeyNodeOrNull; -export type BTNPureKeyOrNode = K | NODE; +export type BinaryTreeDeleteResult = { deleted: OptNodeOrNull; needBalanced: OptNodeOrNull }; -export type BTNPureKeyOrNodeOrEntry = [K, OptValue] | BTNPureKeyOrNode; +export type NodeCallback = (node: NODE) => D; -export type BinaryTreeDeleteResult = { deleted: OptBTNOrNull; needBalanced: OptBTNOrNull }; +export type NodePredicate = (node: NODE) => boolean; -export type BTNCallback = (node: NODE) => D; - -export type BTNPredicate = (node: NODE) => boolean; - -export type DFSStackItem = { opt: DFSOperation; node: OptBTNOrNull } +export type DFSStackItem = { opt: DFSOperation; node: OptNodeOrNull } diff --git a/src/types/data-structures/binary-tree/bst.ts b/src/types/data-structures/binary-tree/bst.ts index 20d0d81..a541273 100644 --- a/src/types/data-structures/binary-tree/bst.ts +++ b/src/types/data-structures/binary-tree/bst.ts @@ -7,12 +7,12 @@ export type BSTNodeNested = BSTNode> = BST>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BSTOptions = BinaryTreeOptions & { - comparator?: Comparator + comparator?: Comparator } -export type OptBSTNKey = K | undefined; +export type BSTNOptKey = K | undefined; -export type OptBSTN = NODE | undefined; +export type OptNode = NODE | undefined; -export type BSTNKeyOrNode = OptBSTNKey | NODE; +export type BSTNOptKeyOrNode = BSTNOptKey | NODE; diff --git a/test/performance/data-structures/binary-tree/avl-tree.test.ts b/test/performance/data-structures/binary-tree/avl-tree.test.ts index 1fb736f..23c83e2 100644 --- a/test/performance/data-structures/binary-tree/avl-tree.test.ts +++ b/test/performance/data-structures/binary-tree/avl-tree.test.ts @@ -3,7 +3,7 @@ import * as Benchmark from 'benchmark'; import { getRandomIntArray, magnitude } from '../../../utils'; const suite = new Benchmark.Suite(); -const avlTree = new AVLTree(); +const avlTree = new AVLTree([], { isMapMode: true }); const { HUNDRED_THOUSAND } = magnitude; const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); diff --git a/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts b/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts index 2bc584d..0461fa6 100644 --- a/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +++ b/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts @@ -3,8 +3,8 @@ import * as Benchmark from 'benchmark'; import { getRandomIntArray, magnitude } from '../../../utils'; const suite = new Benchmark.Suite(); -const rbTree = new RedBlackTree(); -const avlTree = new AVLTree(); +const rbTree = new RedBlackTree([], { isMapMode: true }); +const avlTree = new AVLTree([], { isMapMode: true }); const { TEN_THOUSAND } = magnitude; const arr = getRandomIntArray(TEN_THOUSAND, 0, TEN_THOUSAND - 1, true); diff --git a/test/performance/data-structures/binary-tree/binary-tree.test.ts b/test/performance/data-structures/binary-tree/binary-tree.test.ts index f35a081..3b1359b 100644 --- a/test/performance/data-structures/binary-tree/binary-tree.test.ts +++ b/test/performance/data-structures/binary-tree/binary-tree.test.ts @@ -3,7 +3,7 @@ import * as Benchmark from 'benchmark'; import { getRandomIntArray, magnitude } from '../../../utils'; const suite = new Benchmark.Suite(); -const biTree = new BinaryTree(); +const biTree = new BinaryTree([], { isMapMode: true }); const { THOUSAND } = magnitude; const arr = getRandomIntArray(THOUSAND, 0, THOUSAND, true); diff --git a/test/performance/data-structures/binary-tree/bst.test.ts b/test/performance/data-structures/binary-tree/bst.test.ts index a00db81..b485603 100644 --- a/test/performance/data-structures/binary-tree/bst.test.ts +++ b/test/performance/data-structures/binary-tree/bst.test.ts @@ -3,7 +3,7 @@ import * as Benchmark from 'benchmark'; import { getRandomIntArray, magnitude } from '../../../utils'; const suite = new Benchmark.Suite(); -const bst = new BST(); +const bst = new BST([], { isMapMode: true }); const { TEN_THOUSAND } = magnitude; const arr = getRandomIntArray(TEN_THOUSAND, 0, TEN_THOUSAND, true); diff --git a/test/performance/data-structures/binary-tree/rb-tree.test.ts b/test/performance/data-structures/binary-tree/rb-tree.test.ts index 6f8f823..5e18043 100644 --- a/test/performance/data-structures/binary-tree/rb-tree.test.ts +++ b/test/performance/data-structures/binary-tree/rb-tree.test.ts @@ -5,7 +5,7 @@ import { OrderedMap } from 'js-sdsl'; import { isCompetitor } from '../../../config'; const suite = new Benchmark.Suite(); -const rbTree = new RedBlackTree(); +const rbTree = new RedBlackTree([], { isMapMode: true }); const { HUNDRED_THOUSAND } = magnitude; const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); const cOrderedMap = new OrderedMap(); diff --git a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts index a51f3ff..d774f6b 100644 --- a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts @@ -678,7 +678,7 @@ describe('AVLTreeMultiMap iterative methods test', () => { expect(treeMM.count).toBe(21); const cloned = treeMM.clone(); expect(cloned.root?.left?.key).toBe(1); - expect(cloned.root?.right?.value).toBe('c'); + expect(cloned.get(cloned.root?.right)).toBe('c'); }); it('should keys', () => { @@ -761,3 +761,115 @@ describe('AVLTree toEntryFn', () => { expect(tree.dfs(node => node.key, 'IN', tree.root, 'RECURSIVE')).toEqual(expected); }); }); + +describe('AVLTreeMultiMap map mode count', () => { + let tm: AVLTreeMultiMap; + beforeEach(() => { + tm = new AVLTreeMultiMap([], { isMapMode: true }); + }); + it('Should added isolated node count ', () => { + tm.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new AVLTreeMultiMapNode(3, undefined, 10); + tm.add(newNode, 33); + expect(tm.count).toBe(15); + }); +}); + +describe('AVLTreeMultiMap map mode operations test1', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { + const treeMultimap = new AVLTreeMultiMap([], { isMapMode: true }); + + expect(treeMultimap instanceof AVLTreeMultiMap); + treeMultimap.add([11, 11]); + treeMultimap.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + treeMultimap.addMany(idAndValues); + expect(treeMultimap.root instanceof AVLTreeMultiMapNode); + + if (treeMultimap.root) expect(treeMultimap.root.key == 11); + + expect(treeMultimap.size).toBe(16); + expect(treeMultimap.count).toBe(18); + + expect(treeMultimap.has(6)); + + expect(treeMultimap.getHeight(6)).toBe(4); + expect(treeMultimap.getDepth(6)).toBe(0); + const nodeId10 = treeMultimap.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeMultimap.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); + +describe('AVLTreeMultiMap map mode operations test recursively1', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { + const treeMultimap = new AVLTreeMultiMap([], { + iterationType: 'RECURSIVE', + isMapMode: true + }); + + expect(treeMultimap instanceof AVLTreeMultiMap); + treeMultimap.add([11, 11]); + treeMultimap.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + treeMultimap.addMany(idAndValues); + expect(treeMultimap.root).toBeInstanceOf(AVLTreeMultiMapNode); + + if (treeMultimap.root) expect(treeMultimap.root.key).toBe(6); + + expect(treeMultimap.size).toBe(16); + expect(treeMultimap.count).toBe(18); + + expect(treeMultimap.has(6)); + + expect(treeMultimap.getHeight(6)).toBe(4); + expect(treeMultimap.getDepth(6)).toBe(0); + const nodeId10 = treeMultimap.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeMultimap.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index b262ce5..8e78038 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -33,7 +33,6 @@ describe('AVL Tree Test', () => { // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. expect(node15?.value).toBe(15); - const dfs = tree.dfs(node => node, 'IN'); expect(dfs[0].key).toBe(1); expect(dfs[dfs.length - 1].key).toBe(16); @@ -437,3 +436,90 @@ describe('AVLTree iterative methods test', () => { expect(leaves).toEqual([1, 3]); }); }); + +describe('AVL Tree map mode', () => { + it('should perform various operations on a AVL Tree', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + const tree = new AVLTree([], { isMapMode: true }); + + for (const i of arr) tree.add([i, i]); + + tree.add(null); + const node6 = tree.getNode(6); + + expect(node6 && tree.getHeight(node6)).toBe(3); + expect(node6 && tree.getDepth(node6)).toBe(1); + + const getNodeById = tree.getNode(10); + expect(getNodeById?.key).toBe(10); + + const getMinNodeByRoot = tree.getLeftMost(); + expect(getMinNodeByRoot).toBe(1); + + const node15 = tree.getNode(15); + const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + expect(subTreeSum).toBe(70); + + let lesserSum = 0; + tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); + + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(tree.get(node15)).toBe(15); + }); +}); + +describe('AVL Tree map mode test recursively', () => { + it('should perform various operations on a AVL Tree', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + const tree = new AVLTree([], { iterationType: 'RECURSIVE', isMapMode: true }); + + for (const i of arr) tree.add([i, i]); + + const node6 = tree.getNode(6); + + expect(node6 && tree.getHeight(node6)).toBe(3); + expect(node6 && tree.getDepth(node6)).toBe(1); + + const getNodeById = tree.getNode(10); + expect(getNodeById?.key).toBe(10); + + const getMinNodeByRoot = tree.getLeftMost(); + expect(getMinNodeByRoot).toBe(1); + + const node15 = tree.getNode(15); + const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + expect(subTreeSum).toBe(70); + + let lesserSum = 0; + tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); + + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(tree.get(node15)).toBe(15); + }); +}); + +describe('AVLTree iterative methods map mode', () => { + let avl: AVLTree; + beforeEach(() => { + avl = new AVLTree([], { isMapMode: true }); + avl.add([1, 'a']); + avl.add([2, 'b']); + avl.add([3, 'c']); + }); + + it('should clone work well', () => { + const cloned = avl.clone(); + expect(cloned.root?.left?.key).toBe(1); + expect(cloned.get(cloned.root?.right?.key)).toBe('c'); + }); +}); diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index 309668e..cd9c611 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -20,6 +20,8 @@ describe('BinaryTreeNode', () => { it('should set and get the value correctly', () => { const node: BinaryTreeNode = new BinaryTreeNode(1, 42); + expect(node.key).toBe(1); + expect(node.value).toBe(42); node.value = 55; @@ -319,26 +321,28 @@ describe('BinaryTree', () => { it('should isSubtreeBST', () => { expect(tree.toVisual()).toBe(''); tree.addMany([4, 2, 6, 1, 3, 5, 7, 4]); - expect(tree.toVisual()).toBe('N for null\n' + - ' ___4___ \n' + - ' / \\ \n' + - ' _2_ _6_ \n' + - ' / \\ / \\ \n' + - ' 1 3 5 7 \n' + - ' \n'); + expect(tree.toVisual()).toBe( + 'N for null\n' + + ' ___4___ \n' + + ' / \\ \n' + + ' _2_ _6_ \n' + + ' / \\ / \\ \n' + + ' 1 3 5 7 \n' + + ' \n' + ); const visualized = tree.toVisual(undefined, { isShowUndefined: true, isShowNull: true, isShowRedBlackNIL: true }); expect(visualized).toBe( 'U for undefined\n' + - 'N for null\n' + - 'S for Sentinel Node(NIL)\n' + - ' _______4_______ \n' + - ' / \\ \n' + - ' ___2___ ___6___ \n' + - ' / \\ / \\ \n' + - ' _1_ _3_ _5_ _7_ \n' + - ' / \\ / \\ / \\ / \\ \n' + - ' U U U U U U U U \n' + - ' \n' + 'N for null\n' + + 'S for Sentinel Node(NIL)\n' + + ' _______4_______ \n' + + ' / \\ \n' + + ' ___2___ ___6___ \n' + + ' / \\ / \\ \n' + + ' _1_ _3_ _5_ _7_ \n' + + ' / \\ / \\ / \\ / \\ \n' + + ' U U U U U U U U \n' + + ' \n' ); expect(tree.isBST(tree.getNode(4), 'RECURSIVE')).toBe(true); @@ -729,34 +733,40 @@ describe('BinaryTree', () => { ]); }); - it('should keyValueOrEntryOrRawElementToNode', () => { + it('should keyValueNodeEntryRawToNodeAndValue', () => { const tree = new BinaryTree(); - const node0 = tree.keyValueOrEntryOrRawElementToNode(0); - expect(node0).toEqual({ - _left: undefined, - _right: undefined, - key: 0, - parent: undefined, - value: undefined - }); + const node0 = tree.keyValueNodeEntryRawToNodeAndValue(0); + expect(node0).toEqual([ + { + _left: undefined, + _right: undefined, + key: 0, + parent: undefined, + value: undefined + }, + undefined + ]); - const nodeUndefined = tree.keyValueOrEntryOrRawElementToNode(undefined); - expect(nodeUndefined).toBe(undefined); + const nodeUndefined = tree.keyValueNodeEntryRawToNodeAndValue(undefined); + expect(nodeUndefined).toEqual([undefined, undefined]); - const nodeNull = tree.keyValueOrEntryOrRawElementToNode(null); - expect(nodeNull).toBe(null); + const nodeNull = tree.keyValueNodeEntryRawToNodeAndValue(null); + expect(nodeNull).toEqual([null, undefined]); - const nodeWithSeparateValue = tree.keyValueOrEntryOrRawElementToNode(7, 77); - expect(nodeWithSeparateValue?.value).toBe(77); + const [, nodeWithSeparateValue] = tree.keyValueNodeEntryRawToNodeAndValue(7, 77); + expect(nodeWithSeparateValue).toBe(77); - expect(tree.keyValueOrEntryOrRawElementToNode([undefined, 2])).toBe(undefined); + expect(tree.keyValueNodeEntryRawToNodeAndValue([undefined, 2])).toEqual([undefined, undefined]); - expect(tree.keyValueOrEntryOrRawElementToNode(Symbol('test') as unknown as number)).toBe(undefined); + expect(tree.keyValueNodeEntryRawToNodeAndValue(Symbol('test') as unknown as number)).toEqual([ + undefined, + undefined + ]); const bTree = new BinaryTree([], { toEntryFn: (ele: { obj: { id: number } }) => [Symbol('test') as unknown as number, ele.obj.id] }); - expect(bTree.keyValueOrEntryOrRawElementToNode({ obj: { id: 1 } })).toBe(undefined); + expect(bTree.keyValueNodeEntryRawToNodeAndValue({ obj: { id: 1 } })).toEqual([undefined, undefined]); }); }); @@ -1150,17 +1160,21 @@ describe('BinaryTree', () => { tree.add([2, 'B']); tree.add([null, 'null']); - const nodes = tree.getNodes(node => node.value === 'B'); + const nodes = tree.isMapMode ? tree.getNodes(node => node.key === 2) : tree.getNodes(node => node.value === 'B'); expect(nodes.length).toBe(1); expect(nodes[0].key).toBe(2); - const nodesRec = tree.getNodes(node => node.value === 'B', false, tree.root, 'RECURSIVE'); + const nodesRec = tree.isMapMode + ? tree.getNodes(node => node.key === 2, false, tree.root, 'RECURSIVE') + : tree.getNodes(node => node.value === 'B', false, tree.root, 'RECURSIVE'); expect(nodesRec.length).toBe(1); expect(nodesRec[0].key).toBe(2); - const nodesItr = tree.getNodes(node => node.value === 'B', false, tree.root, 'ITERATIVE'); + const nodesItr = tree.isMapMode + ? tree.getNodes(node => node.key === 2, false, tree.root, 'ITERATIVE') + : tree.getNodes(node => node.value === 'B', false, tree.root, 'ITERATIVE'); expect(nodesItr.length).toBe(1); expect(nodesItr[0].key).toBe(2); @@ -1206,6 +1220,98 @@ describe('BinaryTree', () => { }); }); +describe('BinaryTree map mode', () => { + let tree: BinaryTree; + + beforeEach(() => { + tree = new BinaryTree([], { + iterationType: 'RECURSIVE', + isMapMode: true + }); + }); + + afterEach(() => { + tree.clear(); + }); + + it('should add and find nodes', () => { + tree.add([1, 1]); + tree.add(undefined); + tree.add([2, 2]); + tree.add([3, 3]); + + expect(tree.has(1)).toBe(true); + expect(tree.has(2)).toBe(true); + expect(tree.has(3)).toBe(true); + expect(tree.has(4)).toBe(false); + const node4 = tree.getNode(4); + expect(tree.has(node4)).toBe(false); + expect(tree.has(node => node === node4)).toBe(false); + if (tree.isMapMode) expect(tree.has(node => node.key?.toString() === '3')).toBe(true); + else expect(tree.has(node => node.value?.toString() === '3')).toBe(true); + }); + + it('should isSubtreeBST', () => { + tree.addMany([ + new BinaryTreeNode(4), + new BinaryTreeNode(2), + new BinaryTreeNode(6), + new BinaryTreeNode(1), + new BinaryTreeNode(3), + new BinaryTreeNode(5), + new BinaryTreeNode(7), + new BinaryTreeNode(4) + ]); + + expect(tree.isBST(tree.getNode(4), 'RECURSIVE')).toBe(true); + expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true); + }); + + it('should get nodes by key', () => { + tree.add([5, 'A']); + tree.add([3, 'B']); + tree.add([7, 'C']); + + const nodeA = tree.getNode(5); + const nodeB = tree.getNode(3); + + expect(nodeA?.key).toBe(5); + expect(tree.get(nodeA)).toBe('A'); + expect(nodeB?.key).toBe(3); + expect(tree.get(nodeB)).toBe('B'); + }); + + it('should get nodes by a custom callback', () => { + tree.add([5, 'E']); + tree.add([4, 'D']); + tree.add([3, 'C']); + tree.add([7, 'G']); + tree.add([null, 'null']); + tree.add([1, 'A']); + tree.add([6, 'F']); + tree.add([null, 'null']); + tree.add([2, 'B']); + tree.add([null, 'null']); + + const nodes = tree.getNodes(node => node.key === 2); + + expect(nodes.length).toBe(1); + expect(nodes[0].key).toBe(2); + + const nodesRec = tree.getNodes(node => node.key === 2, false, tree.root, 'RECURSIVE'); + + expect(nodesRec.length).toBe(1); + expect(nodesRec[0].key).toBe(2); + + const nodesItr = tree.getNodes(node => node.key === 2, false, tree.root, 'ITERATIVE'); + + expect(nodesItr.length).toBe(1); + expect(nodesItr[0].key).toBe(2); + + expect(nodesItr).toEqual(nodesRec); + }); +}); + describe('BinaryTree iterative methods test', () => { let binaryTree: BinaryTree; beforeEach(() => { @@ -1273,7 +1379,8 @@ describe('BinaryTree iterative methods test', () => { it('should clone work well', () => { const cloned = binaryTree.clone(); expect(cloned.root?.left?.key).toBe(2); - expect(cloned.root?.right?.value).toBe('c'); + if (cloned.isMapMode) expect(cloned.get(cloned.root?.right)).toBe('c'); + else expect(cloned.root?.right?.value).toBe('c'); }); it('should keys', () => { @@ -1315,3 +1422,19 @@ describe('BinaryTree iterative methods test', () => { ]); }); }); + +describe('BinaryTree map mode iterative methods test', () => { + let binaryTree: BinaryTree; + beforeEach(() => { + binaryTree = new BinaryTree([], { isMapMode: true }); + binaryTree.add([1, 'a']); + binaryTree.add(2, 'b'); + binaryTree.add([3, 'c']); + }); + + it('should clone work well', () => { + const cloned = binaryTree.clone(); + expect(cloned.root?.left?.key).toBe(2); + expect(cloned.get(cloned.root?.right)).toBe('c'); + }); +}); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 6637822..2306212 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -1,5 +1,5 @@ import { BinaryTreeNode, BST, BSTNode } from '../../../../src'; -import { isDebugTest, SYSTEM_MAX_CALL_STACK, isTestStackOverflow } from '../../../config'; +import { isDebugTest, isTestStackOverflow, SYSTEM_MAX_CALL_STACK } from '../../../config'; const isDebug = isDebugTest; @@ -450,22 +450,25 @@ describe('BST operations test', () => { expect(bfsNodes[2].key).toBe(16); }); - it('should keyValueOrEntryOrRawElementToNode', () => { + it('should keyValueNodeEntryRawToNodeAndValue', () => { const bst = new BST(); - const node0 = bst.keyValueOrEntryOrRawElementToNode(0); - expect(node0).toEqual({ - _left: undefined, - _right: undefined, - key: 0, - parent: undefined, - value: undefined - }); + const node0 = bst.keyValueNodeEntryRawToNodeAndValue(0); + expect(node0).toEqual([ + { + _left: undefined, + _right: undefined, + key: 0, + parent: undefined, + value: undefined + }, + undefined + ]); - const nodeUndefined = bst.keyValueOrEntryOrRawElementToNode(undefined); - expect(nodeUndefined).toBe(undefined); + const nodeUndefined = bst.keyValueNodeEntryRawToNodeAndValue(undefined); + expect(nodeUndefined).toEqual([undefined, undefined]); - const nodeNull = bst.keyValueOrEntryOrRawElementToNode(null); - expect(nodeNull).toBe(undefined); + const nodeNull = bst.keyValueNodeEntryRawToNodeAndValue(null); + expect(nodeNull).toEqual([undefined, undefined]); }); }); @@ -496,7 +499,7 @@ describe('BST operations test recursively', () => { expect(nodeId10?.key).toBe(10); const nodeVal9 = bst.getNode(node => node.value === 9); - expect(nodeVal9?.key).toBe(undefined); + expect(bst.get(nodeVal9?.key)).toBe(undefined); const leftMost = bst.getLeftMost(); expect(leftMost).toBe(1); @@ -1210,3 +1213,300 @@ describe('BST iterative methods test', () => { expect(balanced.leaves(node => node?.value)).toEqual(['a', 'f', 'd', 'i']); }); }); + +describe('BST operations map mode test', () => { + it('should perform various operations on a Binary Search Tree with numeric values', () => { + const bst = new BST([], { isMapMode: true }); + expect(bst).toBeInstanceOf(BST); + bst.add([11, 11]); + bst.add([3, 3]); + const idsAndValues: [number, number][] = [ + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + bst.addMany(idsAndValues, undefined, false); + expect(bst.root).toBeInstanceOf(BSTNode); + + if (bst.root) expect(bst.root.key).toBe(11); + + expect(bst.size).toBe(16); + + expect(bst.has(6)).toBe(true); + + const node6 = bst.getNode(6); + expect(node6 && bst.getHeight(6)).toBe(2); + expect(node6 && bst.getDepth(6)).toBe(3); + + const nodeId10 = bst.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = bst.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + + const leftMost = bst.getLeftMost(); + expect(leftMost).toBe(1); + + expect(bst.isBST()).toBe(true); + + const node15 = bst.getNode(15); + const minNodeBySpecificNode = node15 && bst.getLeftMost(node => node, node15); + expect(minNodeBySpecificNode?.key).toBe(12); + + const nodes = bst.getNodes(node => node.key === 15); + expect(nodes.map(node => node.key)).toEqual([15]); + }); + + it('should perform various operations on a Binary Search Tree with object values', () => { + const objBST = new BST([], { isMapMode: true }); + expect(objBST).toBeInstanceOf(BST); + objBST.add([11, { name: '11', age: 11 }]); + objBST.add([3, { name: '3', age: 3 }]); + + objBST.addMany( + [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5], + [ + { name: 'Alice', age: 15 }, + { name: 'Bob', age: 1 }, + { name: 'Charlie', age: 8 }, + { name: 'David', age: 13 }, + { name: 'Emma', age: 16 }, + { name: 'Frank', age: 2 }, + { name: 'Grace', age: 6 }, + { name: 'Hannah', age: 9 }, + { name: 'Isaac', age: 12 }, + { name: 'Jack', age: 14 }, + { name: 'Katie', age: 4 }, + { name: 'Liam', age: 7 }, + { name: 'Mia', age: 10 }, + { name: 'Noah', age: 5 } + ], + false + ); + + expect(objBST.root).toBeInstanceOf(BSTNode); + + if (objBST.root) expect(objBST.root.key).toBe(11); + + expect(objBST.has(6)).toBe(true); + + const node6 = objBST.getNode(6); + expect(node6 && objBST.getHeight(node6)).toBe(2); + expect(node6 && objBST.getDepth(node6)).toBe(3); + + const nodeId10 = objBST.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = objBST.getNode(9); + expect(nodeVal9?.key).toBe(9); + + const leftMost = objBST.getLeftMost(); + expect(leftMost).toBe(1); + + const node15 = objBST.getNode(15); + expect(objBST.get(node15)).toEqual({ + name: 'Alice', + age: 15 + }); + }); + + it('should keyValueNodeEntryRawToNodeAndValue', () => { + const bst = new BST([], { isMapMode: true }); + const node0 = bst.keyValueNodeEntryRawToNodeAndValue(0); + expect(node0).toEqual([ + { + _left: undefined, + _right: undefined, + key: 0, + parent: undefined, + value: undefined + }, + undefined + ]); + + const nodeUndefined = bst.keyValueNodeEntryRawToNodeAndValue(undefined); + expect(nodeUndefined).toEqual([undefined, undefined]); + + const nodeNull = bst.keyValueNodeEntryRawToNodeAndValue(null); + expect(nodeNull).toEqual([undefined, undefined]); + }); +}); + +describe('BST operations map mode test recursively', () => { + it('should perform various operations on a Binary Search Tree with numeric values', () => { + const bst = new BST([], { + iterationType: 'RECURSIVE', + isMapMode: true + }); + expect(bst).toBeInstanceOf(BST); + bst.add([11, 11]); + bst.add([3, 3]); + const idsAndValues = [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + bst.addMany(idsAndValues, undefined, false); + expect(bst.root).toBeInstanceOf(BSTNode); + + if (bst.root) expect(bst.root.key).toBe(11); + + expect(bst.size).toBe(16); + + expect(bst.has(6)).toBe(true); + + const node6 = bst.getNode(6); + expect(node6 && bst.getHeight(6)).toBe(2); + expect(node6 && bst.getDepth(6)).toBe(3); + + const nodeId10 = bst.getNode(10); + expect(bst.get(10)).toBe(undefined); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = bst.getNode(node => node.key === 9); + expect(bst.get(nodeVal9?.key)).toBe(undefined); + }); + + it('should perform various operations on a Binary Search Tree with object values', () => { + const objBST = new BST([], { isMapMode: true }); + expect(objBST).toBeInstanceOf(BST); + objBST.add([11, { key: 11, keyA: 11 }]); + objBST.add([3, { key: 3, keyA: 3 }]); + const entries: [number, { key: number; keyA: number }][] = [ + [15, { key: 15, keyA: 15 }], + [1, { key: 1, keyA: 1 }], + [8, { key: 8, keyA: 8 }], + [13, { key: 13, keyA: 13 }], + [16, { key: 16, keyA: 16 }], + [2, { key: 2, keyA: 2 }], + [6, { key: 6, keyA: 6 }], + [9, { key: 9, keyA: 9 }], + [12, { key: 12, keyA: 12 }], + [14, { key: 14, keyA: 14 }], + [4, { key: 4, keyA: 4 }], + [7, { key: 7, keyA: 7 }], + [10, { key: 10, keyA: 10 }], + [5, { key: 5, keyA: 5 }] + ]; + + objBST.addMany(entries, undefined, false); + + expect(objBST.root).toBeInstanceOf(BSTNode); + + if (objBST.root) expect(objBST.root.key).toBe(11); + + expect(objBST.has(6)).toBe(true); + + const node6 = objBST.getNode(6); + expect(objBST.get(6)).toEqual({ + key: 6, + keyA: 6 + }); + expect(node6 && objBST.getHeight(node6)).toBe(2); + expect(node6 && objBST.getDepth(node6)).toBe(3); + + const nodeId10 = objBST.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = objBST.getNode(9); + expect(nodeVal9?.key).toBe(9); + + const leftMost = objBST.getLeftMost(); + expect(leftMost).toBe(1); + + const node15 = objBST.getNode(15); + expect(objBST.get(node15)).toEqual({ + key: 15, + keyA: 15 + }); + }); +}); + +describe('BST iterative methods map mode test', () => { + let bst: BST; + beforeEach(() => { + bst = new BST(); + bst.addMany( + [ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ], + [], + false + ); + }); + + it('should clone work well', () => { + const cloned = bst.clone(); + expect(cloned.root?.left).toBe(undefined); + expect(cloned.get(cloned.root?.right)).toBe('b'); + }); + + it('should collapsed, unbalanced, balanced bst leaves', () => { + const collapsedToLinkedList = new BST(); + collapsedToLinkedList.addMany( + [ + [1, 'a'], + [2, 'b'], + [3, 'c'], + [4, 'd'], + [5, 'e'], + [6, 'f'], + [7, 'g'], + [8, 'h'], + [9, 'i'] + ], + [], + false + ); + + expect(collapsedToLinkedList.leaves()).toEqual([9]); + + const unbalanced = new BST(); + unbalanced.addMany( + [ + [2, 'b'], + [1, 'a'], + [3, 'c'], + [4, 'd'], + [5, 'e'], + [6, 'f'], + [7, 'g'], + [8, 'h'], + [9, 'i'] + ], + [], + false + ); + + expect(unbalanced.leaves()).toEqual([1, 9]); + + const balanced = new BST(); + balanced.addMany( + [ + [2, 'b'], + [1, 'a'], + [3, 'c'], + [4, 'd'], + [5, 'e'], + [6, 'f'], + [7, 'g'], + [8, 'h'], + [9, 'i'] + ], + [], + true + ); + + expect(balanced.leaves()).toEqual([1, 6, 4, 9]); + expect(balanced.leaves(node => balanced.get(node?.key))).toEqual(['a', 'f', 'd', 'i']); + }); +}); diff --git a/test/unit/data-structures/binary-tree/tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts index a4a2f7a..96bb9a0 100644 --- a/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts @@ -817,7 +817,8 @@ describe('TreeMultiMap iterative methods test', () => { expect(treeMM.getComputedCount()).toBe(21); const cloned = treeMM.clone(); expect(cloned.root?.left?.key).toBe(1); - expect(cloned.root?.right?.value).toBe('c'); + if (cloned.isMapMode) expect(cloned.get(cloned.root?.right)).toBe('c'); + else expect(cloned.root?.right?.value).toBe(undefined); }); it('should keys', () => { @@ -835,3 +836,140 @@ describe('TreeMultiMap iterative methods test', () => { expect(leaves).toEqual([1, 3]); }); }); + +describe('TreeMultiMap count map mode', () => { + let tmm: TreeMultiMap; + beforeEach(() => { + tmm = new TreeMultiMap([], { isMapMode: true }); + }); + + it('Should added node count ', () => { + tmm.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new TreeMultiMapNode(3, undefined, 10); + tmm.add(newNode, 33, 20); + // TODO expect(tmm.count).toBe(25); + expect(tmm.count).toBe(15); + expect(tmm.getComputedCount()).toBe(15); + expect(tmm.getNode(3)?.count).toBe(11); + }); +}); + +describe('TreeMultiMap operations test1 map mode', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { + const tmm = new TreeMultiMap([], { isMapMode: true }); + + expect(tmm instanceof TreeMultiMap); + + tmm.add([11, 11]); + tmm.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + tmm.addMany(idAndValues); + expect(tmm.root instanceof TreeMultiMapNode); + + if (tmm.root) expect(tmm.root.key == 11); + + expect(tmm.size).toBe(16); + expect(tmm.count).toBe(18); + expect(tmm.getComputedCount()).toBe(18); + + expect(tmm.has(6)); + if (isDebug) tmm.print(); + expect(tmm.getHeight(6)).toBe(1); + expect(tmm.getDepth(6)).toBe(3); + const nodeId10 = tmm.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = tmm.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); + +describe('TreeMultiMap operations test recursively1 map mode', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { + const tmm = new TreeMultiMap([], { + iterationType: 'RECURSIVE', + isMapMode: true + }); + + expect(tmm instanceof TreeMultiMap); + tmm.add([11, 11]); + tmm.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + tmm.addMany(idAndValues); + expect(tmm.root).toBeInstanceOf(TreeMultiMapNode); + + if (tmm.root) expect(tmm.root.key).toBe(5); + + expect(tmm.size).toBe(16); + expect(tmm.count).toBe(18); + expect(tmm.getComputedCount()).toBe(18); + + expect(tmm.has(6)); + + expect(tmm.getHeight(6)).toBe(1); + expect(tmm.getDepth(6)).toBe(3); + const nodeId10 = tmm.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = tmm.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); + +describe('TreeMultiMap iterative methods testm ap mode', () => { + let treeMM: TreeMultiMap; + beforeEach(() => { + treeMM = new TreeMultiMap([], { isMapMode: true }); + treeMM.add(1, 'a', 10); + treeMM.add([2, 'b'], undefined, 10); + treeMM.add([3, 'c'], undefined, 1); + }); + + it('should clone work well', () => { + expect(treeMM.count).toBe(21); + expect(treeMM.getComputedCount()).toBe(21); + const cloned = treeMM.clone(); + expect(cloned.root?.left?.key).toBe(1); + expect(cloned.get(cloned.root?.right)).toBe('c'); + }); +});