From 6dc64dbb539e69ddca712fc3ed87de629789ca59 Mon Sep 17 00:00:00 2001 From: Revone Date: Thu, 28 Nov 2024 19:37:51 +1300 Subject: [PATCH] fix: Discard the awkward type-safe implementation in the binary tree data structure where subclasses need to pass the node type and tree type back to the parent class. --- CHANGELOG.md | 2 +- package-lock.json | 52 +- package.json | 14 +- .../binary-tree/avl-tree-multi-map.ts | 143 ++-- src/data-structures/binary-tree/avl-tree.ts | 152 +++-- .../binary-tree/binary-tree.ts | 621 +++++++++--------- src/data-structures/binary-tree/bst.ts | 358 +++++----- .../binary-tree/red-black-tree.ts | 229 ++++--- .../binary-tree/tree-multi-map.ts | 304 +++++---- src/interfaces/binary-tree.ts | 34 +- .../binary-tree/avl-tree-multi-map.ts | 5 - .../data-structures/binary-tree/avl-tree.ts | 5 - .../binary-tree/binary-tree.ts | 5 - src/types/data-structures/binary-tree/bst.ts | 10 +- .../data-structures/binary-tree/rb-tree.ts | 7 +- .../binary-tree/tree-multi-map.ts | 5 - test/integration/all-in-one.test.ts | 2 +- test/integration/avl-tree.test.ts | 2 +- test/integration/bst.test.ts | 4 +- test/integration/compile.js | 143 ++++ test/integration/compile.ts | 171 +++++ test/integration/index.html | 96 +-- 22 files changed, 1336 insertions(+), 1028 deletions(-) create mode 100644 test/integration/compile.js create mode 100644 test/integration/compile.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 712f826..6d82371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - [Semantic Versioning](https://semver.org/spec/v2.0.0.html) - [`auto-changelog`](https://github.com/CookPete/auto-changelog) -## [v1.53.9](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming) +## [v1.54.0](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming) ### Changes diff --git a/package-lock.json b/package-lock.json index 804e678..c09ff19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-structure-typed", - "version": "1.53.9", + "version": "1.54.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.53.9", + "version": "1.54.0", "license": "MIT", "devDependencies": { "@eslint/compat": "^1.2.2", @@ -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.53.8", + "avl-tree-typed": "^1.54.0", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.53.8", - "bst-typed": "^1.53.8", - "data-structure-typed": "^1.53.8", + "binary-tree-typed": "^1.54.0", + "bst-typed": "^1.54.0", + "data-structure-typed": "^1.54.0", "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.53.8", + "heap-typed": "^1.54.0", "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.53.8", - "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.8.tgz", - "integrity": "sha512-FJKmj/kYbdK7Du/Bva9NSLHmiSVA4KqV7AEd4yqnKYrwEfXVfvHJ6wggfyQmdwhrygFTET4ByAldshFzGIwBHQ==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.54.0.tgz", + "integrity": "sha512-MKPtDs1yhtlf/5zd2ObkIk7LvpCoCfCljz0b2TNiy28HUiwcbLUsbdbKu40UWEvNxQyqATiN4cJdmkdzL9ExPQ==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.8" + "data-structure-typed": "^1.54.0" } }, "node_modules/babel-jest": { @@ -3602,13 +3602,13 @@ } }, "node_modules/binary-tree-typed": { - "version": "1.53.8", - "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.53.8.tgz", - "integrity": "sha512-qMTzw2MfEB1Ihn5LBtmgk4bL5rBd0E6x1bJLiZTudbWv/PrDrT+GrtIcVozwKQG6QwjRbbnnkWYDm509l+To3g==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.54.0.tgz", + "integrity": "sha512-O21INGCCZpW784GDaw9J/GAEo8/IfZ/UJR0B6bK8GHm9g4d9UvMqvWG3Yss4OmMnrZRmRNbbKrx/uzNwjRwPCg==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.8" + "data-structure-typed": "^1.54.0" } }, "node_modules/brace-expansion": { @@ -3691,13 +3691,13 @@ } }, "node_modules/bst-typed": { - "version": "1.53.8", - "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.8.tgz", - "integrity": "sha512-BvqI2skHCrPYE3JYhSpSjpsRwGHQKw9/SNE5vC4AzxOQpL3fmg9bSBG5aGIrPk4Uc3BEkL9NUignLbjJMNwXcw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.54.0.tgz", + "integrity": "sha512-/3S/ijw6syQ66raRBjkuFcSLKOpJ8OOr409+go52fJ+QLMqBRUUb2CBbHHKN3KD65JXoTM+bLd6TzOsquaA8CQ==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.8" + "data-structure-typed": "^1.54.0" } }, "node_modules/buffer-from": { @@ -4069,9 +4069,9 @@ } }, "node_modules/data-structure-typed": { - "version": "1.53.8", - "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.8.tgz", - "integrity": "sha512-d0kCsb0U1kNMNcsoJI2zX1LkrsiX9FSLXPN4PL9IweyvAsQybcwdV7Y48ZLcUPWnShipuvaaxFLYtznjNcSsqw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.54.0.tgz", + "integrity": "sha512-arcHv2vHwFLElHpavuVp4RpGyjGp0aDjWD+zp1G3UXYjKVBR38NPtFGmPn8BCUnqEElhtmu3HBss6edOzrdKkg==", "dev": true, "license": "MIT" }, @@ -5946,13 +5946,13 @@ } }, "node_modules/heap-typed": { - "version": "1.53.8", - "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.53.8.tgz", - "integrity": "sha512-Gmwpyl0JSZka8KMR6q3OMsrfA88XA8BmP3aEdT26UnMsX5HBwlBnj2vJvKqOCl5lPl3AhOD5nAj5zxI84bwrrw==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.54.0.tgz", + "integrity": "sha512-eVkfZSx1Kf5IzVltLFybgVHzcTJL0i9CsgZFHfDSpVTUe3tZ2i/mzCh00olslFsoO5KqmodPaqK3epoUUyEMsw==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.8" + "data-structure-typed": "^1.54.0" } }, "node_modules/html-escaper": { diff --git a/package.json b/package.json index fec33d3..6fb1fc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.53.9", + "version": "1.54.0", "description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", @@ -23,7 +23,7 @@ "gen:examples": "ts-node scripts/testToExample.ts", "test:in-band": "jest --runInBand", "test": "npm run test:in-band", - "test:integration": "npm run update:subs && jest --config jest.integration.config.js", + "test:integration": "npm run update:subs && jest --config jest.integration.config.js && tsc test/integration/compile.ts", "test:perf": "npm run build:cjs && npm run build:mjs && ts-node test/performance/reportor.ts", "check": "tsc --noEmit", "check:circular-refs": "dependency-cruiser src", @@ -70,11 +70,11 @@ "@typescript-eslint/eslint-plugin": "^8.12.1", "@typescript-eslint/parser": "^8.12.1", "auto-changelog": "^2.5.0", - "avl-tree-typed": "^1.53.8", + "avl-tree-typed": "^1.54.0", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.53.8", - "bst-typed": "^1.53.8", - "data-structure-typed": "^1.53.8", + "binary-tree-typed": "^1.54.0", + "bst-typed": "^1.54.0", + "data-structure-typed": "^1.54.0", "dependency-cruiser": "^16.5.0", "doctoc": "^2.2.1", "eslint": "^9.13.0", @@ -83,7 +83,7 @@ "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-import": "^2.31.0", "fast-glob": "^3.3.2", - "heap-typed": "^1.53.8", + "heap-typed": "^1.54.0", "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 114758b..819d910 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -6,23 +6,18 @@ * @license MIT License */ import type { - AVLTreeMultiMapNested, - AVLTreeMultiMapNodeNested, AVLTreeMultiMapOptions, BinaryTreeDeleteResult, BSTNOptKeyOrNode, BTNRep, EntryCallback, - IterationType + IterationType, + OptNodeOrNull } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { AVLTree, AVLTreeNode } from './avl-tree'; -export class AVLTreeMultiMapNode< - K = any, - V = any, - NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNodeNested -> extends AVLTreeNode { +export class AVLTreeMultiMapNode extends AVLTreeNode { /** * The constructor function initializes a BinaryTreeNode object with a key, value, and count. * @param {K} key - The `key` parameter is of type `K` and represents the unique identifier @@ -37,32 +32,42 @@ export class AVLTreeMultiMapNode< super(key, value); this.count = count; } + + override parent?: AVLTreeMultiMapNode = undefined; + + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { + return this._left; + } + + override set left(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._left = v; + } + + override _right?: OptNodeOrNull> = undefined; + + override get right(): OptNodeOrNull> { + return this._right; + } + + override set right(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._right = v; + } } /** * The only distinction between a AVLTreeMultiMap and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters. */ -export class AVLTreeMultiMap< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNode>, - TREE extends AVLTreeMultiMap = AVLTreeMultiMap< - K, - V, - R, - MK, - MV, - MR, - NODE, - AVLTreeMultiMapNested - > - > - extends AVLTree - implements IBinaryTree +export class AVLTreeMultiMap + extends AVLTree + implements IBinaryTree { /** * The constructor initializes a new AVLTreeMultiMap object with optional initial elements. @@ -73,7 +78,7 @@ export class AVLTreeMultiMap< * `compareValues` functions to define custom comparison logic for keys and values, respectively. */ constructor( - keysNodesEntriesOrRaws: Iterable> = [], + keysNodesEntriesOrRaws: Iterable>> = [], options?: AVLTreeMultiMapOptions ) { super([], options); @@ -114,10 +119,10 @@ export class AVLTreeMultiMap< * @param {number} [count] - The `count` parameter represents the number of occurrences of a * key-value pair in the AVLTreeMultiMapNode. It is an optional parameter, so it can be omitted when * calling the `createNode` method. If provided, it specifies the initial count for the node. - * @returns a new instance of the AVLTreeMultiMapNode class, casted as NODE. + * @returns a new instance of the AVLTreeMultiMapNode class, casted as AVLTreeMultiMapNode. */ - override createNode(key: K, value?: V, count?: number): NODE { - return new AVLTreeMultiMapNode(key, this._isMapMode ? undefined : value, count) as NODE; + override createNode(key: K, value?: V, count?: number): AVLTreeMultiMapNode { + return new AVLTreeMultiMapNode(key, this._isMapMode ? undefined : value, count) as AVLTreeMultiMapNode; } /** @@ -127,25 +132,27 @@ export class AVLTreeMultiMap< * @returns a new instance of the AVLTreeMultiMap class, with the specified options, as a TREE * object. */ - override createTree(options?: AVLTreeMultiMapOptions): TREE { - return new AVLTreeMultiMap([], { + override createTree(options?: AVLTreeMultiMapOptions) { + return new AVLTreeMultiMap([], { iterationType: this.iterationType, isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, ...options - }) as TREE; + }); } /** * The function checks if the input is an instance of AVLTreeMultiMapNode. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + override isNode( + keyNodeEntryOrRaw: BTNRep> | R + ): keyNodeEntryOrRaw is AVLTreeMultiMapNode { return keyNodeEntryOrRaw instanceof AVLTreeMultiMapNode; } @@ -155,9 +162,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 {BTNRep | R} keyNodeEntryOrRaw - The + * @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, + * 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. @@ -166,7 +173,7 @@ 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(keyNodeEntryOrRaw: BTNRep | R, value?: V, count = 1): boolean { + override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V, count = 1): boolean { const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count); if (newNode === undefined) return false; @@ -184,7 +191,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 {BTNRep | R} keyNodeEntryOrRaw - 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. @@ -197,16 +204,19 @@ 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(keyNodeEntryOrRaw: BTNRep | R, ignoreCount = false): BinaryTreeDeleteResult[] { - const deletedResult: BinaryTreeDeleteResult[] = []; + override delete( + keyNodeEntryOrRaw: BTNRep> | R, + ignoreCount = false + ): BinaryTreeDeleteResult>[] { + const deletedResult: BinaryTreeDeleteResult>[] = []; if (!this.root) return deletedResult; - const curr: NODE | undefined = this.getNode(keyNodeEntryOrRaw) ?? undefined; + const curr: AVLTreeMultiMapNode | undefined = this.getNode(keyNodeEntryOrRaw) ?? undefined; if (!curr) return deletedResult; - const parent: NODE | undefined = curr?.parent ? curr.parent : undefined; - let needBalanced: NODE | undefined = undefined, - orgCurrent: NODE | undefined = curr; + const parent: AVLTreeMultiMapNode | undefined = curr?.parent ? curr.parent : undefined; + let needBalanced: AVLTreeMultiMapNode | undefined = undefined, + orgCurrent: AVLTreeMultiMapNode | undefined = curr; if (curr.count > 1 && !ignoreCount) { curr.count--; @@ -324,7 +334,7 @@ export class AVLTreeMultiMap< * The function overrides the clone method to create a deep copy of a tree object. * @returns The `clone()` method is returning a cloned instance of the `TREE` object. */ - override clone(): TREE { + override clone() { const cloned = this.createTree(); 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)); @@ -366,20 +376,20 @@ export class AVLTreeMultiMap< /** * The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into * a node object. - * @param {BTNRep | R} keyNodeEntryOrRaw - The - * `keyNodeEntryOrRaw` parameter can be of type `R` or `BTNRep`. + * @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`. * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of * 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. + * @returns either a AVLTreeMultiMapNode object or undefined. */ protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep | R, + keyNodeEntryOrRaw: BTNRep> | R, value?: V, count = 1 - ): [NODE | undefined, V | undefined] { + ): [AVLTreeMultiMapNode | undefined, V | undefined] { if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; @@ -407,17 +417,17 @@ export class AVLTreeMultiMap< * * The `_swapProperties` function swaps the properties (key, value, count, height) between two nodes * in a binary search tree. - * @param {R | BSTNOptKeyOrNode} 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 | BSTNOptKeyOrNode} 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 | BSTNOptKeyOrNode, - destNode: R | BSTNOptKeyOrNode - ): NODE | undefined { + srcNode: R | BSTNOptKeyOrNode>, + destNode: R | BSTNOptKeyOrNode> + ): AVLTreeMultiMapNode | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); if (srcNode && destNode) { @@ -447,13 +457,16 @@ export class AVLTreeMultiMap< * Space Complexity: O(1) * * The function replaces an old node with a new node and updates the count property of the new node. - * @param {NODE} oldNode - The oldNode parameter represents the node that needs to be replaced in the - * data structure. It is of type NODE. - * @param {NODE} newNode - The `newNode` parameter is an instance of the `NODE` class. + * @param {AVLTreeMultiMapNode} oldNode - The oldNode parameter represents the node that needs to be replaced in the + * data structure. It is of type AVLTreeMultiMapNode. + * @param {AVLTreeMultiMapNode} newNode - The `newNode` parameter is an instance of the `AVLTreeMultiMapNode` class. * @returns The method is returning the result of calling the `_replaceNode` method from the - * superclass, which is of type `NODE`. + * superclass, which is of type `AVLTreeMultiMapNode`. */ - protected override _replaceNode(oldNode: NODE, newNode: NODE): NODE { + protected override _replaceNode( + oldNode: AVLTreeMultiMapNode, + newNode: AVLTreeMultiMapNode + ): AVLTreeMultiMapNode { newNode.count = oldNode.count + newNode.count; return super._replaceNode(oldNode, newNode); } diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index e74481c..87a56a0 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -7,21 +7,16 @@ */ import { BST, BSTNode } from './bst'; import type { - AVLTreeNested, - AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeleteResult, BSTNOptKeyOrNode, BTNRep, - EntryCallback + EntryCallback, + OptNodeOrNull } from '../../types'; import { IBinaryTree } from '../../interfaces'; -export class AVLTreeNode< - K = any, - V = any, - NODE extends AVLTreeNode = AVLTreeNodeNested -> extends BSTNode { +export class AVLTreeNode extends BSTNode { /** * The constructor function initializes a new instance of a class with a key and an optional value, * and sets the height property to 0. @@ -33,6 +28,34 @@ export class AVLTreeNode< constructor(key: K, value?: V) { super(key, value); } + + override parent?: AVLTreeNode = undefined; + + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { + return this._left; + } + + override set left(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._left = v; + } + + override _right?: OptNodeOrNull> = undefined; + + override get right(): OptNodeOrNull> { + return this._right; + } + + override set right(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._right = v; + } } /** @@ -44,27 +67,9 @@ export class AVLTreeNode< * 6. Complex Insertions and Deletions: Due to rebalancing, these operations are more complex than in a regular BST. * 7. Path Length: The path length from the root to any leaf is longer compared to an unbalanced BST, but shorter than a linear chain of nodes. */ -export class AVLTree< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends AVLTreeNode = AVLTreeNode>, - TREE extends AVLTree = AVLTree< - K, - V, - R, - MK, - MV, - MR, - NODE, - AVLTreeNested - > - > - extends BST - implements IBinaryTree +export class AVLTree + extends BST + implements IBinaryTree { /** * This is a constructor function for an AVLTree class that initializes the tree with keys, nodes, @@ -77,7 +82,10 @@ export class AVLTree< * keys), `allowDuplicates` (a boolean indicating whether duplicate keys are allowed), and * `nodeBuilder` ( */ - constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: AVLTreeOptions) { + constructor( + keysNodesEntriesOrRaws: Iterable>> = [], + options?: AVLTreeOptions + ) { super([], options); if (keysNodesEntriesOrRaws) super.addMany(keysNodesEntriesOrRaws); } @@ -89,10 +97,10 @@ export class AVLTree< * @param {V} [value] - The "value" parameter is an optional parameter of type V. It represents the * value associated with the key in the node being created. * @returns The method is returning a new instance of the AVLTreeNode class, casted as the generic - * type NODE. + * type AVLTreeNode. */ - override createNode(key: K, value?: V): NODE { - return new AVLTreeNode(key, this._isMapMode ? undefined : value) as NODE; + override createNode(key: K, value?: V): AVLTreeNode { + return new AVLTreeNode(key, this._isMapMode ? undefined : value) as AVLTreeNode; } /** @@ -102,25 +110,25 @@ export class AVLTree< * being created. * @returns a new AVLTree object. */ - override createTree(options?: AVLTreeOptions): TREE { - return new AVLTree([], { + override createTree(options?: AVLTreeOptions) { + return new AVLTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, ...options - }) as TREE; + }); } /** * The function checks if the input is an instance of AVLTreeNode. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + override isNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is AVLTreeNode { return keyNodeEntryOrRaw instanceof AVLTreeNode; } @@ -130,14 +138,14 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept values of type `R`, `BTNRep`, 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(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V): boolean { if (keyNodeEntryOrRaw === null) return false; const inserted = super.add(keyNodeEntryOrRaw, value); if (inserted) this._balancePath(keyNodeEntryOrRaw); @@ -150,14 +158,14 @@ export class AVLTree< * * The function overrides the delete method in a TypeScript class, performs deletion, and then * balances the tree if necessary. - * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @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(keyNodeEntryOrRaw: BTNRep | R): BinaryTreeDeleteResult[] { + override delete(keyNodeEntryOrRaw: BTNRep> | R): BinaryTreeDeleteResult>[] { const deletedResults = super.delete(keyNodeEntryOrRaw); for (const { needBalanced } of deletedResults) { if (needBalanced) { @@ -180,23 +188,29 @@ export class AVLTree< return newTree; } + override clone() { + const cloned = this.createTree(); + this._clone(cloned); + return cloned; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) * * The `_swapProperties` function swaps the key, value, and height properties between two nodes in a * binary search tree. - * @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 | BSTNOptKeyOrNode} destNode - The `destNode` parameter is either an instance of - * `R` or an instance of `BSTNOptKeyOrNode`. + * @param {R | BSTNOptKeyOrNode>} srcNode - The `srcNode` parameter represents either a node + * object (`AVLTreeNode`) or a key-value pair (`R`) that is being swapped with another node. + * @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 | BSTNOptKeyOrNode, - destNode: R | BSTNOptKeyOrNode - ): NODE | undefined { + srcNode: R | BSTNOptKeyOrNode>, + destNode: R | BSTNOptKeyOrNode> + ): AVLTreeNode | undefined { const srcNodeEnsured = this.ensureNode(srcNode); const destNodeEnsured = this.ensureNode(destNode); @@ -226,12 +240,12 @@ export class AVLTree< * Space Complexity: O(1) * * The function calculates the balance factor of a node in a binary tree. - * @param {NODE} node - The parameter "node" is of type "NODE", which likely represents a node in a + * @param {AVLTreeNode} node - The parameter "node" is of type "AVLTreeNode", which likely represents a node in a * binary tree data structure. * @returns the balance factor of a given node. The balance factor is calculated by subtracting the * height of the left subtree from the height of the right subtree. */ - protected _balanceFactor(node: NODE): number { + protected _balanceFactor(node: AVLTreeNode): number { if (!node.right) // node has no right subtree return -node.height; @@ -247,9 +261,9 @@ export class AVLTree< * * The function updates the height of a node in a binary tree based on the heights of its left and * right children. - * @param {NODE} node - The parameter "node" represents a node in a binary tree data structure. + * @param {AVLTreeNode} node - The parameter "node" represents a node in a binary tree data structure. */ - protected _updateHeight(node: NODE): void { + protected _updateHeight(node: AVLTreeNode): void { if (!node.left && !node.right) node.height = 0; else if (!node.left) { const rightHeight = node.right ? node.right.height : 0; @@ -263,9 +277,9 @@ export class AVLTree< * Space Complexity: O(1) * * The `_balanceLL` function performs a left-left rotation to balance a binary search tree. - * @param {NODE} A - A is a node in a binary tree. + * @param {AVLTreeNode} A - A is a node in a binary tree. */ - protected _balanceLL(A: NODE): void { + protected _balanceLL(A: AVLTreeNode): void { const parentOfA = A.parent; const B = A.left; if (B !== null) A.parent = B; @@ -296,9 +310,9 @@ export class AVLTree< * Space Complexity: O(1) * * The `_balanceLR` function performs a left-right rotation to balance a binary tree. - * @param {NODE} A - A is a node in a binary tree. + * @param {AVLTreeNode} A - A is a node in a binary tree. */ - protected _balanceLR(A: NODE): void { + protected _balanceLR(A: AVLTreeNode): void { const parentOfA = A.parent; const B = A.left; let C = undefined; @@ -347,9 +361,9 @@ export class AVLTree< * Space Complexity: O(1) * * The function `_balanceRR` performs a right-right rotation to balance a binary tree. - * @param {NODE} A - A is a node in a binary tree. + * @param {AVLTreeNode} A - A is a node in a binary tree. */ - protected _balanceRR(A: NODE): void { + protected _balanceRR(A: AVLTreeNode): void { const parentOfA = A.parent; const B = A.right; if (B !== null) A.parent = B; @@ -385,9 +399,9 @@ export class AVLTree< * Space Complexity: O(1) * * The function `_balanceRL` performs a right-left rotation to balance a binary tree. - * @param {NODE} A - A is a node in a binary tree. + * @param {AVLTreeNode} A - A is a node in a binary tree. */ - protected _balanceRL(A: NODE): void { + protected _balanceRL(A: AVLTreeNode): void { const parentOfA = A.parent; const B = A.right; let C = undefined; @@ -436,10 +450,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 {BTNRep | R} node - The `node` parameter can be of type `R` or - * `BTNRep`. + * @param {BTNRep> | R} node - The `node` parameter can be of type `R` or + * `BTNRep>`. */ - protected _balancePath(node: BTNRep | 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++) { @@ -487,14 +501,14 @@ export class AVLTree< * * The function replaces an old node with a new node and sets the height of the new node to be the * same as the old node. - * @param {NODE} oldNode - The `oldNode` parameter represents the node that needs to be replaced in + * @param {AVLTreeNode} oldNode - The `oldNode` parameter represents the node that needs to be replaced in * the data structure. - * @param {NODE} newNode - The `newNode` parameter is the new node that will replace the `oldNode` in + * @param {AVLTreeNode} newNode - The `newNode` parameter is the new node that will replace the `oldNode` in * the data structure. * @returns The method is returning the result of calling the `_replaceNode` method from the * superclass, with the `oldNode` and `newNode` as arguments. */ - protected override _replaceNode(oldNode: NODE, newNode: NODE): NODE { + protected override _replaceNode(oldNode: AVLTreeNode, newNode: AVLTreeNode): AVLTreeNode { newNode.height = oldNode.height; return super._replaceNode(oldNode, newNode); diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index d3076b5..a175e84 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -6,10 +6,8 @@ * @license MIT License */ -import { +import type { BinaryTreeDeleteResult, - BinaryTreeNested, - BinaryTreeNodeNested, BinaryTreeOptions, BinaryTreePrintOptions, BTNEntry, @@ -23,7 +21,7 @@ import { NodeDisplayLayout, NodePredicate, OptNodeOrNull, - type RBTNColor, + RBTNColor, ToEntryFn } from '../../types'; import { IBinaryTree } from '../../interfaces'; @@ -35,46 +33,42 @@ import { DFSOperation, Range } from '../../common'; /** * Represents a node in a binary tree. * @template V - The type of data stored in the node. - * @template NODE - The type of the family relationship in the binary tree. + * @template BinaryTreeNode - The type of the family relationship in the binary tree. */ -export class BinaryTreeNode< - K = any, - V = any, - NODE extends BinaryTreeNode = BinaryTreeNode> -> { - key: K; - - value?: V; - - parent?: NODE; - +export class BinaryTreeNode { constructor(key: K, value?: V) { this.key = key; this.value = value; } - _left?: OptNodeOrNull; + key: K; - get left(): OptNodeOrNull { + value?: V; + + parent?: BinaryTreeNode = undefined; + + _left?: OptNodeOrNull> = undefined; + + get left(): OptNodeOrNull> { return this._left; } - set left(v: OptNodeOrNull) { + set left(v: OptNodeOrNull>) { if (v) { - v.parent = this as unknown as NODE; + v.parent = this as unknown as BinaryTreeNode; } this._left = v; } - _right?: OptNodeOrNull; + _right?: OptNodeOrNull> = undefined; - get right(): OptNodeOrNull { + get right(): OptNodeOrNull> { return this._right; } - set right(v: OptNodeOrNull) { + set right(v: OptNodeOrNull>) { if (v) { - v.parent = this as unknown as NODE; + v.parent = this; } this._right = v; } @@ -110,14 +104,13 @@ export class BinaryTreeNode< } get familyPosition(): FamilyPosition { - const that = this as unknown as NODE; if (!this.parent) { return this.left || this.right ? 'ROOT' : 'ISOLATED'; } - if (this.parent.left === that) { + if (this.parent.left === this) { return this.left || this.right ? 'ROOT_LEFT' : 'LEFT'; - } else if (this.parent.right === that) { + } else if (this.parent.right === this) { return this.left || this.right ? 'ROOT_RIGHT' : 'RIGHT'; } @@ -132,27 +125,9 @@ export class BinaryTreeNode< * 4. Subtrees: Each child of a node forms the root of a subtree. * 5. Leaf Nodes: Nodes without children are leaves. */ -export class BinaryTree< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends BinaryTreeNode = BinaryTreeNode>, - TREE extends BinaryTree = BinaryTree< - K, - V, - R, - MK, - MV, - MR, - NODE, - BinaryTreeNested - > - > +export class BinaryTree extends IterableEntryBase - implements IBinaryTree + implements IBinaryTree { iterationType: IterationType = 'ITERATIVE'; @@ -160,12 +135,15 @@ export class BinaryTree< * The constructor initializes a binary tree with optional options and adds keys, nodes, entries, or * raw data if provided. * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor - * is an iterable that can contain elements of type `BTNRep` or `R`. It is + * 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(keysNodesEntriesOrRaws: Iterable | R> = [], options?: BinaryTreeOptions) { + constructor( + keysNodesEntriesOrRaws: Iterable> | R> = [], + options?: BinaryTreeOptions + ) { super(); if (options) { const { iterationType, toEntryFn, isMapMode } = options; @@ -190,9 +168,9 @@ export class BinaryTree< return this._store; } - protected _root?: OptNodeOrNull; + protected _root?: OptNodeOrNull>; - get root(): OptNodeOrNull { + get root(): OptNodeOrNull> { return this._root; } @@ -202,9 +180,9 @@ export class BinaryTree< return this._size; } - protected _NIL: NODE = new BinaryTreeNode(NaN as K) as unknown as NODE; + protected _NIL: BinaryTreeNode = new BinaryTreeNode(NaN as K) as unknown as BinaryTreeNode; - get NIL(): NODE { + get NIL(): BinaryTreeNode { return this._NIL; } @@ -224,10 +202,10 @@ export class BinaryTree< * not required to be provided when calling the function. If a `value` is provided, it should be of * type `V`, which is the type of the value associated with the node. * @returns A new BinaryTreeNode instance with the provided key and value is being returned, casted - * as NODE. + * as BinaryTreeNode. */ - createNode(key: K, value?: V): NODE { - return new BinaryTreeNode(key, this._isMapMode ? undefined : value) as NODE; + createNode(key: K, value?: V): BinaryTreeNode { + return new BinaryTreeNode(key, this._isMapMode ? undefined : value) as BinaryTreeNode; } /** @@ -238,56 +216,13 @@ export class BinaryTree< * of properties * @returns A new instance of a binary tree with the specified options is being returned. */ - createTree(options?: BinaryTreeOptions): TREE { - return new BinaryTree([], { + createTree(options?: BinaryTreeOptions) { + return new BinaryTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, toEntryFn: this._toEntryFn, ...options - }) as TREE; - } - - /** - * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object - * or returns null. - * @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 `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 `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. - */ - protected _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep | R, - value?: V - ): [OptNodeOrNull, V | undefined] { - if (keyNodeEntryOrRaw === undefined) return [undefined, undefined]; - if (keyNodeEntryOrRaw === null) return [null, undefined]; - - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - 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.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value]; - - return [undefined, undefined]; + }); } /** @@ -296,8 +231,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 {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` - * parameter in the `ensureNode` function can be of type `BTNRep` 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 @@ -306,9 +241,9 @@ export class BinaryTree< * conditions specified in the code snippet. */ ensureNode( - keyNodeEntryOrRaw: BTNRep | R, + keyNodeEntryOrRaw: BTNRep> | R, iterationType: IterationType = this.iterationType - ): OptNodeOrNull { + ): OptNodeOrNull> { if (keyNodeEntryOrRaw === null) return null; if (keyNodeEntryOrRaw === undefined) return; if (keyNodeEntryOrRaw === this._NIL) return; @@ -332,7 +267,7 @@ export class BinaryTree< /** * The function isNode checks if the input is an instance of BinaryTreeNode. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * @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. @@ -341,62 +276,64 @@ export class BinaryTree< * it is not an instance of `BinaryTreeNode`, the function returns `false`, indicating that the input * is not a node. */ - isNode(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + isNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BinaryTreeNode { return keyNodeEntryOrRaw instanceof BinaryTreeNode; } /** * The function `isRaw` checks if the input parameter is of type `R` by verifying if it is an object. - * @param {BTNRep | R} keyNodeEntryOrRaw - BTNRep | R + * @param {BTNRep> | R} keyNodeEntryOrRaw - BTNRep> | R * @returns The function `isRaw` is checking if the `keyNodeEntryOrRaw` parameter is of type `R` by * checking if it is an object. If the parameter is an object, the function will return `true`, * indicating that it is of type `R`. */ - isRaw(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is R { + isRaw(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is R { return this._toEntryFn !== undefined && typeof keyNodeEntryOrRaw === 'object'; } /** * The function `isRealNode` checks if a given input is a valid node in a binary tree. - * @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 + * @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 `BinaryTreeNode` type by verifying if it is not equal * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + isRealNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BinaryTreeNode { 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 {BTNRep | R} keyNodeEntryOrRaw - The parameter + * @param {BTNRep> | 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 + * V, BinaryTreeNode>` 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 * `keyNodeEntryOrRaw` is either `null` or a real node, and returns `true` if it is a node or * `null`, and `false` otherwise. */ - isRealNodeOrNull(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE | null { + isRealNodeOrNull( + keyNodeEntryOrRaw: BTNRep> | R + ): keyNodeEntryOrRaw is BinaryTreeNode | 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 {BTNRep | R} keyNodeEntryOrRaw - BTNRep | R + * @param {BTNRep> | R} keyNodeEntryOrRaw - BTNRep> | R * @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(keyNodeEntryOrRaw: BTNRep | R): boolean { + isNIL(keyNodeEntryOrRaw: BTNRep> | R): boolean { return keyNodeEntryOrRaw === this._NIL; } isRange( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate | Range + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate> | Range ): keyNodeEntryRawOrPredicate is Range { return keyNodeEntryRawOrPredicate instanceof Range; } @@ -404,14 +341,14 @@ export class BinaryTree< /** * The function determines whether a given key, node, entry, or raw data is a leaf node in a binary * tree. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `BTNRep` 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 * `keyNodeEntryOrRaw` is a leaf node in a binary tree. */ - isLeaf(keyNodeEntryOrRaw: BTNRep | R): boolean { + isLeaf(keyNodeEntryOrRaw: BTNRep> | R): boolean { keyNodeEntryOrRaw = this.ensureNode(keyNodeEntryOrRaw); if (keyNodeEntryOrRaw === undefined) return false; if (keyNodeEntryOrRaw === null) return true; @@ -421,14 +358,14 @@ export class BinaryTree< /** * The function `isEntry` checks if the input is a BTNEntry object by verifying if it is an array * with a length of 2. - * @param {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` - * parameter in the `isEntry` function can be of type `BTNRep` or type `R`. + * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is BTNEntry { + isEntry(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BTNEntry { return Array.isArray(keyNodeEntryOrRaw) && keyNodeEntryOrRaw.length === 2; } @@ -454,7 +391,7 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - The `add` method you provided + * @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 @@ -465,7 +402,7 @@ 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(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + add(keyNodeEntryOrRaw: BTNRep> | R, value?: V): boolean { const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); if (newNode === undefined) return false; @@ -477,8 +414,8 @@ export class BinaryTree< return true; } - const queue = new Queue([this._root]); - let potentialParent: NODE | undefined; // Record the parent node of the potential insertion location + const queue = new Queue>([this._root]); + let potentialParent: BinaryTreeNode | undefined; // Record the parent node of the potential insertion location while (queue.size > 0) { const cur = queue.shift(); @@ -530,7 +467,7 @@ export class BinaryTree< * each insertion was successful. * @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 - * `BTNRep` 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 * `keysNodesEntriesOrRaws` parameter. If provided, the function will iterate over the values and @@ -539,7 +476,10 @@ export class BinaryTree< * 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(keysNodesEntriesOrRaws: Iterable | R>, values?: Iterable): boolean[] { + addMany( + keysNodesEntriesOrRaws: Iterable> | R>, + values?: Iterable + ): boolean[] { // TODO not sure addMany not be run multi times const inserted: boolean[] = []; @@ -570,9 +510,9 @@ export class BinaryTree< * * The `merge` function in TypeScript merges another binary tree into the current tree by adding all * elements from the other tree. - * @param anotherTree - `BinaryTree` + * @param anotherTree - `BinaryTree, TREE>` */ - merge(anotherTree: BinaryTree) { + merge(anotherTree: BinaryTree>) { this.addMany(anotherTree, []); } @@ -583,12 +523,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 keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the `refill` - * method can accept an iterable containing a mix of `BTNRep` objects or `R` + * 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(keysNodesEntriesOrRaws: Iterable | R>, values?: Iterable): void { + refill( + keysNodesEntriesOrRaws: Iterable> | R>, + values?: Iterable + ): void { this.clear(); this.addMany(keysNodesEntriesOrRaws, values); } @@ -599,7 +542,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 {BTNRep | R} keyNodeEntryOrRaw + * @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 @@ -608,16 +551,16 @@ 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(keyNodeEntryOrRaw: BTNRep | R): BinaryTreeDeleteResult[] { - const deletedResult: BinaryTreeDeleteResult[] = []; + delete(keyNodeEntryOrRaw: BTNRep> | R): BinaryTreeDeleteResult>[] { + const deletedResult: BinaryTreeDeleteResult>[] = []; if (!this._root) return deletedResult; const curr = this.getNode(keyNodeEntryOrRaw); if (!curr) return deletedResult; - const parent: NODE | undefined = curr?.parent; - let needBalanced: NODE | undefined; - let orgCurrent: NODE | undefined = curr; + const parent: BinaryTreeNode | undefined = curr?.parent; + let needBalanced: BinaryTreeNode | undefined; + let orgCurrent: BinaryTreeNode | undefined = curr; if (!curr.left && !curr.right && !parent) { this._setRoot(undefined); @@ -659,15 +602,15 @@ export class BinaryTree< * * The `search` function in TypeScript performs a depth-first or breadth-first search on a tree * structure based on a given predicate or key, with options to return multiple results or just one. - * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate - The + * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The * `keyNodeEntryRawOrPredicate` parameter in the `search` function can accept three types of values: * @param [onlyOne=false] - The `onlyOne` parameter in the `search` function is a boolean flag that * determines whether the search should stop after finding the first matching node. If `onlyOne` is * set to `true`, the search will return as soon as a matching node is found. If `onlyOne` is * @param {C} callback - The `callback` parameter in the `search` function is a callback function * that will be called on each node that matches the search criteria. It is of type `C`, which - * extends `NodeCallback`. The default value for `callback` is `this._DEFAULT_NODE_CALLBACK` if - * @param {BTNRep | R} startNode - The `startNode` parameter in the `search` function is + * extends `NodeCallback>`. The default value for `callback` is `this._DEFAULT_NODE_CALLBACK` if + * @param {BTNRep> | R} startNode - The `startNode` parameter in the `search` function is * used to specify the node from which the search operation should begin. It represents the starting * point in the binary tree where the search will be performed. If no specific `startNode` is * provided, the search operation will start from the root @@ -677,11 +620,11 @@ export class BinaryTree< * @returns The `search` function returns an array of values that match the provided criteria based * on the search algorithm implemented within the function. */ - search>( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, + search>>( + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, onlyOne = false, callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { if (keyNodeEntryRawOrPredicate === undefined) return []; @@ -693,7 +636,7 @@ export class BinaryTree< const ans: ReturnType[] = []; if (iterationType === 'RECURSIVE') { - const dfs = (cur: NODE) => { + const dfs = (cur: BinaryTreeNode) => { if (predicate(cur)) { ans.push(callback(cur)); if (onlyOne) return; @@ -728,12 +671,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 {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * @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 * `keyNodeEntryRawOrPredicate` parameter. If `onlyOne` is set to `true`, the function will - * @param {BTNRep | R} startNode - The `startNode` parameter in the + * @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 @@ -744,11 +687,11 @@ export class BinaryTree< * based on the input parameters and the iteration type specified. */ getNodes( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, onlyOne = false, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType - ): NODE[] { + ): BinaryTreeNode[] { return this.search(keyNodeEntryRawOrPredicate, onlyOne, node => node, startNode, iterationType); } @@ -758,10 +701,10 @@ export class BinaryTree< * * The `getNode` function retrieves a node based on the provided key, node, entry, raw data, or * predicate. - * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * @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 {BTNRep | R} startNode - The `startNode` 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. @@ -773,10 +716,10 @@ export class BinaryTree< * or `null` if no matching node is found. */ getNode( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, - startNode: BTNRep | R = this._root, + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType - ): OptNodeOrNull { + ): OptNodeOrNull> { return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType)[0] ?? null; } @@ -786,10 +729,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 {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate * - The `keyNodeEntryRawOrPredicate` parameter in the `get` method can accept one of the * following types: - * @param {BTNRep | R} startNode - The `startNode` 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`). @@ -803,8 +746,8 @@ export class BinaryTree< * `undefined`. */ override get( - keyNodeEntryRawOrPredicate: BTNRep | R, - startNode: BTNRep | R = this._root, + keyNodeEntryRawOrPredicate: BTNRep> | R, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): V | undefined { if (this._isMapMode) { @@ -821,10 +764,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 {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate + * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate * - The `keyNodeEntryRawOrPredicate` parameter in the `override has` method can accept one of * the following types: - * @param {BTNRep | R} startNode - The `startNode` 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 @@ -837,8 +780,8 @@ export class BinaryTree< * Otherwise, it returns `false`. */ override has( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, - startNode: BTNRep | R = this._root, + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): boolean { return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType).length > 0; @@ -874,7 +817,7 @@ export class BinaryTree< * * The function checks if a binary tree is perfectly balanced by comparing its minimum height with * its height. - * @param {BTNRep | R} startNode - The `startNode` 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 @@ -883,7 +826,7 @@ export class BinaryTree< * height plus 1 is greater than or equal to the height of the tree, then it is considered perfectly * balanced and */ - isPerfectlyBalanced(startNode: BTNRep | R = this._root): boolean { + isPerfectlyBalanced(startNode: BTNRep> | R = this._root): boolean { return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode); } @@ -893,7 +836,7 @@ export class BinaryTree< * * The function `isBST` in TypeScript checks if a binary search tree is valid using either recursive * or iterative methods. - * @param {BTNRep | R} startNode - The `startNode` 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 @@ -905,13 +848,16 @@ 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(startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType): boolean { + isBST( + startNode: BTNRep> | R = this._root, + iterationType: IterationType = this.iterationType + ): boolean { // TODO there is a bug startNode = this.ensureNode(startNode); if (!startNode) return true; if (iterationType === 'RECURSIVE') { - const dfs = (cur: OptNodeOrNull, 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; @@ -926,7 +872,7 @@ export class BinaryTree< const stack = []; let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER; // @ts-ignore - let curr: OptNodeOrNull = startNode; + let curr: OptNodeOrNull> = startNode; while (this.isRealNode(curr) || stack.length > 0) { while (this.isRealNode(curr)) { stack.push(curr); @@ -951,10 +897,10 @@ export class BinaryTree< * Space Complexity: O(1) * * The `getDepth` function calculates the depth between two nodes in a binary tree. - * @param {BTNRep | 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 `startNode` node. - * @param {BTNRep | R} startNode - The `startNode` parameter in the + * @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 `startNode` is set to the root of the binary @@ -962,7 +908,10 @@ export class BinaryTree< * `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: BTNRep | R, startNode: BTNRep | R = this._root): number { + getDepth( + dist: BTNRep> | R, + startNode: BTNRep> | R = this._root + ): number { let distEnsured = this.ensureNode(dist); const beginRootEnsured = this.ensureNode(startNode); let depth = 0; @@ -982,7 +931,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 {BTNRep | R} startNode - The `startNode` 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. @@ -993,12 +942,15 @@ 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(startNode: BTNRep | R = this._root, iterationType: IterationType = this.iterationType): number { + 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: OptNodeOrNull): number => { + const _getMaxHeight = (cur: OptNodeOrNull>): number => { if (!this.isRealNode(cur)) return -1; const leftHeight = _getMaxHeight(cur.left); const rightHeight = _getMaxHeight(cur.right); @@ -1007,7 +959,7 @@ export class BinaryTree< return _getMaxHeight(startNode); } else { - const stack: { node: NODE; depth: number }[] = [{ node: startNode, depth: 0 }]; + const stack: { node: BinaryTreeNode; depth: number }[] = [{ node: startNode, depth: 0 }]; let maxHeight = 0; while (stack.length > 0) { @@ -1029,7 +981,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 {BTNRep | R} startNode - The `startNode` 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 @@ -1042,14 +994,14 @@ export class BinaryTree< * a stack) based on the `iterationType` parameter. */ getMinHeight( - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): number { startNode = this.ensureNode(startNode); if (!startNode) return -1; if (iterationType === 'RECURSIVE') { - const _getMinHeight = (cur: OptNodeOrNull): 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); @@ -1059,10 +1011,10 @@ export class BinaryTree< return _getMinHeight(startNode); } else { - const stack: NODE[] = []; - let node: OptNodeOrNull = startNode, - last: OptNodeOrNull = null; - const depths: Map = new Map(); + const stack: BinaryTreeNode[] = []; + let node: OptNodeOrNull> = startNode, + last: OptNodeOrNull> = null; + const depths: Map, number> = new Map(); while (stack.length > 0 || node) { if (this.isRealNode(node)) { @@ -1097,7 +1049,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 {BTNRep | 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 @@ -1107,8 +1059,8 @@ export class BinaryTree< * array is either in reverse order or in the original order based on the value of the `isReverse` * parameter. */ - getPathToRoot>>( - beginNode: BTNRep | R, + getPathToRoot>>>( + beginNode: BTNRep> | R, callback: C = this._DEFAULT_NODE_CALLBACK as C, isReverse = false ): ReturnType[] { @@ -1135,7 +1087,7 @@ export class BinaryTree< * @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_NODE_CALLBACK` if not specified. - * @param {BTNRep | R} startNode - The `startNode` parameter in the + * @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 @@ -1147,9 +1099,9 @@ export class BinaryTree< * `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>>( + getLeftMost>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); @@ -1158,7 +1110,7 @@ export class BinaryTree< if (!this.isRealNode(startNode)) return callback(startNode); if (iterationType === 'RECURSIVE') { - const dfs = (cur: NODE): NODE => { + const dfs = (cur: BinaryTreeNode): BinaryTreeNode => { if (!this.isRealNode(cur.left)) return cur; return dfs(cur.left); }; @@ -1166,7 +1118,7 @@ export class BinaryTree< return callback(dfs(startNode)); } else { // Indirect implementation of iteration using tail recursion optimization - const dfs = trampoline((cur: NODE): NODE => { + const dfs = trampoline((cur: BinaryTreeNode): BinaryTreeNode => { if (!this.isRealNode(cur.left)) return cur; return dfs.cont(cur.left); }); @@ -1182,10 +1134,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 `NodeCallback>`, + * 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 {BTNRep | R} startNode - The `startNode` 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 @@ -1197,9 +1149,9 @@ export class BinaryTree< * the binary tree structure, determined based on the specified iteration type ('RECURSIVE' or * other). */ - getRightMost>>( + getRightMost>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); @@ -1207,7 +1159,7 @@ export class BinaryTree< if (!startNode) return callback(startNode); if (iterationType === 'RECURSIVE') { - const dfs = (cur: NODE): NODE => { + const dfs = (cur: BinaryTreeNode): BinaryTreeNode => { if (!this.isRealNode(cur.right)) return cur; return dfs(cur.right); }; @@ -1215,7 +1167,7 @@ export class BinaryTree< return callback(dfs(startNode)); } else { // Indirect implementation of iteration using tail recursion optimization - const dfs = trampoline((cur: NODE) => { + const dfs = trampoline((cur: BinaryTreeNode) => { if (!this.isRealNode(cur.right)) return cur; return dfs.cont(cur.right); }); @@ -1230,16 +1182,16 @@ export class BinaryTree< * * The function `getPredecessor` in TypeScript returns the predecessor node of a given node in a * binary tree. - * @param {NODE} node - The `getPredecessor` function you provided seems to be attempting to find the + * @param {BinaryTreeNode} node - The `getPredecessor` function you provided seems to be attempting to find the * predecessor of a given node in a binary tree. However, there seems to be a logical issue in the * while loop condition that might cause an infinite loop. - * @returns The `getPredecessor` function returns the predecessor node of the input `NODE` parameter. + * @returns The `getPredecessor` function returns the predecessor node of the input `BinaryTreeNode` parameter. * If the left child of the input node exists, it traverses to the rightmost node of the left subtree * to find the predecessor. If the left child does not exist, it returns the input node itself. */ - getPredecessor(node: NODE): NODE { + getPredecessor(node: BinaryTreeNode): BinaryTreeNode { if (this.isRealNode(node.left)) { - let predecessor: OptNodeOrNull = 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; @@ -1257,14 +1209,14 @@ export class BinaryTree< * * The function `getSuccessor` in TypeScript returns the next node in an in-order traversal of a * binary tree. - * @param {K | NODE | null} [x] - The `getSuccessor` function takes a parameter `x`, which can be of - * type `K`, `NODE`, or `null`. + * @param {K | BinaryTreeNode | null} [x] - The `getSuccessor` function takes a parameter `x`, which can be of + * type `K`, `BinaryTreeNode`, or `null`. * @returns The `getSuccessor` function returns the successor node of the input node `x`. If `x` has * a right child, the function returns the leftmost node in the right subtree of `x`. If `x` does not * 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): OptNodeOrNull { + getSuccessor(x?: K | BinaryTreeNode | null): OptNodeOrNull> { x = this.ensureNode(x); if (!this.isRealNode(x)) return undefined; @@ -1272,7 +1224,7 @@ export class BinaryTree< return this.getLeftMost(node => node, x.right); } - let y: OptNodeOrNull = x.parent; + let y: OptNodeOrNull> = x.parent; while (this.isRealNode(y) && x === y.right) { x = y; y = y.parent; @@ -1280,17 +1232,17 @@ export class BinaryTree< return y; } - dfs>( + dfs>>( callback?: C, pattern?: DFSOrderPattern, - startNode?: BTNRep | R, + startNode?: BTNRep> | R, iterationType?: IterationType ): ReturnType[]; - dfs>( + dfs | null>>( callback?: C, pattern?: DFSOrderPattern, - startNode?: BTNRep | R, + startNode?: BTNRep> | R, iterationType?: IterationType, includeNull?: boolean ): ReturnType[]; @@ -1302,12 +1254,12 @@ 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 - * `NodeCallback` interface with a type parameter of `OptNodeOrNull`. It has a default value of + * `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 {BTNRep | R} startNode - The `startNode` 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 `BTNRep` object representing a key, node, or entry in the binary tree map, * or it can be a @@ -1321,10 +1273,10 @@ 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>>( + dfs>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { @@ -1333,16 +1285,16 @@ export class BinaryTree< return this._dfs(callback, pattern, startNode, iterationType, includeNull); } - bfs>( + bfs>>( callback?: C, - startNode?: BTNRep | R, + startNode?: BTNRep> | R, iterationType?: IterationType, includeNull?: false ): ReturnType[]; - bfs>( + bfs | null>>( callback?: C, - startNode?: BTNRep | R, + startNode?: BTNRep> | R, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1355,8 +1307,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 `NodeCallback` type, which takes a parameter of type `NODE` or `null`. - * @param {BTNRep | R} startNode - The `startNode` parameter in the `bfs` + * that extends the `NodeCallback` type, which takes a parameter of type `BinaryTreeNode` 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 @@ -1370,19 +1322,21 @@ 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>( + bfs | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; - const ans: ReturnType>[] = []; + const ans: ReturnType>>[] = []; if (iterationType === 'RECURSIVE') { - const queue: Queue> = new Queue>([startNode]); + const queue: Queue>> = new Queue>>([ + startNode + ]); const dfs = (level: number) => { if (queue.size === 0) return; @@ -1403,7 +1357,7 @@ export class BinaryTree< dfs(0); } else { - const queue = new Queue>([startNode]); + const queue = new Queue>>([startNode]); while (queue.size > 0) { const levelSize = queue.size; @@ -1432,7 +1386,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 {BTNRep | R} startNode - The `startNode` 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 @@ -1442,17 +1396,17 @@ 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>( + leaves | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { startNode = this.ensureNode(startNode); - const leaves: ReturnType>[] = []; + const leaves: ReturnType>>[] = []; if (!this.isRealNode(startNode)) return []; if (iterationType === 'RECURSIVE') { - const dfs = (cur: NODE) => { + const dfs = (cur: BinaryTreeNode) => { if (this.isLeaf(cur)) { leaves.push(callback(cur)); } @@ -1479,16 +1433,16 @@ export class BinaryTree< return leaves; } - listLevels>( + listLevels>>( callback?: C, - startNode?: BTNRep | R, + startNode?: BTNRep> | R, iterationType?: IterationType, includeNull?: false ): ReturnType[][]; - listLevels>( + listLevels | null>>( callback?: C, - startNode?: BTNRep | R, + startNode?: BTNRep> | R, iterationType?: IterationType, includeNull?: true ): ReturnType[][]; @@ -1502,7 +1456,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 {BTNRep | R} startNode - The `startNode` 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. @@ -1517,9 +1471,9 @@ 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>( + listLevels | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[][] { @@ -1528,7 +1482,7 @@ export class BinaryTree< if (!startNode) return levelsNodes; if (iterationType === 'RECURSIVE') { - const _recursive = (node: NODE | null, level: number) => { + const _recursive = (node: BinaryTreeNode | null, level: number) => { if (!levelsNodes[level]) levelsNodes[level] = []; levelsNodes[level].push(callback(node)); if (includeNull) { @@ -1542,7 +1496,7 @@ export class BinaryTree< _recursive(startNode, 0); } else { - const stack: [NODE | null, number][] = [[startNode, 0]]; + const stack: [BinaryTreeNode | null, number][] = [[startNode, 0]]; while (stack.length > 0) { const head = stack.pop()!; @@ -1572,11 +1526,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 - * `NodeCallback` 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 {BTNRep | R} startNode - The `startNode` 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 @@ -1584,19 +1538,19 @@ export class BinaryTree< * provided callback function to each node in the binary tree in the specified order pattern (IN, * PRE, or POST). */ - morris>( + morris>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep | R = this._root + startNode: BTNRep> | R = this._root ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; - const ans: ReturnType>[] = []; + const ans: ReturnType>>[] = []; - let cur: OptNodeOrNull = startNode; - const _reverseEdge = (node: OptNodeOrNull) => { - let pre: OptNodeOrNull = null; - let next: OptNodeOrNull = 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; @@ -1605,9 +1559,9 @@ export class BinaryTree< } return pre; }; - const _printEdge = (node: OptNodeOrNull) => { - const tail: OptNodeOrNull = _reverseEdge(node); - let cur: OptNodeOrNull = tail; + const _printEdge = (node: OptNodeOrNull>) => { + const tail: OptNodeOrNull> = _reverseEdge(node); + let cur: OptNodeOrNull> = tail; while (cur) { ans.push(callback(cur)); cur = cur.right; @@ -1681,8 +1635,13 @@ export class BinaryTree< * original tree using breadth-first search (bfs), and adds the nodes to the new tree. If a node in * the original tree is null, a null node is added to the cloned tree. If a node */ - clone(): TREE { + clone() { const cloned = this.createTree(); + this._clone(cloned); + return cloned; + } + + protected _clone(cloned: BinaryTree) { this.bfs( node => { if (node === null) cloned.add(null); @@ -1696,7 +1655,6 @@ export class BinaryTree< true ); if (this._isMapMode) cloned._store = this._store; - return cloned; } /** @@ -1764,7 +1722,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 {BTNRep | R} startNode - The `startNode` 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 @@ -1776,7 +1734,10 @@ 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(startNode: BTNRep | 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 }; startNode = this.ensureNode(startNode); let output = ''; @@ -1786,8 +1747,8 @@ export class BinaryTree< if (opts.isShowNull) output += `N for null\n`; if (opts.isShowRedBlackNIL) output += `S for Sentinel Node(NIL)\n`; - const display = (root: OptNodeOrNull): void => { - const [lines, ,] = this._displayAux(root, opts); + const display = (root: OptNodeOrNull>): void => { + const [lines] = this._displayAux(root, opts); let paragraph = ''; for (const line of lines) { paragraph += line + '\n'; @@ -1809,15 +1770,58 @@ 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 {BTNRep | R} startNode - The `startNode` 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, startNode: BTNRep | R = this._root) { + override print(options?: BinaryTreePrintOptions, startNode: BTNRep> | R = this._root) { console.log(this.toVisual(startNode, options)); } + /** + * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object + * or returns null. + * @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 `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 `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. + */ + protected _keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep> | R, + value?: V + ): [OptNodeOrNull>, V | undefined] { + if (keyNodeEntryOrRaw === undefined) return [undefined, undefined]; + if (keyNodeEntryOrRaw === null) return [null, undefined]; + + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; + + 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.isRaw(keyNodeEntryOrRaw)) { + const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue]; + } + + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value]; + + return [undefined, undefined]; + } + /** * Time complexity: O(n) * Space complexity: O(n) @@ -1826,11 +1830,11 @@ 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 `NodeCallback>`. 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 {BTNRep | R} startNode - The `startNode` 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 `BTNRep` object or a reference to the root node * of the tree. If no specific @@ -1860,26 +1864,26 @@ export class BinaryTree< * @returns The function `_dfs` returns an array of the return type of the callback function provided * as input. */ - protected _dfs>>( + protected _dfs>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType, includeNull = false, - shouldVisitLeft: (node: OptNodeOrNull) => boolean = node => !!node, - shouldVisitRight: (node: OptNodeOrNull) => boolean = node => !!node, - shouldVisitRoot: (node: OptNodeOrNull) => 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: OptNodeOrNull) => boolean = node => this.isRealNodeOrNull(node) + shouldProcessRoot: (node: OptNodeOrNull>) => boolean = node => this.isRealNodeOrNull(node) ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; const ans: ReturnType[] = []; if (iterationType === 'RECURSIVE') { - const dfs = (node: OptNodeOrNull) => { + const dfs = (node: OptNodeOrNull>) => { if (!shouldVisitRoot(node)) return; const visitLeft = () => { @@ -1910,15 +1914,15 @@ export class BinaryTree< dfs(startNode); } else { - const stack: DFSStackItem[] = [{ opt: DFSOperation.VISIT, node: startNode }]; + const stack: DFSStackItem>[] = [{ opt: DFSOperation.VISIT, node: startNode }]; - const pushLeft = (cur: DFSStackItem) => { + const pushLeft = (cur: DFSStackItem>) => { if (shouldVisitLeft(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.left }); }; - const pushRight = (cur: DFSStackItem) => { + const pushRight = (cur: DFSStackItem>) => { if (shouldVisitRight(cur.node)) stack.push({ opt: DFSOperation.VISIT, node: cur.node?.right }); }; - const pushRoot = (cur: DFSStackItem) => { + const pushRoot = (cur: DFSStackItem>) => { if (shouldVisitRoot(cur.node)) stack.push({ opt: DFSOperation.PROCESS, node: cur.node }); }; @@ -1972,8 +1976,8 @@ export class BinaryTree< if (!node) return; if (this.iterationType === 'ITERATIVE') { - const stack: OptNodeOrNull[] = []; - let current: OptNodeOrNull = node; + const stack: OptNodeOrNull>[] = []; + let current: OptNodeOrNull> = node; while (current || stack.length > 0) { while (this.isRealNode(current)) { @@ -2016,7 +2020,7 @@ export class BinaryTree< * information about how to display a node in a binary tree. The `NodeDisplayLayout` consists of four * elements: */ - protected _displayAux(node: OptNodeOrNull, options: BinaryTreePrintOptions): NodeDisplayLayout { + protected _displayAux(node: OptNodeOrNull>, options: BinaryTreePrintOptions): NodeDisplayLayout { const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options; const emptyDisplayLayout = [['─'], 1, 0, 0]; @@ -2084,24 +2088,27 @@ export class BinaryTree< } } - protected _DEFAULT_NODE_CALLBACK = (node: OptNodeOrNull) => (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 {BTNRep | R} srcNode - The `srcNode` parameter in the + * @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 {BTNRep | 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 `BTNRep` 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: BTNRep | R, destNode: BTNRep | R): NODE | undefined { + protected _swapProperties( + srcNode: BTNRep> | R, + destNode: BTNRep> | R + ): BinaryTreeNode | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -2127,16 +2134,16 @@ export class BinaryTree< * Space Complexity: O(1) * * The _replaceNode function replaces an old node with a new node in a binary tree structure. - * @param {NODE} oldNode - The `oldNode` parameter represents the node that you want to replace in a + * @param {BinaryTreeNode} oldNode - The `oldNode` parameter represents the node that you want to replace in a * tree data structure. - * @param {NODE} newNode - The `newNode` parameter in the `_replaceNode` function represents the node + * @param {BinaryTreeNode} newNode - The `newNode` parameter in the `_replaceNode` function represents the node * that will replace the `oldNode` in a tree data structure. This function is responsible for * updating the parent, left child, right child, and root (if necessary) references when replacing a * node in the tree. * @returns The method `_replaceNode` is returning the `newNode` that was passed as a parameter after * replacing the `oldNode` with it in the binary tree structure. */ - protected _replaceNode(oldNode: NODE, newNode: NODE): NODE { + protected _replaceNode(oldNode: BinaryTreeNode, newNode: BinaryTreeNode): BinaryTreeNode { if (oldNode.parent) { if (oldNode.parent.left === oldNode) { oldNode.parent.left = newNode; @@ -2160,10 +2167,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 `OptNodeOrNull`, which means - * it can either be an optional `NODE` type or `null`. + * @param v - The parameter `v` in the `_setRoot` method is of type `OptNodeOrNull>`, which means + * it can either be an optional `BinaryTreeNode` type or `null`. */ - protected _setRoot(v: OptNodeOrNull) { + protected _setRoot(v: OptNodeOrNull>) { if (v) { v.parent = undefined; } @@ -2176,34 +2183,36 @@ 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 {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate - The + * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The * `_ensurePredicate` method in the provided code snippet is responsible for ensuring that the input * parameter `keyNodeEntryRawOrPredicate` is transformed into a valid predicate function that can be * used for filtering nodes in a binary tree. - * @returns A NodePredicate function is being returned. + * @returns A NodePredicate> function is being returned. */ protected _ensurePredicate( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate - ): NodePredicate { + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate> + ): NodePredicate> { if (keyNodeEntryRawOrPredicate === null || keyNodeEntryRawOrPredicate === undefined) - return (node: NODE) => (node ? false : false); + return (node: BinaryTreeNode) => (node ? false : false); if (this._isPredicate(keyNodeEntryRawOrPredicate)) return keyNodeEntryRawOrPredicate; - if (this.isRealNode(keyNodeEntryRawOrPredicate)) return (node: NODE) => node === keyNodeEntryRawOrPredicate; + if (this.isRealNode(keyNodeEntryRawOrPredicate)) + return (node: BinaryTreeNode) => node === keyNodeEntryRawOrPredicate; if (this.isEntry(keyNodeEntryRawOrPredicate)) { const [key] = keyNodeEntryRawOrPredicate; - return (node: NODE) => node.key === key; + return (node: BinaryTreeNode) => node.key === key; } - if (this.isKey(keyNodeEntryRawOrPredicate)) return (node: NODE) => node.key === keyNodeEntryRawOrPredicate; + if (this.isKey(keyNodeEntryRawOrPredicate)) + return (node: BinaryTreeNode) => node.key === keyNodeEntryRawOrPredicate; if (this._toEntryFn) { const [key] = this._toEntryFn(keyNodeEntryRawOrPredicate); - return (node: NODE) => node.key === key; + return (node: BinaryTreeNode) => node.key === key; } - return (node: NODE) => node.key === keyNodeEntryRawOrPredicate; + return (node: BinaryTreeNode) => node.key === keyNodeEntryRawOrPredicate; } /** @@ -2213,12 +2222,12 @@ export class BinaryTree< * 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 `_isPredicate` is checking if `p` is a function that - * satisfies the type `NodePredicate`. + * 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 _isPredicate(p: any): p is NodePredicate { + protected _isPredicate(p: any): p is NodePredicate> { return typeof p === 'function'; } @@ -2228,14 +2237,14 @@ export class BinaryTree< * * The function `_extractKey` 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 `_extractKey` 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 ` + * @param {BTNRep> | R} keyNodeEntryOrRaw - The `_extractKey` 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 `BinaryTreeNode`, and ` * @returns The `_extractKey` 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 _extractKey(keyNodeEntryOrRaw: BTNRep | R): K | null | undefined { + protected _extractKey(keyNodeEntryOrRaw: BTNRep> | R): K | null | undefined { if (keyNodeEntryOrRaw === null) return null; if (keyNodeEntryOrRaw === undefined) return; if (keyNodeEntryOrRaw === this._NIL) return; diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 976dd6b..823127e 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -5,9 +5,7 @@ * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ -import { - BSTNested, - BSTNodeNested, +import type { BSTNOptKeyOrNode, BSTOptions, BTNRep, @@ -28,61 +26,35 @@ import { Queue } from '../queue'; import { isComparable } from '../../utils'; import { Range } from '../../common'; -export class BSTNode = BSTNodeNested> extends BinaryTreeNode< - K, - V, - NODE -> { - override parent?: NODE; - +export class BSTNode extends BinaryTreeNode { constructor(key: K, value?: V) { super(key, value); - this.parent = undefined; - this._left = undefined; - this._right = undefined; } - override _left?: OptNodeOrNull; + override parent?: BSTNode = undefined; - /** - * The function returns the value of the `_left` property. - * @returns The `_left` property of the current object is being returned. - */ - override get left(): OptNodeOrNull { + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { return this._left; } - /** - * The function sets the left child of a node and updates the parent reference of the child. - * @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: OptNodeOrNull) { + override set left(v: OptNodeOrNull>) { if (v) { - v.parent = this as unknown as NODE; + v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull; + override _right?: OptNodeOrNull> = undefined; - /** - * The function returns the right node of a binary tree or undefined if there is no right node. - * @returns The method is returning the value of the `_right` property, which is of type `NODE` or - * `undefined`. - */ - override get right(): OptNodeOrNull { + override get right(): OptNodeOrNull> { return this._right; } - /** - * The function sets the right child of a node and updates the parent reference of the child. - * @param {OptNode} v - The parameter `v` is of type `OptNode`. It can either be a - * `NODE` object or `undefined`. - */ - override set right(v: OptNodeOrNull) { + override set right(v: OptNodeOrNull>) { if (v) { - v.parent = this as unknown as NODE; + v.parent = this; } this._right = v; } @@ -153,27 +125,9 @@ export class BSTNode = BSTNod * console.log(findLCA(5, 35)); // 15 * console.log(findLCA(20, 30)); // 25 */ -export class BST< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends BSTNode = BSTNode>, - TREE extends BST = BST< - K, - V, - R, - MK, - MV, - MR, - NODE, - BSTNested - > - > - extends BinaryTree - implements IBinaryTree +export class BST + extends BinaryTree + implements IBinaryTree { /** * This is the constructor function for a Binary Search Tree class in TypeScript. @@ -183,7 +137,7 @@ export class BST< * @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(keysNodesEntriesOrRaws: Iterable> = [], options?: BSTOptions) { + constructor(keysNodesEntriesOrRaws: Iterable>> = [], options?: BSTOptions) { super([], options); if (options) { @@ -195,13 +149,13 @@ export class BST< if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } - protected override _root?: NODE = undefined; + protected override _root?: BSTNode = undefined; /** * The function returns the root node of a tree structure. - * @returns The `_root` property of the object, which is of type `NODE` or `undefined`. + * @returns The `_root` property of the object, which is of type `BSTNode` or `undefined`. */ - override get root(): OptNode { + override get root(): OptNode> { return this._root; } @@ -216,16 +170,55 @@ export class BST< return this._isReverse; } + protected _comparator: Comparator = (a: K, b: K): number => { + if (isComparable(a) && isComparable(b)) { + if (a > b) return 1; + if (a < b) return -1; + return 0; + } + if (this._specifyComparable) { + if (this._specifyComparable(a) > this._specifyComparable(b)) return 1; + if (this._specifyComparable(a) < this._specifyComparable(b)) return -1; + return 0; + } + if (typeof a === 'object' || typeof b === 'object') { + throw TypeError( + `When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.` + ); + } + + return 0; + }; + + /** + * The function returns the value of the _comparator property. + * @returns The `_comparator` property is being returned. + */ + get comparator() { + return this._comparator; + } + + protected _specifyComparable?: (key: K) => Comparable; + + /** + * This function returns the value of the `_specifyComparable` property. + * @returns The method `specifyComparable()` is being returned, which is a getter method for the + * `_specifyComparable` property. + */ + get specifyComparable() { + return this._specifyComparable; + } + /** * The function creates a new BSTNode with the given key and value and returns it. * @param {K} key - The key parameter is of type K, which represents the type of the key for the node * being created. * @param {V} [value] - The "value" parameter is an optional parameter of type V. It represents the * value associated with the key in the node being created. - * @returns The method is returning a new instance of the BSTNode class, casted as the NODE type. + * @returns The method is returning a new instance of the BSTNode class, casted as the BSTNode type. */ - override createNode(key: K, value?: V): NODE { - return new BSTNode(key, this._isMapMode ? undefined : value) as NODE; + override createNode(key: K, value?: V): BSTNode { + return new BSTNode(key, this._isMapMode ? undefined : value) as BSTNode; } /** @@ -235,33 +228,15 @@ export class BST< * following properties: * @returns a new instance of the BST class with the provided options. */ - override createTree(options?: BSTOptions): TREE { - return new BST([], { + override createTree(options?: BSTOptions) { + return new BST([], { iterationType: this.iterationType, isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, ...options - }) as TREE; - } - - /** - * The function overrides a method and converts a key, value pair or entry or raw element to a node. - * @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. - */ - protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep | R, - value?: V - ): [OptNode, V | undefined] { - const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); - if (node === null) return [undefined, undefined]; - return [node, value ?? entryValue]; + }); } /** @@ -270,7 +245,7 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - The parameter + * @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 @@ -280,20 +255,20 @@ export class BST< * not be ensured. */ override ensureNode( - keyNodeEntryOrRaw: BTNRep | R, + keyNodeEntryOrRaw: BTNRep> | R, iterationType: IterationType = this.iterationType - ): OptNode { + ): OptNode> { return super.ensureNode(keyNodeEntryOrRaw, iterationType) ?? undefined; } /** * The function checks if the input is an instance of the BSTNode class. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + override isNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BSTNode { return keyNodeEntryOrRaw instanceof BSTNode; } @@ -314,13 +289,13 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep`. + * @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(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V): boolean { const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); if (newNode === undefined) return false; @@ -381,7 +356,7 @@ export class BST< * successfully inserted into the data structure. */ override addMany( - keysNodesEntriesOrRaws: Iterable>, + keysNodesEntriesOrRaws: Iterable>>, values?: Iterable, isBalanceAdd = true, iterationType: IterationType = this.iterationType @@ -403,7 +378,7 @@ export class BST< } const realBTNExemplars: { - key: R | BTNRep; + key: R | BTNRep>; value: V | undefined; orgIndex: number; }[] = []; @@ -414,7 +389,7 @@ export class BST< i++; } - let sorted: { key: R | BTNRep; 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; @@ -440,7 +415,7 @@ export class BST< return 0; }); - const _dfs = (arr: { key: R | BTNRep; 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); @@ -483,7 +458,7 @@ export class BST< * * The function `search` in TypeScript overrides the search behavior in a binary tree structure based * on specified criteria. - * @param {BTNRep | R | NodePredicate} keyNodeEntryRawOrPredicate - The + * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The * `keyNodeEntryRawOrPredicate` parameter in the `override search` method can accept one of the * following types: * @param [onlyOne=false] - The `onlyOne` parameter is a boolean flag that determines whether the @@ -491,9 +466,9 @@ export class BST< * search will return as soon as a matching node is found. If `onlyOne` is set to `false`, the * @param {C} callback - The `callback` parameter in the `override search` function is a function * that will be called on each node that matches the search criteria. It is of type `C`, which - * extends `NodeCallback`. The callback function should accept a node of type `NODE` as its + * extends `NodeCallback>`. The callback function should accept a node of type `BSTNode` as its * argument and - * @param {BTNRep | R} startNode - The `startNode` parameter in the `override search` + * @param {BTNRep> | R} startNode - The `startNode` parameter in the `override search` * method represents the node from which the search operation will begin. It is the starting point * for searching within the tree data structure. The method ensures that the `startNode` is a valid * node before proceeding with the search operation. If the ` @@ -505,18 +480,18 @@ export class BST< * structure based on the provided key, predicate, and other options. The search results are * collected in an array and returned as the output of the method. */ - override search>( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate | Range, + override search>>( + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate> | Range, onlyOne = false, callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { if (keyNodeEntryRawOrPredicate === undefined) return []; if (keyNodeEntryRawOrPredicate === null) return []; startNode = this.ensureNode(startNode); if (!startNode) return []; - let predicate: NodePredicate; + let predicate: NodePredicate>; const isRange = this.isRange(keyNodeEntryRawOrPredicate); // Set predicate based on parameter type @@ -525,7 +500,7 @@ export class BST< } else { predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate); } - const isToLeftByRange = (cur: NODE) => { + const isToLeftByRange = (cur: BSTNode) => { if (isRange) { const range = keyNodeEntryRawOrPredicate; const leftS = this.isReverse ? range.high : range.low; @@ -535,7 +510,7 @@ export class BST< return false; }; - const isToRightByRange = (cur: NODE) => { + const isToRightByRange = (cur: BSTNode) => { if (isRange) { const range = keyNodeEntryRawOrPredicate; const rightS = this.isReverse ? range.low : range.high; @@ -547,7 +522,7 @@ export class BST< }; const ans: ReturnType[] = []; if (iterationType === 'RECURSIVE') { - const dfs = (cur: NODE) => { + const dfs = (cur: BSTNode) => { if (predicate(cur)) { ans.push(callback(cur)); if (onlyOne) return; @@ -627,9 +602,9 @@ export class BST< * either a `Range` object or an array of two elements representing the range boundaries. * @param {C} callback - The `callback` parameter in the `rangeSearch` function is a callback * function that is used to process each node that is found within the specified range during the - * search operation. It is of type `NodeCallback`, where `NODE` is the type of nodes in the + * search operation. It is of type `NodeCallback>`, where `BSTNode` is the type of nodes in the * data structure. - * @param {BTNRep | R} startNode - The `startNode` parameter in the `rangeSearch` + * @param {BTNRep> | R} startNode - The `startNode` parameter in the `rangeSearch` * function represents the node from which the search for nodes within the specified range will * begin. It is the starting point for the range search operation. * @param {IterationType} iterationType - The `iterationType` parameter in the `rangeSearch` function @@ -639,10 +614,10 @@ export class BST< * @returns The `rangeSearch` function is returning the result of calling the `search` method with * the specified parameters. */ - rangeSearch>( + rangeSearch>>( range: Range | [K, K], callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ) { const searchRange: Range = range instanceof Range ? range : new Range(range[0], range[1]); @@ -654,9 +629,9 @@ export class BST< * Space Complexity: O(1) * * 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 + * @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. @@ -664,16 +639,16 @@ 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 (`OptNode`). + * @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( - keyNodeEntryRawOrPredicate: BTNRep | R | NodePredicate, - startNode: R | BSTNOptKeyOrNode = this._root, + keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, + startNode: R | BSTNOptKeyOrNode> = this._root, iterationType: IterationType = this.iterationType - ): OptNode { + ): OptNode> { return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined; } @@ -689,7 +664,7 @@ export class BST< * @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 {BTNRep | R} startNode - The `startNode` 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 @@ -697,10 +672,10 @@ export class BST< * following values: * @returns The method is returning an array of the return type of the callback function. */ - override dfs>( + override dfs>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { return super.dfs(callback, pattern, startNode, iterationType); @@ -715,7 +690,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 {BTNRep | R} startNode - The `startNode` 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 @@ -723,9 +698,9 @@ export class BST< * the following values: * @returns an array of the return type of the callback function. */ - override bfs>( + override bfs>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { return super.bfs(callback, startNode, iterationType, false); @@ -738,9 +713,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 - * `NodeCallback`. 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 {BTNRep | R} startNode - The `startNode` 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 @@ -749,9 +724,9 @@ export class BST< * @returns The method is returning a two-dimensional array of the return type of the callback * function. */ - override listLevels>( + override listLevels>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep | R = this._root, + startNode: BTNRep> | R = this._root, iterationType: IterationType = this.iterationType ): ReturnType[][] { return super.listLevels(callback, startNode, iterationType, false); @@ -769,7 +744,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 {BTNRep | 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, @@ -778,21 +753,21 @@ export class BST< * @returns The function `lesserOrGreaterTraverse` returns an array of values of type * `ReturnType`, which is the return type of the callback function passed as an argument. */ - lesserOrGreaterTraverse>( + lesserOrGreaterTraverse>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, lesserOrGreater: CP = -1, - targetNode: BTNRep | 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; const targetKey = targetNodeEnsured.key; if (iterationType === 'RECURSIVE') { - const dfs = (cur: NODE) => { + const dfs = (cur: BSTNode) => { const compared = this._compare(cur.key, targetKey); if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur)); // TODO here can be optimized to O(log n) @@ -803,7 +778,7 @@ export class BST< dfs(this._root); return ans; } else { - const queue = new Queue([this._root]); + const queue = new Queue>([this._root]); while (queue.size > 0) { const cur = queue.shift(); if (this.isRealNode(cur)) { @@ -887,7 +862,7 @@ export class BST< let balanced = true; if (iterationType === 'RECURSIVE') { - const _height = (cur: OptNodeOrNull): number => { + const _height = (cur: OptNodeOrNull>): number => { if (!cur) return 0; const leftHeight = _height(cur.left), rightHeight = _height(cur.right); @@ -896,10 +871,10 @@ export class BST< }; _height(this._root); } else { - const stack: NODE[] = []; - let node: OptNode = this._root, - last: OptNode = undefined; - const depths: Map = new Map(); + const stack: BSTNode[] = []; + let node: OptNode> = this._root, + last: OptNode> = undefined; + const depths: Map, number> = new Map(); while (stack.length > 0 || node) { if (node) { @@ -925,61 +900,6 @@ export class BST< return balanced; } - protected _comparator: Comparator = (a: K, b: K): number => { - if (isComparable(a) && isComparable(b)) { - if (a > b) return 1; - if (a < b) return -1; - return 0; - } - if (this._specifyComparable) { - if (this._specifyComparable(a) > this._specifyComparable(b)) return 1; - if (this._specifyComparable(a) < this._specifyComparable(b)) return -1; - return 0; - } - if (typeof a === 'object' || typeof b === 'object') { - throw TypeError( - `When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.` - ); - } - - return 0; - }; - - /** - * The function returns the value of the _comparator property. - * @returns The `_comparator` property is being returned. - */ - get comparator() { - return this._comparator; - } - - protected _specifyComparable?: (key: K) => Comparable; - - /** - * This function returns the value of the `_specifyComparable` property. - * @returns The method `specifyComparable()` is being returned, which is a getter method for the - * `_specifyComparable` property. - */ - get specifyComparable() { - return this._specifyComparable; - } - - /** - * The function sets the root of a tree-like structure and updates the parent property of the new - * root. - * @param {OptNode} v - v is a parameter of type NODE or undefined. - */ - protected override _setRoot(v: OptNode) { - if (v) { - v.parent = undefined; - } - this._root = v; - } - - protected _compare(a: K, b: K) { - return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b); - } - override map( callback: EntryCallback, options?: BSTOptions, @@ -992,4 +912,44 @@ export class BST< } return newTree; } + + override clone() { + const cloned = this.createTree(); + this._clone(cloned); + return cloned; + } + + /** + * The function overrides a method and converts a key, value pair or entry or raw element to a node. + * @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 BSTNode object or undefined. + */ + protected override _keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep> | R, + value?: V + ): [OptNode>, V | undefined] { + const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + if (node === null) return [undefined, undefined]; + return [node, value ?? entryValue]; + } + + /** + * The function sets the root of a tree-like structure and updates the parent property of the new + * root. + * @param {OptNode>} v - v is a parameter of type BSTNode or undefined. + */ + protected override _setRoot(v: OptNode>) { + if (v) { + v.parent = undefined; + } + this._root = v; + } + + protected _compare(a: K, b: K) { + return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b); + } } diff --git a/src/data-structures/binary-tree/red-black-tree.ts b/src/data-structures/binary-tree/red-black-tree.ts index c16d925..a2fd0b3 100644 --- a/src/data-structures/binary-tree/red-black-tree.ts +++ b/src/data-structures/binary-tree/red-black-tree.ts @@ -4,19 +4,14 @@ import type { CRUD, EntryCallback, OptNode, + OptNodeOrNull, RBTNColor, - RedBlackTreeOptions, - RedBlackTreeNested, - RedBlackTreeNodeNested + RedBlackTreeOptions } from '../../types'; import { BST, BSTNode } from './bst'; import { IBinaryTree } from '../../interfaces'; -export class RedBlackTreeNode< - K = any, - V = any, - NODE extends RedBlackTreeNode = RedBlackTreeNodeNested -> extends BSTNode { +export class RedBlackTreeNode extends BSTNode { /** * The constructor function initializes a Red-Black Tree Node with a key, an optional value, and a * color. @@ -32,6 +27,34 @@ export class RedBlackTreeNode< super(key, value); this._color = color; } + + override parent?: RedBlackTreeNode = undefined; + + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { + return this._left; + } + + override set left(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._left = v; + } + + override _right?: OptNodeOrNull> = undefined; + + override get right(): OptNodeOrNull> { + return this._right; + } + + override set right(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._right = v; + } } /** @@ -87,27 +110,9 @@ export class RedBlackTreeNode< * ); * console.log(stocksInRange); // ['GOOGL', 'MSFT', 'META'] */ -export class RedBlackTree< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends RedBlackTreeNode = RedBlackTreeNode>, - TREE extends RedBlackTree = RedBlackTree< - K, - V, - R, - MK, - MV, - MR, - NODE, - RedBlackTreeNested - > - > - extends BST - implements IBinaryTree +export class RedBlackTree + extends BST + implements IBinaryTree { /** * This is the constructor function for a Red-Black Tree data structure in TypeScript. @@ -119,7 +124,10 @@ export class RedBlackTree< * configuring the behavior of the Red-Black Tree. The specific properties and their meanings would * depend on the implementation */ - constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: RedBlackTreeOptions) { + constructor( + keysNodesEntriesOrRaws: Iterable>> = [], + options?: RedBlackTreeOptions + ) { super([], options); this._root = this.NIL; @@ -129,13 +137,13 @@ export class RedBlackTree< } } - protected override _root: NODE | undefined; + protected override _root: RedBlackTreeNode | undefined; /** * The function returns the root node of a tree or undefined if there is no root. * @returns The root node of the tree structure, or undefined if there is no root node. */ - override get root(): NODE | undefined { + override get root(): RedBlackTreeNode | undefined { return this._root; } @@ -153,8 +161,8 @@ export class RedBlackTree< * @returns A new instance of a RedBlackTreeNode with the specified key, value, and color is being * returned. */ - override createNode(key: K, value?: V, color: RBTNColor = 'BLACK'): NODE { - return new RedBlackTreeNode(key, this._isMapMode ? undefined : value, color) as NODE; + override createNode(key: K, value?: V, color: RBTNColor = 'BLACK'): RedBlackTreeNode { + return new RedBlackTreeNode(key, this._isMapMode ? undefined : value, color) as RedBlackTreeNode; } /** @@ -163,14 +171,14 @@ export class RedBlackTree< * configuration options for creating the Red-Black Tree. It has the following properties: * @returns a new instance of a RedBlackTree object. */ - override createTree(options?: RedBlackTreeOptions): TREE { - return new RedBlackTree([], { + override createTree(options?: RedBlackTreeOptions) { + return new RedBlackTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, ...options - }) as TREE; + }); } /** @@ -178,12 +186,14 @@ export class RedBlackTree< * Space Complexity: O(1) * * The function checks if the input is an instance of the RedBlackTreeNode class. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + override isNode( + keyNodeEntryOrRaw: BTNRep> | R + ): keyNodeEntryOrRaw is RedBlackTreeNode { return keyNodeEntryOrRaw instanceof RedBlackTreeNode; } @@ -205,8 +215,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 {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep`. + * @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. @@ -214,7 +224,7 @@ 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(keyNodeEntryOrRaw: BTNRep | R, value?: V): boolean { + override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V): boolean { const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); if (!this.isRealNode(newNode)) return false; @@ -244,19 +254,21 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @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. - * @returns The `override delete` method is returning an array of `BinaryTreeDeleteResult` + * @returns The `override delete` method is returning an array of `BinaryTreeDeleteResult>` * objects. Each object in the array contains information about the deleted node and whether * balancing is needed. */ - override delete(keyNodeEntryOrRaw: BTNRep | R): BinaryTreeDeleteResult[] { + override delete( + keyNodeEntryOrRaw: BTNRep> | R + ): BinaryTreeDeleteResult>[] { if (keyNodeEntryOrRaw === null) return []; - const results: BinaryTreeDeleteResult[] = []; - let nodeToDelete: OptNode; + const results: BinaryTreeDeleteResult>[] = []; + let nodeToDelete: OptNode>; if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw); else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw); @@ -265,7 +277,7 @@ export class RedBlackTree< } let originalColor = nodeToDelete.color; - let replacementNode: NODE | undefined; + let replacementNode: RedBlackTreeNode | undefined; if (!this.isRealNode(nodeToDelete.left)) { if (nodeToDelete.right !== null) { @@ -316,15 +328,54 @@ export class RedBlackTree< return results; } + /** + * Time Complexity: O(n) + * Space Complexity: O(n) + * + * The `map` function in TypeScript overrides the default behavior to create a new Red-Black Tree by + * applying a callback to each entry in the original tree. + * @param callback - A function that will be called for each entry in the tree, with parameters + * representing the key, value, index, and the tree itself. It should return an entry for the new + * tree. + * @param [options] - The `options` parameter in the `map` method is of type `RedBlackTreeOptions`. This parameter allows you to specify additional options or configurations for the Red-Black + * Tree that will be created during the mapping process. These options could include things like + * custom comparators + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) for the callback function. This can be useful when you want to access properties + * or + * @returns A new Red-Black Tree is being returned, where each entry has been transformed using the + * provided callback function. + */ + override map( + callback: EntryCallback, + options?: RedBlackTreeOptions, + thisArg?: any + ): RedBlackTree { + const newTree = new RedBlackTree([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + + override clone() { + const cloned = this.createTree(); + this._clone(cloned); + return cloned; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) * * The function sets the root of a tree-like structure and updates the parent property of the new * root. - * @param {NODE | undefined} v - v is a parameter of type NODE or undefined. + * @param {RedBlackTreeNode | undefined} v - v is a parameter of type RedBlackTreeNode or undefined. */ - protected override _setRoot(v: NODE | undefined) { + protected override _setRoot(v: RedBlackTreeNode | undefined) { if (v) { v.parent = undefined; } @@ -336,14 +387,17 @@ export class RedBlackTree< * Space Complexity: O(1) * * The function replaces an old node with a new node while preserving the color of the old node. - * @param {NODE} oldNode - The `oldNode` parameter represents the node that needs to be replaced in + * @param {RedBlackTreeNode} oldNode - The `oldNode` parameter represents the node that needs to be replaced in * the data structure. - * @param {NODE} newNode - The `newNode` parameter is of type `NODE`, which represents a node in a + * @param {RedBlackTreeNode} newNode - The `newNode` parameter is of type `RedBlackTreeNode`, which represents a node in a * data structure. * @returns The method is returning the result of calling the `_replaceNode` method from the * superclass, with the `oldNode` and `newNode` parameters. */ - protected override _replaceNode(oldNode: NODE, newNode: NODE): NODE { + protected override _replaceNode( + oldNode: RedBlackTreeNode, + newNode: RedBlackTreeNode + ): RedBlackTreeNode { newNode.color = oldNode.color; return super._replaceNode(oldNode, newNode); @@ -355,15 +409,15 @@ export class RedBlackTree< * * The `_insert` function inserts a node into a binary search tree and performs necessary fix-ups to * maintain the red-black tree properties. - * @param {NODE} node - The `node` parameter represents the node that needs to be inserted into the + * @param {RedBlackTreeNode} node - The `node` parameter represents the node that needs to be inserted into the * binary search tree. * @returns a string value indicating the result of the insertion operation. It can return either * 'UPDATED' if the node with the same key already exists and was updated, or 'CREATED' if a new node * was created and inserted into the tree. */ - protected _insert(node: NODE): CRUD { + protected _insert(node: RedBlackTreeNode): CRUD { let current = this.root; - let parent: NODE | undefined = undefined; + let parent: RedBlackTreeNode | undefined = undefined; while (this.isRealNode(current)) { parent = current; @@ -401,11 +455,11 @@ export class RedBlackTree< * Space Complexity: O(1) * * The function `_transplant` is used to replace a node `u` with another node `v` in a binary tree. - * @param {NODE} u - The parameter "u" represents a node in a binary tree. - * @param {NODE | undefined} v - The parameter `v` is of type `NODE | undefined`, which means it can - * either be a `NODE` object or `undefined`. + * @param {RedBlackTreeNode} u - The parameter "u" represents a node in a binary tree. + * @param {RedBlackTreeNode | undefined} v - The parameter `v` is of type `RedBlackTreeNode | undefined`, which means it can + * either be a `RedBlackTreeNode` object or `undefined`. */ - protected _transplant(u: NODE, v: NODE | undefined): void { + protected _transplant(u: RedBlackTreeNode, v: RedBlackTreeNode | undefined): void { if (!u.parent) { this._setRoot(v); } else if (u === u.parent.left) { @@ -424,10 +478,10 @@ export class RedBlackTree< * Space Complexity: O(1) * * The `_insertFixup` function is used to fix the Red-Black Tree after inserting a new node. - * @param {NODE | undefined} z - The parameter `z` represents a node in the Red-Black Tree data + * @param {RedBlackTreeNode | undefined} z - The parameter `z` represents a node in the Red-Black Tree data * structure. It can either be a valid node or `undefined`. */ - protected _insertFixup(z: NODE | undefined): void { + protected _insertFixup(z: RedBlackTreeNode | undefined): void { // Continue fixing the tree as long as the parent of z is red while (z?.parent?.color === 'RED') { // Check if the parent of z is the left child of its parent @@ -460,7 +514,7 @@ export class RedBlackTree< } else { // Symmetric case for the right child (left and right exchanged) // Follow the same logic as above with left and right exchanged - const y: NODE | undefined = z?.parent?.parent?.left ?? undefined; + const y: RedBlackTreeNode | undefined = z?.parent?.parent?.left ?? undefined; if (y?.color === 'RED') { z.parent.color = 'BLACK'; y.color = 'BLACK'; @@ -491,12 +545,12 @@ export class RedBlackTree< * * The `_deleteFixup` function is used to fix the red-black tree after a node deletion by adjusting * the colors and performing rotations. - * @param {NODE | undefined} node - The `node` parameter represents a node in a binary tree. It can + * @param {RedBlackTreeNode | undefined} node - The `node` parameter represents a node in a binary tree. It can * be either a valid node object or `undefined`. * @returns The function does not return any value. It has a return type of `void`, which means it * does not return anything. */ - protected _deleteFixup(node: NODE | undefined): void { + protected _deleteFixup(node: RedBlackTreeNode | undefined): void { // Early exit condition if (!node || node === this.root || node.color === 'BLACK') { if (node) { @@ -506,7 +560,7 @@ export class RedBlackTree< } while (node && node !== this.root && node.color === 'BLACK') { - const parent: NODE | undefined = node.parent; + const parent: RedBlackTreeNode | undefined = node.parent; if (!parent) { break; // Ensure the loop terminates if there's an issue with the tree structure @@ -573,11 +627,11 @@ export class RedBlackTree< * Space Complexity: O(1) * * The `_leftRotate` function performs a left rotation on a given node in a binary tree. - * @param {NODE | undefined} x - The parameter `x` is of type `NODE | undefined`. It represents a + * @param {RedBlackTreeNode | undefined} x - The parameter `x` is of type `RedBlackTreeNode | undefined`. It represents a * node in a binary tree or `undefined` if there is no node. * @returns void, which means it does not return any value. */ - protected _leftRotate(x: NODE | undefined): void { + protected _leftRotate(x: RedBlackTreeNode | undefined): void { if (!x || !x.right) { return; } @@ -608,11 +662,11 @@ export class RedBlackTree< * Space Complexity: O(1) * * The `_rightRotate` function performs a right rotation on a given node in a binary tree. - * @param {NODE | undefined} y - The parameter `y` is of type `NODE | undefined`. It represents a + * @param {RedBlackTreeNode | undefined} y - The parameter `y` is of type `RedBlackTreeNode | undefined`. It represents a * node in a binary tree or `undefined` if there is no node. * @returns void, which means it does not return any value. */ - protected _rightRotate(y: NODE | undefined): void { + protected _rightRotate(y: RedBlackTreeNode | undefined): void { if (!y || !y.left) { return; } @@ -637,37 +691,4 @@ export class RedBlackTree< x.right = y; y.parent = x; } - - /** - * Time Complexity: O(n) - * Space Complexity: O(n) - * - * The `map` function in TypeScript overrides the default behavior to create a new Red-Black Tree by - * applying a callback to each entry in the original tree. - * @param callback - A function that will be called for each entry in the tree, with parameters - * representing the key, value, index, and the tree itself. It should return an entry for the new - * tree. - * @param [options] - The `options` parameter in the `map` method is of type `RedBlackTreeOptions`. This parameter allows you to specify additional options or configurations for the Red-Black - * Tree that will be created during the mapping process. These options could include things like - * custom comparators - * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify - * the value of `this` when executing the `callback` function. It allows you to set the context - * (value of `this`) for the callback function. This can be useful when you want to access properties - * or - * @returns A new Red-Black Tree is being returned, where each entry has been transformed using the - * provided callback function. - */ - override map( - callback: EntryCallback, - options?: RedBlackTreeOptions, - thisArg?: any - ): RedBlackTree { - const newTree = new RedBlackTree([], options); - let index = 0; - for (const [key, value] of this) { - newTree.add(callback.call(thisArg, key, value, index++, this)); - } - return newTree; - } } diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index 1e17a7d..cd8ff05 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -12,19 +12,14 @@ import type { EntryCallback, IterationType, OptNode, + OptNodeOrNull, RBTNColor, - TreeMultiMapNested, - TreeMultiMapNodeNested, TreeMultiMapOptions } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { RedBlackTree, RedBlackTreeNode } from './red-black-tree'; -export class TreeMultiMapNode< - K = any, - V = any, - NODE extends TreeMultiMapNode = TreeMultiMapNodeNested -> extends RedBlackTreeNode { +export class TreeMultiMapNode extends RedBlackTreeNode { /** * The constructor function initializes a Red-Black Tree node with a key, value, count, and color. * @param {K} key - The key parameter represents the key of the node in the Red-Black Tree. It is @@ -41,29 +36,39 @@ export class TreeMultiMapNode< super(key, value, color); this.count = count; } + + override parent?: TreeMultiMapNode = undefined; + + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { + return this._left; + } + + override set left(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._left = v; + } + + override _right?: OptNodeOrNull> = undefined; + + override get right(): OptNodeOrNull> { + return this._right; + } + + override set right(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._right = v; + } } -export class TreeMultiMap< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends TreeMultiMapNode = TreeMultiMapNode>, - TREE extends TreeMultiMap = TreeMultiMap< - K, - V, - R, - MK, - MV, - MR, - NODE, - TreeMultiMapNested - > - > - extends RedBlackTree - implements IBinaryTree +export class TreeMultiMap + extends RedBlackTree + implements IBinaryTree { /** * The constructor function initializes a TreeMultiMap object with optional initial data. @@ -74,7 +79,10 @@ export class TreeMultiMap< * 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(keysNodesEntriesOrRaws: Iterable> = [], options?: TreeMultiMapOptions) { + constructor( + keysNodesEntriesOrRaws: Iterable>> = [], + options?: TreeMultiMapOptions + ) { super([], options); if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } @@ -115,10 +123,10 @@ export class TreeMultiMap< * @param {number} [count] - The `count` parameter represents the number of occurrences of a key in * the tree. It is an optional parameter and is used to keep track of the number of values associated * with a key in the tree. - * @returns A new instance of the TreeMultiMapNode class, casted as NODE. + * @returns A new instance of the TreeMultiMapNode class, casted as TreeMultiMapNode. */ - override createNode(key: K, value?: V, color: RBTNColor = 'BLACK', count?: number): NODE { - return new TreeMultiMapNode(key, this._isMapMode ? undefined : value, count, color) as NODE; + override createNode(key: K, value?: V, color: RBTNColor = 'BLACK', count?: number): TreeMultiMapNode { + return new TreeMultiMapNode(key, this._isMapMode ? undefined : value, count, color) as TreeMultiMapNode; } /** @@ -129,63 +137,26 @@ export class TreeMultiMap< * @returns a new instance of the `TreeMultiMap` class, with the provided options merged with the * existing `iterationType` property. The returned value is casted as `TREE`. */ - override createTree(options?: TreeMultiMapOptions): TREE { - return new TreeMultiMap([], { + override createTree(options?: TreeMultiMapOptions) { + return new TreeMultiMap([], { iterationType: this.iterationType, isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, ...options - }) as TREE; - } - - /** - * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a - * node based on the input. - * @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. - * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of - * 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. - */ - protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep | R, - value?: V, - count = 1 - ): [NODE | undefined, V | undefined] { - if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; - - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - 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.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value]; - - return [undefined, undefined]; + }); } /** * The function checks if the input is an instance of the TreeMultiMapNode class. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @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(keyNodeEntryOrRaw: BTNRep | R): keyNodeEntryOrRaw is NODE { + override isNode( + keyNodeEntryOrRaw: BTNRep> | R + ): keyNodeEntryOrRaw is TreeMultiMapNode { return keyNodeEntryOrRaw instanceof TreeMultiMapNode; } @@ -195,7 +166,7 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - The + * @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. @@ -205,7 +176,7 @@ 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(keyNodeEntryOrRaw: BTNRep | R, value?: V, count = 1): boolean { + 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, newValue); @@ -224,21 +195,24 @@ 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 {BTNRep | R} keyNodeEntryOrRaw - 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 * boolean flag that determines whether to ignore the count of nodes when performing deletion. If * `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If * `ignoreCount` is `false - * @returns The `override delete` method returns an array of `BinaryTreeDeleteResult` objects. + * @returns The `override delete` method returns an array of `BinaryTreeDeleteResult>` objects. */ - override delete(keyNodeEntryOrRaw: BTNRep | R, ignoreCount = false): BinaryTreeDeleteResult[] { + override delete( + keyNodeEntryOrRaw: BTNRep> | R, + ignoreCount = false + ): BinaryTreeDeleteResult>[] { if (keyNodeEntryOrRaw === null) return []; - const results: BinaryTreeDeleteResult[] = []; + const results: BinaryTreeDeleteResult>[] = []; - let nodeToDelete: OptNode; + let nodeToDelete: OptNode>; if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw); else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw); @@ -247,7 +221,7 @@ export class TreeMultiMap< } let originalColor = nodeToDelete.color; - let replacementNode: NODE | undefined; + let replacementNode: TreeMultiMapNode | undefined; if (!this.isRealNode(nodeToDelete.left)) { if (nodeToDelete.right !== null) replacementNode = nodeToDelete.right; @@ -400,71 +374,13 @@ export class TreeMultiMap< * The function overrides the clone method to create a deep copy of a tree object. * @returns The `clone()` method is returning a cloned instance of the `TREE` object. */ - override clone(): TREE { + override clone() { const cloned = this.createTree(); this.bfs(node => cloned.add(node.key, undefined, node.count)); if (this._isMapMode) cloned._store = this._store; return cloned; } - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes - * in a binary search tree. - * @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 `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 | BSTNOptKeyOrNode, - destNode: R | BSTNOptKeyOrNode - ): NODE | undefined { - srcNode = this.ensureNode(srcNode); - destNode = this.ensureNode(destNode); - if (srcNode && destNode) { - const { key, value, count, color } = destNode; - const tempNode = this.createNode(key, value, color, count); - if (tempNode) { - tempNode.color = color; - - destNode.key = srcNode.key; - if (!this._isMapMode) destNode.value = srcNode.value; - destNode.count = srcNode.count; - destNode.color = srcNode.color; - - srcNode.key = tempNode.key; - if (!this._isMapMode) srcNode.value = tempNode.value; - srcNode.count = tempNode.count; - srcNode.color = tempNode.color; - } - - return destNode; - } - return undefined; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function replaces an old node with a new node and updates the count property of the new node. - * @param {NODE} oldNode - The `oldNode` parameter is the node that you want to replace in the data - * structure. - * @param {NODE} newNode - The `newNode` parameter is an instance of the `NODE` class. - * @returns The method is returning the result of calling the `_replaceNode` method from the - * superclass, which is of type `NODE`. - */ - protected override _replaceNode(oldNode: NODE, newNode: NODE): NODE { - newNode.count = oldNode.count + newNode.count; - return super._replaceNode(oldNode, newNode); - } - /** * The `map` function in TypeScript overrides the default behavior to create a new TreeMultiMap with * modified entries based on a provided callback. @@ -492,4 +408,104 @@ export class TreeMultiMap< } return newTree; } + + /** + * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a + * node based on the input. + * @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. + * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of + * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. + * @returns either a TreeMultiMapNode object or undefined. + */ + protected override _keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep> | R, + value?: V, + count = 1 + ): [TreeMultiMapNode | undefined, V | undefined] { + if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; + + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; + + 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.isRaw(keyNodeEntryOrRaw)) { + const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; + } + + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value]; + + return [undefined, undefined]; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes + * in a binary search tree. + * @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 `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 | BSTNOptKeyOrNode>, + destNode: R | BSTNOptKeyOrNode> + ): TreeMultiMapNode | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); + if (srcNode && destNode) { + const { key, value, count, color } = destNode; + const tempNode = this.createNode(key, value, color, count); + if (tempNode) { + tempNode.color = color; + + destNode.key = srcNode.key; + if (!this._isMapMode) destNode.value = srcNode.value; + destNode.count = srcNode.count; + destNode.color = srcNode.color; + + srcNode.key = tempNode.key; + if (!this._isMapMode) srcNode.value = tempNode.value; + srcNode.count = tempNode.count; + srcNode.color = tempNode.color; + } + + return destNode; + } + return undefined; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function replaces an old node with a new node and updates the count property of the new node. + * @param {TreeMultiMapNode} oldNode - The `oldNode` parameter is the node that you want to replace in the data + * structure. + * @param {TreeMultiMapNode} newNode - The `newNode` parameter is an instance of the `TreeMultiMapNode` class. + * @returns The method is returning the result of calling the `_replaceNode` method from the + * superclass, which is of type `TreeMultiMapNode`. + */ + protected override _replaceNode( + oldNode: TreeMultiMapNode, + newNode: TreeMultiMapNode + ): TreeMultiMapNode { + newNode.count = oldNode.count + newNode.count; + return super._replaceNode(oldNode, newNode); + } } diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index a5f8dad..6895fc8 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -1,30 +1,16 @@ -import { BinaryTree, BinaryTreeNode } from '../data-structures'; -import type { - BinaryTreeDeleteResult, - BinaryTreeNested, - BinaryTreeNodeNested, - BinaryTreeOptions, - BTNRep, - NodePredicate -} from '../types'; +import { BinaryTreeNode } from '../data-structures'; +import type { BinaryTreeDeleteResult, BinaryTreeOptions, BTNRep, NodePredicate } from '../types'; -export interface IBinaryTree< - K = any, - V = any, - R = object, - MK = any, - MV = any, - MR = object, - NODE extends BinaryTreeNode = BinaryTreeNodeNested, - TREE extends BinaryTree = BinaryTreeNested -> { - createNode(key: K, value?: NODE['value']): NODE; +export interface IBinaryTree { + createNode(key: K, value?: BinaryTreeNode['value']): BinaryTreeNode; - createTree(options?: Partial>): TREE; + createTree(options?: Partial>): IBinaryTree; - add(keyOrNodeOrEntryOrRawElement: BTNRep, 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 | BTNRep | NodePredicate): BinaryTreeDeleteResult[]; + delete( + predicate: R | BTNRep> | NodePredicate> + ): BinaryTreeDeleteResult>[]; } diff --git a/src/types/data-structures/binary-tree/avl-tree-multi-map.ts b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts index 059a76d..267cb37 100644 --- a/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts @@ -1,8 +1,3 @@ -import { AVLTreeMultiMap, AVLTreeMultiMapNode } from '../../../data-structures'; import type { AVLTreeOptions } from './avl-tree'; -export type AVLTreeMultiMapNodeNested = AVLTreeMultiMapNode>> - -export type AVLTreeMultiMapNested> = AVLTreeMultiMap>> - export type AVLTreeMultiMapOptions = AVLTreeOptions & {} diff --git a/src/types/data-structures/binary-tree/avl-tree.ts b/src/types/data-structures/binary-tree/avl-tree.ts index 1411764..0a15424 100644 --- a/src/types/data-structures/binary-tree/avl-tree.ts +++ b/src/types/data-structures/binary-tree/avl-tree.ts @@ -1,8 +1,3 @@ -import { AVLTree, AVLTreeNode } from '../../../data-structures'; import { BSTOptions } from './bst'; -export type AVLTreeNodeNested = AVLTreeNode>> - -export type AVLTreeNested> = AVLTree>> - export type AVLTreeOptions = BSTOptions & {}; diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index dfc05a9..081e9c6 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -1,11 +1,6 @@ -import { BinaryTree, BinaryTreeNode } from '../../../data-structures'; import { IterationType, OptValue } from '../../common'; import { DFSOperation } from '../../../common'; -export type BinaryTreeNodeNested = BinaryTreeNode>> - -export type BinaryTreeNested> = BinaryTree>> - export type ToEntryFn = (rawElement: R) => BTNEntry; export type BinaryTreeOptions = { diff --git a/src/types/data-structures/binary-tree/bst.ts b/src/types/data-structures/binary-tree/bst.ts index 5a40bf1..dac6932 100644 --- a/src/types/data-structures/binary-tree/bst.ts +++ b/src/types/data-structures/binary-tree/bst.ts @@ -1,10 +1,6 @@ -import { BST, BSTNode } from '../../../data-structures'; import type { BinaryTreeOptions } from './binary-tree'; import { Comparable } from '../../utils'; - -export type BSTNodeNested = BSTNode>> - -export type BSTNested> = BST>> +import { OptValue } from '../../common'; export type BSTOptions = BinaryTreeOptions & { specifyComparable?: (key: K) => Comparable @@ -15,5 +11,9 @@ export type BSTNOptKey = K | undefined; export type OptNode = NODE | undefined; +export type BSTNEntry = [BSTNOptKey, OptValue]; + export type BSTNOptKeyOrNode = BSTNOptKey | NODE; +export type BSTNRep = BSTNEntry | BSTNOptKeyOrNode; + diff --git a/src/types/data-structures/binary-tree/rb-tree.ts b/src/types/data-structures/binary-tree/rb-tree.ts index 2f813b2..5501112 100644 --- a/src/types/data-structures/binary-tree/rb-tree.ts +++ b/src/types/data-structures/binary-tree/rb-tree.ts @@ -1,10 +1,5 @@ -import { RedBlackTree, RedBlackTreeNode } from '../../../data-structures'; -import type { BSTOptions } from "./bst"; +import type { BSTOptions } from './bst'; export type RBTNColor = 'RED' | 'BLACK'; -export type RedBlackTreeNodeNested = RedBlackTreeNode>> - -export type RedBlackTreeNested> = RedBlackTree>> - export type RedBlackTreeOptions = BSTOptions & {}; diff --git a/src/types/data-structures/binary-tree/tree-multi-map.ts b/src/types/data-structures/binary-tree/tree-multi-map.ts index 47ee09d..e03e50a 100644 --- a/src/types/data-structures/binary-tree/tree-multi-map.ts +++ b/src/types/data-structures/binary-tree/tree-multi-map.ts @@ -1,8 +1,3 @@ -import { TreeMultiMap, TreeMultiMapNode } from '../../../data-structures'; import type { RedBlackTreeOptions } from './rb-tree'; -export type TreeMultiMapNodeNested = TreeMultiMapNode>> - -export type TreeMultiMapNested> = TreeMultiMap>> - export type TreeMultiMapOptions = RedBlackTreeOptions & {} diff --git a/test/integration/all-in-one.test.ts b/test/integration/all-in-one.test.ts index 8f1ec5d..5958e3f 100644 --- a/test/integration/all-in-one.test.ts +++ b/test/integration/all-in-one.test.ts @@ -3,7 +3,7 @@ import { AVLTree } from 'data-structure-typed'; describe('AVL Tree Test from data-structure-typed', () => { it('should perform various operations on a AVL Tree from data-structure-typed', () => { const keys = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - const tree = new AVLTree(); + const tree = new AVLTree([], { isMapMode: false }); for (const i of keys) tree.add([i, i]); diff --git a/test/integration/avl-tree.test.ts b/test/integration/avl-tree.test.ts index 5ff99af..d5dc9e2 100644 --- a/test/integration/avl-tree.test.ts +++ b/test/integration/avl-tree.test.ts @@ -48,7 +48,7 @@ describe('AVL Tree Test', () => { 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(node15?.value).toBe(15); + expect(node15?.value).toBe(undefined); const dfs = tree.dfs(node => node, 'IN'); expect(dfs[0].key).toBe(1); diff --git a/test/integration/bst.test.ts b/test/integration/bst.test.ts index d177ede..e6719dd 100644 --- a/test/integration/bst.test.ts +++ b/test/integration/bst.test.ts @@ -27,7 +27,7 @@ describe('Individual package BST operations test', () => { expect(nodeVal9?.key).toBe(undefined); const nodeVal11 = bst.getNode(node => node.value === 11); - expect(nodeVal11?.key).toBe(11); + expect(nodeVal11?.key).toBe(undefined); const leftMost = bst.getLeftMost(node => node); expect(leftMost?.key).toBe(1); @@ -186,7 +186,7 @@ describe('Individual package BST operations test', () => { }); it('should perform various operations on a Binary Search Tree with object values', () => { - const objBST = new BST(); + const objBST = new BST([], { isMapMode: false }); expect(objBST).toBeInstanceOf(BST); objBST.add([11, { key: 11, keyA: 11 }]); objBST.add([3, { key: 3, keyA: 3 }]); diff --git a/test/integration/compile.js b/test/integration/compile.js new file mode 100644 index 0000000..bc7a6d5 --- /dev/null +++ b/test/integration/compile.js @@ -0,0 +1,143 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +var data_structure_typed_1 = require('data-structure-typed'); +var orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8]; +var orgStrArr = ['trie', 'trial', 'trick', 'trip', 'tree', 'trend', 'triangle', 'track', 'trace', 'transmit']; +var entries = [ + [6, '6'], + [1, '1'], + [2, '2'], + [7, '7'], + [5, '5'], + [3, '3'], + [4, '4'], + [9, '9'], + [8, '8'] +]; +var queue = new data_structure_typed_1.Queue(orgArr); +queue.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +var deque = new data_structure_typed_1.Deque(orgArr); +deque.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +var sList = new data_structure_typed_1.SinglyLinkedList(orgArr); +sList.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +var dList = new data_structure_typed_1.DoublyLinkedList(orgArr); +dList.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +var stack = new data_structure_typed_1.Stack(orgArr); +stack.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +var minHeap = new data_structure_typed_1.MinHeap(orgArr); +minHeap.print(); +// [1, 5, 2, 7, 6, 3, 4, 9, 8] +var maxPQ = new data_structure_typed_1.MaxPriorityQueue(orgArr); +maxPQ.print(); +// [9, 8, 4, 7, 5, 2, 3, 1, 6] +var biTree = new data_structure_typed_1.BinaryTree(entries); +biTree.print(); +// ___6___ +// / \ +// ___1_ _2_ +// / \ / \ +// _7_ 5 3 4 +// / \ +// 9 8 +var bst = new data_structure_typed_1.BST(entries); +bst.print(); +// _____5___ +// / \ +// _2_ _7_ +// / \ / \ +// 1 3_ 6 8_ +// \ \ +// 4 9 +var rbTree = new data_structure_typed_1.RedBlackTree(entries); +rbTree.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 +var avl = new data_structure_typed_1.AVLTree(entries); +avl.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 +var treeMulti = new data_structure_typed_1.TreeMultiMap(entries); +treeMulti.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 +var hm = new data_structure_typed_1.HashMap(entries); +hm.print(); +// [[6, "6"], [1, "1"], [2, "2"], [7, "7"], [5, "5"], [3, "3"], [4, "4"], [9, "9"], [8, "8"]] +var rbTreeH = new data_structure_typed_1.RedBlackTree(hm); +rbTreeH.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 +var pq = new data_structure_typed_1.MinPriorityQueue(orgArr); +pq.print(); +// [1, 5, 2, 7, 6, 3, 4, 9, 8] +var bst1 = new data_structure_typed_1.BST(pq); +bst1.print(); +// _____5___ +// / \ +// _2_ _7_ +// / \ / \ +// 1 3_ 6 8_ +// \ \ +// 4 9 +var dq1 = new data_structure_typed_1.Deque(orgArr); +dq1.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +var rbTree1 = new data_structure_typed_1.RedBlackTree(dq1); +rbTree1.print(); +// _____5___ +// / \ +// _2___ _7___ +// / \ / \ +// 1 _4 6 _9 +// / / +// 3 8 +var trie2 = new data_structure_typed_1.Trie(orgStrArr); +trie2.print(); +// ['trie', 'trial', 'triangle', 'trick', 'trip', 'tree', 'trend', 'track', 'trace', 'transmit'] +var heap2 = new data_structure_typed_1.Heap(trie2, { + comparator: function (a, b) { + return Number(a) - Number(b); + } +}); +heap2.print(); +// ['transmit', 'trace', 'tree', 'trend', 'track', 'trial', 'trip', 'trie', 'trick', 'triangle'] +var dq2 = new data_structure_typed_1.Deque(heap2); +dq2.print(); +// ['transmit', 'trace', 'tree', 'trend', 'track', 'trial', 'trip', 'trie', 'trick', 'triangle'] +var entries2 = dq2.map(function (el, i) { + return [i, el]; +}); +var avl2 = new data_structure_typed_1.AVLTree(entries2); +avl2.print(); +// ___3_______ +// / \ +// _1_ ___7_ +// / \ / \ +// 0 2 _5_ 8_ +// / \ \ +// 4 6 9 diff --git a/test/integration/compile.ts b/test/integration/compile.ts new file mode 100644 index 0000000..6e0a4dc --- /dev/null +++ b/test/integration/compile.ts @@ -0,0 +1,171 @@ +import { + AVLTree, + BinaryTree, + BST, + Deque, + DoublyLinkedList, + HashMap, + Heap, + MaxPriorityQueue, + MinHeap, + MinPriorityQueue, + Queue, + RedBlackTree, + SinglyLinkedList, + Stack, + TreeMultiMap, + Trie +} from 'data-structure-typed'; + +const orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8]; +const orgStrArr = ['trie', 'trial', 'trick', 'trip', 'tree', 'trend', 'triangle', 'track', 'trace', 'transmit']; +const entries: [number, string][] = [ + [6, '6'], + [1, '1'], + [2, '2'], + [7, '7'], + [5, '5'], + [3, '3'], + [4, '4'], + [9, '9'], + [8, '8'] +]; + +const queue = new Queue(orgArr); +queue.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] + +const deque = new Deque(orgArr); +deque.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] + +const sList = new SinglyLinkedList(orgArr); +sList.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] + +const dList = new DoublyLinkedList(orgArr); +dList.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] + +const stack = new Stack(orgArr); +stack.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] + +const minHeap = new MinHeap(orgArr); +minHeap.print(); +// [1, 5, 2, 7, 6, 3, 4, 9, 8] + +const maxPQ = new MaxPriorityQueue(orgArr); +maxPQ.print(); +// [9, 8, 4, 7, 5, 2, 3, 1, 6] + +const biTree = new BinaryTree(entries); +biTree.print(); +// ___6___ +// / \ +// ___1_ _2_ +// / \ / \ +// _7_ 5 3 4 +// / \ +// 9 8 + +const bst = new BST(entries); +bst.print(); +// _____5___ +// / \ +// _2_ _7_ +// / \ / \ +// 1 3_ 6 8_ +// \ \ +// 4 9 + +const rbTree = new RedBlackTree(entries); +rbTree.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 + +const avl = new AVLTree(entries); +avl.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 + +const treeMulti = new TreeMultiMap(entries); +treeMulti.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 + +const hm = new HashMap(entries); +hm.print(); +// [[6, "6"], [1, "1"], [2, "2"], [7, "7"], [5, "5"], [3, "3"], [4, "4"], [9, "9"], [8, "8"]] + +const rbTreeH = new RedBlackTree(hm); +rbTreeH.print(); +// ___4___ +// / \ +// _2_ _6___ +// / \ / \ +// 1 3 5 _8_ +// / \ +// 7 9 + +const pq = new MinPriorityQueue(orgArr); +pq.print(); +// [1, 5, 2, 7, 6, 3, 4, 9, 8] + +const bst1 = new BST(pq); +bst1.print(); +// _____5___ +// / \ +// _2_ _7_ +// / \ / \ +// 1 3_ 6 8_ +// \ \ +// 4 9 + +const dq1 = new Deque(orgArr); +dq1.print(); +// [6, 1, 2, 7, 5, 3, 4, 9, 8] +const rbTree1 = new RedBlackTree(dq1); +rbTree1.print(); +// _____5___ +// / \ +// _2___ _7___ +// / \ / \ +// 1 _4 6 _9 +// / / +// 3 8 + +const trie2 = new Trie(orgStrArr); +trie2.print(); +// ['trie', 'trial', 'triangle', 'trick', 'trip', 'tree', 'trend', 'track', 'trace', 'transmit'] +const heap2 = new Heap(trie2, { comparator: (a, b) => Number(a) - Number(b) }); +heap2.print(); +// ['transmit', 'trace', 'tree', 'trend', 'track', 'trial', 'trip', 'trie', 'trick', 'triangle'] +const dq2 = new Deque(heap2); +dq2.print(); +// ['transmit', 'trace', 'tree', 'trend', 'track', 'trial', 'trip', 'trie', 'trick', 'triangle'] +const entries2 = dq2.map((el, i) => [i, el]); +const avl2 = new AVLTree(entries2); +avl2.print(); +// ___3_______ +// / \ +// _1_ ___7_ +// / \ / \ +// 0 2 _5_ 8_ +// / \ \ +// 4 6 9 diff --git a/test/integration/index.html b/test/integration/index.html index fe7c6be..f985dee 100644 --- a/test/integration/index.html +++ b/test/integration/index.html @@ -12,7 +12,7 @@ @@ -44,7 +44,7 @@ for (let i = 0; i < n; i++) { queue.push(i); } - }) + }); let last = 0; @@ -52,7 +52,7 @@ for (let i = 0; i < n; i++) { last = queue.shift(); } - }) + }); } catch (e) { console.error(e); @@ -80,7 +80,7 @@ const tree = new BinaryTree(); tree.add(3); tree.add(12); - tree.addMany([1, 6, 9, 8, 5, 2, 3, 4, 7]) + tree.addMany([1, 6, 9, 8, 5, 2, 3, 4, 7]); tree.add(10); console.log(tree.isPerfectlyBalanced(), `tree.isPerfectlyBalanced()`); tree.print(undefined, { isShowUndefined: true }); @@ -112,7 +112,7 @@ console.log((performance.now() - tS).toFixed(2), `RedBlackTree ${n.toLocaleString()} add`); console.log(`rbTree.size`, rbTree.size); for (let i = 0; i < n - 8; i++) { - rbTree.delete(i) + rbTree.delete(i); } rbTree.print(rbTree.root, { isShowRedBlackNIL: true }); const cS = performance.now(); @@ -181,34 +181,34 @@ const objBST = new BST(); - objBST.add(11, { "name": "Pablo", "age": 15 }); - objBST.add(3, { "name": "Kirk", "age": 1 }); + objBST.add(11, { 'name': 'Pablo', 'age': 15 }); + objBST.add(3, { 'name': 'Kirk', 'age': 1 }); 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 } + { '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 } ] ); - objBST.print() + objBST.print(); objBST.delete(11); - objBST.print() + objBST.print(); const rbTree = new RedBlackTree(); - rbTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]) + rbTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]); rbTree.isAVLBalanced(); // true rbTree.delete(10); rbTree.isAVLBalanced(); // true @@ -216,16 +216,16 @@ rbTree.print(); rbTree.delete(14); - rbTree.print() + rbTree.print(); const avlTree = new AVLTree(); - avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]) + avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]); avlTree.isAVLBalanced(); // true avlTree.delete(10); avlTree.isAVLBalanced(); // true - avlTree.print() + avlTree.print(); avlTree.delete(14); - avlTree.print() + avlTree.print(); } catch (e) { console.error(e); } @@ -250,7 +250,7 @@ Trie } = dataStructureTyped; const orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8]; - const orgStrArr = ["trie", "trial", "trick", "trip", "tree", "trend", "triangle", "track", "trace", "transmit"]; + const orgStrArr = ['trie', 'trial', 'trick', 'trip', 'tree', 'trend', 'triangle', 'track', 'trace', 'transmit']; const entries = [[6, 6], [1, 1], [2, 2], [7, 7], [5, 5], [3, 3], [4, 4], [9, 9], [8, 8]]; const queue = new Queue(orgArr); @@ -327,7 +327,7 @@ // 7 9 const hm = new HashMap(entries); - hm.print() // [[6, 6], [1, 1], [2, 2], [7, 7], [5, 5], [3, 3], [4, 4], [9, 9], [8, 8]] + hm.print(); // [[6, 6], [1, 1], [2, 2], [7, 7], [5, 5], [3, 3], [4, 4], [9, 9], [8, 8]] const rbTreeH = new RedBlackTree(hm); rbTreeH.print(); @@ -408,39 +408,39 @@ const minNumHeap = new dataStructureTyped.MinHeap([1, 6, 2, 0, 5]); minNumHeap.add(9); - minNumHeap.has(1) // true - minNumHeap.has(2) // true - minNumHeap.poll() // 0 - minNumHeap.poll() // 1 - minNumHeap.peek() // 2 + minNumHeap.has(1); // true + minNumHeap.has(2); // true + minNumHeap.poll(); // 0 + minNumHeap.poll(); // 1 + minNumHeap.peek(); // 2 minNumHeap.has(1); // false minNumHeap.has(2); // true const arrFromHeap = minNumHeap.toArray(); - arrFromHeap.length // 4 - arrFromHeap[0] // 2 - arrFromHeap[1] // 5 - arrFromHeap[2] // 9 - arrFromHeap[3] // 6 - minNumHeap.sort() // [2, 5, 6, 9] + arrFromHeap.length; // 4 + arrFromHeap[0]; // 2 + arrFromHeap[1]; // 5 + arrFromHeap[2]; // 9 + arrFromHeap[3]; // 6 + minNumHeap.sort(); // [2, 5, 6, 9] const maxHeap = new dataStructureTyped.MaxHeap([], { comparator: (a, b) => b.keyA - a.keyA }); const obj1 = { keyA: 'a1' }, obj6 = { keyA: 'a6' }, obj5 = { keyA: 'a5' }, obj2 = { keyA: 'a2' }, obj0 = { keyA: 'a0' }, obj9 = { keyA: 'a9' }; maxHeap.add(obj1); - maxHeap.has(obj1) // true - maxHeap.has(obj9) // false + maxHeap.has(obj1); // true + maxHeap.has(obj9); // false maxHeap.add(obj6); - maxHeap.has(obj6) // true + maxHeap.has(obj6); // true maxHeap.add(obj5); maxHeap.add(obj2); maxHeap.add(obj0); maxHeap.add(obj9); - maxHeap.has(obj9) // true + maxHeap.has(obj9); // true const peek9 = maxHeap.peek(); - console.log(peek9.keyA) // 'a9' + console.log(peek9.keyA); // 'a9' const heapToArr = maxHeap.toArray(); console.log(heapToArr.map(ele => ele?.keyA)); // ['a9', 'a2', 'a6', 'a1', 'a0', 'a5'] @@ -449,7 +449,7 @@ let i = 0; while (maxHeap.size > 0) { const polled = maxHeap.poll(); - console.log(polled.keyA) // values[i] + console.log(polled.keyA); // values[i] i++; } }