diff --git a/src/data-structures/binary-tree/avl-tree-counter.ts b/src/data-structures/binary-tree/avl-tree-counter.ts index 314d621..08d2c75 100644 --- a/src/data-structures/binary-tree/avl-tree-counter.ts +++ b/src/data-structures/binary-tree/avl-tree-counter.ts @@ -9,15 +9,15 @@ import type { AVLTreeCounterOptions, BinaryTreeDeleteResult, BSTNOptKeyOrNode, - BTNRep, EntryCallback, - IterationType, - OptNodeOrNull + IterationType } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { AVLTree, AVLTreeNode } from './avl-tree'; export class AVLTreeCounterNode extends AVLTreeNode { + override parent?: AVLTreeCounterNode = undefined; + /** * 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 @@ -33,28 +33,26 @@ export class AVLTreeCounterNode extends AVLTreeNode { this.count = count; } - override parent?: AVLTreeCounterNode = undefined; + override _left?: AVLTreeCounterNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): AVLTreeCounterNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: AVLTreeCounterNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: AVLTreeCounterNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): AVLTreeCounterNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: AVLTreeCounterNode | null | undefined) { if (v) { v.parent = this; } @@ -78,7 +76,9 @@ export class AVLTreeCounter> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined | R + > = [], options?: AVLTreeCounterOptions ) { super([], options); @@ -145,12 +145,14 @@ export class AVLTreeCounter>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined`. * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `AVLTreeCounterNode` class. */ - override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is AVLTreeCounterNode { + override isNode( + keyNodeOrEntry: K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is AVLTreeCounterNode { return keyNodeOrEntry instanceof AVLTreeCounterNode; } @@ -160,9 +162,9 @@ export class AVLTreeCounter>} keyNodeOrEntry - The + * @param {K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The * `keyNodeOrEntry` 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 `K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined`, 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. @@ -171,7 +173,11 @@ export class AVLTreeCounter>, value?: V, count = 1): boolean { + override add( + keyNodeOrEntry: K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined, + value?: V, + count = 1 + ): boolean { const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value, count); if (newNode === undefined) return false; @@ -189,7 +195,7 @@ export class AVLTreeCounter>} keyNodeOrEntry - The `predicate` + * @param {K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - 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. @@ -203,7 +209,7 @@ export class AVLTreeCounter>, + keyNodeOrEntry: K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined, ignoreCount = false ): BinaryTreeDeleteResult>[] { const deletedResult: BinaryTreeDeleteResult>[] = []; @@ -276,6 +282,7 @@ export class AVLTreeCounter>} keyNodeOrEntry - The - * `keyNodeOrEntry` parameter can be of type `R` or `BTNRep>`. + * @param {K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The + * `keyNodeOrEntry` parameter can be of type `R` or `K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined`. * @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`. @@ -384,7 +391,7 @@ export class AVLTreeCounter object or undefined. */ protected override _keyValueNodeOrEntryToNodeAndValue( - keyNodeOrEntry: BTNRep>, + keyNodeOrEntry: K | AVLTreeCounterNode | [K | null | undefined, V | undefined] | null | undefined, value?: V, count = 1 ): [AVLTreeCounterNode | undefined, V | undefined] { 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 141594a..d1e819b 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -5,11 +5,13 @@ * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ -import { AVLTreeMultiMapOptions, BTNOptKeyOrNull, BTNRep, OptNodeOrNull } from '../../types'; +import { AVLTreeMultiMapOptions, BTNOptKeyOrNull } from '../../types'; import { AVLTree, AVLTreeNode } from './avl-tree'; import { IBinaryTree } from '../../interfaces'; export class AVLTreeMultiMapNode extends AVLTreeNode { + override parent?: AVLTreeMultiMapNode = undefined; + /** * This TypeScript constructor initializes an object with a key of type K and an array of values of * type V. @@ -23,28 +25,26 @@ export class AVLTreeMultiMapNode extends AVLTreeNode { super(key, value); } - override parent?: AVLTreeMultiMapNode = undefined; + override _left?: AVLTreeMultiMapNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): AVLTreeMultiMapNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: AVLTreeMultiMapNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: AVLTreeMultiMapNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): AVLTreeMultiMapNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: AVLTreeMultiMapNode | null | undefined) { if (v) { v.parent = this; } @@ -71,7 +71,9 @@ export class AVLTreeMultiMap> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | AVLTreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined | R + > = [], options?: AVLTreeMultiMapOptions ) { super([], { ...options, isMapMode: true }); @@ -117,7 +119,9 @@ export class AVLTreeMultiMap(key, []); } - override add(node: BTNRep>): boolean; + override add( + node: K | AVLTreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined + ): boolean; override add(key: K, value: V): boolean; @@ -127,7 +131,7 @@ export class AVLTreeMultiMap> | K} keyNodeOrEntry - The `keyNodeOrEntry` + * @param {K | AVLTreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined | K} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `override add` method can be either a key-value pair entry or just a key. If it * is a key-value pair entry, it will be in the format `[key, values]`, where `key` is the key and * `values` @@ -137,7 +141,10 @@ export class AVLTreeMultiMap> | K, value?: V): boolean { + override add( + keyNodeOrEntry: K | AVLTreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined | K, + value?: V + ): boolean { if (this.isRealNode(keyNodeOrEntry)) return super.add(keyNodeOrEntry); const _commonAdd = (key?: BTNOptKeyOrNull, values?: V[]) => { @@ -180,7 +187,7 @@ export class AVLTreeMultiMap> | K} keyNodeOrEntry - The `keyNodeOrEntry` + * @param {K | AVLTreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined | K} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `deleteValue` function can be either a `BTNRep` object representing a key-value * pair in the AVLTreeMultiMapNode, or just the key itself. * @param {V} value - The `value` parameter in the `deleteValue` function represents the specific @@ -191,7 +198,10 @@ export class AVLTreeMultiMap> | K, value: V): boolean { + deleteValue( + keyNodeOrEntry: K | AVLTreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined | K, + value: V + ): boolean { const values = this.get(keyNodeOrEntry); if (Array.isArray(values)) { const index = values.indexOf(value); diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 741edfc..ca73888 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -6,17 +6,12 @@ * @license MIT License */ import { BST, BSTNode } from './bst'; -import type { - AVLTreeOptions, - BinaryTreeDeleteResult, - BSTNOptKeyOrNode, - BTNRep, - EntryCallback, - OptNodeOrNull -} from '../../types'; +import type { AVLTreeOptions, BinaryTreeDeleteResult, BSTNOptKeyOrNode, EntryCallback } from '../../types'; import { IBinaryTree } from '../../interfaces'; export class AVLTreeNode extends BSTNode { + override parent?: AVLTreeNode = undefined; + /** * This TypeScript constructor function initializes an instance with a key and an optional value. * @param {K} key - The `key` parameter is typically used to uniquely identify an object or element @@ -30,28 +25,26 @@ export class AVLTreeNode extends BSTNode { super(key, value); } - override parent?: AVLTreeNode = undefined; + override _left?: AVLTreeNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): AVLTreeNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: AVLTreeNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: AVLTreeNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): AVLTreeNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: AVLTreeNode | null | undefined) { if (v) { v.parent = this; } @@ -76,7 +69,8 @@ export class AVLTree>` objects or `R` objects. It is + * iterable that can contain either ` + K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined ` objects or `R` objects. It is * used to initialize the AVLTree with key-value pairs or raw data entries. If provided * @param [options] - The `options` parameter in the constructor is of type `AVLTreeOptions`. It is an optional parameter that allows you to specify additional options for configuring the @@ -84,7 +78,9 @@ export class AVLTree> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined | R + > = [], options?: AVLTreeOptions ) { super([], options); @@ -133,12 +129,15 @@ export class AVLTree>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or ` + K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined `. * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `AVLTreeNode` class. */ - override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is AVLTreeNode { + override isNode( + keyNodeOrEntry: K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is AVLTreeNode { return keyNodeOrEntry instanceof AVLTreeNode; } @@ -148,13 +147,17 @@ export class AVLTree>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can accept values of type `R`, `BTNRep>` + * @param { K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can accept values of type `R`, ` + K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined ` * @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(keyNodeOrEntry: BTNRep>, value?: V): boolean { + override add( + keyNodeOrEntry: K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined, + value?: V + ): boolean { if (keyNodeOrEntry === null) return false; const inserted = super.add(keyNodeOrEntry, value); if (inserted) this._balancePath(keyNodeOrEntry); @@ -167,14 +170,16 @@ export class AVLTree>} keyNodeOrEntry - The `keyNodeOrEntry` + * @param { K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry` * 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(keyNodeOrEntry: BTNRep>): BinaryTreeDeleteResult>[] { + override delete( + keyNodeOrEntry: K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): BinaryTreeDeleteResult>[] { const deletedResults = super.delete(keyNodeOrEntry); for (const { needBalanced } of deletedResults) { if (needBalanced) { @@ -487,10 +492,11 @@ export class AVLTree>} node - The `node` parameter can be of type `R` or - * `BTNRep>`. + * @param { K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined } node - The `node` parameter can be of type `R` or + * ` + K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined `. */ - protected _balancePath(node: BTNRep>): void { + protected _balancePath(node: K | AVLTreeNode | [K | null | undefined, V | undefined] | null | undefined): void { node = this.ensureNode(node); const path = this.getPathToRoot(node, node => node, false); // first O(log n) + O(log n) for (let i = 0; i < path.length; i++) { diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index de27a58..d70a110 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -11,7 +11,6 @@ import type { BinaryTreeOptions, BinaryTreePrintOptions, BTNEntry, - BTNRep, DFSOrderPattern, DFSStackItem, EntryCallback, @@ -36,6 +35,10 @@ import { DFSOperation, Range } from '../../common'; * @template BinaryTreeNode - The type of the family relationship in the binary tree. */ export class BinaryTreeNode { + key: K; + value?: V; + parent?: BinaryTreeNode = undefined; + /** * The constructor function initializes an object with a key and an optional value in TypeScript. * @param {K} key - The `key` parameter in the constructor function is used to store the key value @@ -49,32 +52,26 @@ export class BinaryTreeNode { this.value = value; } - key: K; + _left?: BinaryTreeNode | null | undefined = undefined; - value?: V; - - parent?: BinaryTreeNode = undefined; - - _left?: OptNodeOrNull> = undefined; - - get left(): OptNodeOrNull> { + get left(): BinaryTreeNode | null | undefined { return this._left; } - set left(v: OptNodeOrNull>) { + set left(v: BinaryTreeNode | null | undefined) { if (v) { v.parent = this as unknown as BinaryTreeNode; } this._left = v; } - _right?: OptNodeOrNull> = undefined; + _right?: BinaryTreeNode | null | undefined = undefined; - get right(): OptNodeOrNull> { + get right(): BinaryTreeNode | null | undefined { return this._right; } - set right(v: OptNodeOrNull>) { + set right(v: BinaryTreeNode | null | undefined) { if (v) { v.parent = this; } @@ -137,17 +134,21 @@ export class BinaryTree implements IBinaryTree { + iterationType: IterationType = 'ITERATIVE'; + /** * This TypeScript constructor function initializes a binary tree with optional options and adds * elements based on the provided input. * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an - * iterable that can contain either objects of type `BTNRep>` or `R`. It + * iterable that can contain either objects of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` or `R`. It * is used to initialize the binary tree with keys, nodes, entries, or raw data. * @param [options] - The `options` parameter in the constructor is an optional object that can * contain the following properties: */ constructor( - keysNodesEntriesOrRaws: Iterable> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | R + > = [], options?: BinaryTreeOptions ) { super(); @@ -163,8 +164,6 @@ export class BinaryTree>; + protected _root?: BinaryTreeNode | null | undefined; - get root(): OptNodeOrNull> { + get root(): BinaryTreeNode | null | undefined { return this._root; } @@ -249,8 +248,8 @@ export class BinaryTree>} keyNodeOrEntry - The `keyNodeOrEntry` - * parameter in the `ensureNode` function can be of type `BTNRep>` or `R`. It + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry` + * parameter in the `ensureNode` function can be of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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 @@ -259,9 +258,9 @@ export class BinaryTree>, + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType: IterationType = this.iterationType - ): OptNodeOrNull> { + ): BinaryTreeNode | null | undefined { if (keyNodeOrEntry === null) return null; if (keyNodeOrEntry === undefined) return; if (keyNodeOrEntry === this._NIL) return; @@ -283,7 +282,7 @@ export class BinaryTree>} keyNodeOrEntry - The parameter + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter * `keyNodeOrEntry` 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. @@ -292,7 +291,9 @@ export class BinaryTree>): keyNodeOrEntry is BinaryTreeNode { + isNode( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is BinaryTreeNode { return keyNodeOrEntry instanceof BinaryTreeNode; } @@ -301,12 +302,14 @@ export class BinaryTree> | R} keyNodeEntryOrRaw - BTNRep> + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | R} keyNodeEntryOrRaw - K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined * @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: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | R + ): keyNodeEntryOrRaw is R { return this._toEntryFn !== undefined && typeof keyNodeEntryOrRaw === 'object'; } @@ -315,15 +318,17 @@ export class BinaryTree>} keyNodeOrEntry - The `keyNodeOrEntry` - * parameter in the `isRealNode` function can be of type `BTNRep>` or `R`. + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry` + * parameter in the `isRealNode` function can be of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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 `keyNodeOrEntry` 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(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BinaryTreeNode { + isRealNode( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is BinaryTreeNode { if (keyNodeOrEntry === this._NIL || keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false; return this.isNode(keyNodeOrEntry); } @@ -333,14 +338,16 @@ export class BinaryTree>} keyNodeOrEntry - The parameter + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter * `keyNodeOrEntry` in the `isRealNodeOrNull` function can be of type `BTNRep>` or `R`. It is a union type that can either be a key, a node, an entry, or * @returns The function `isRealNodeOrNull` is returning a boolean value. It checks if the input * `keyNodeOrEntry` is either `null` or a real node, and returns `true` if it is a node or * `null`, and `false` otherwise. */ - isRealNodeOrNull(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BinaryTreeNode | null { + isRealNodeOrNull( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is BinaryTreeNode | null { return keyNodeOrEntry === null || this.isRealNode(keyNodeOrEntry); } @@ -349,12 +356,12 @@ export class BinaryTree>} keyNodeOrEntry - BTNRep | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - BTNRep> * @returns The function is checking if the `keyNodeOrEntry` parameter is equal to the `_NIL` * property of the current object and returning a boolean value based on that comparison. */ - isNIL(keyNodeOrEntry: BTNRep>): boolean { + isNIL(keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined): boolean { return keyNodeOrEntry === this._NIL; } @@ -363,9 +370,9 @@ export class BinaryTree> | NodePredicate> | Range} keyNodeEntryOrPredicate + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate> | Range} keyNodeEntryOrPredicate * - The `keyNodeEntryOrPredicate` parameter in the `isRange` function can be - * of type `BTNRep>`, `NodePredicate>`, or + * of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined `, `NodePredicate>`, or * `Range`. The function checks if the `keyNodeEntry * @returns The `isRange` function is checking if the `keyNodeEntryOrPredicate` parameter is an * instance of the `Range` class. If it is an instance of `Range`, the function will return `true`, @@ -373,7 +380,14 @@ export class BinaryTree> | NodePredicate> | Range + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate> + | Range ): keyNodeEntryOrPredicate is Range { return keyNodeEntryOrPredicate instanceof Range; } @@ -384,14 +398,14 @@ export class BinaryTree>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `BTNRep>` or `R`. It represents a + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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 * `keyNodeOrEntry` is a leaf node in a binary tree. */ - isLeaf(keyNodeOrEntry: BTNRep>): boolean { + isLeaf(keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined): boolean { keyNodeOrEntry = this.ensureNode(keyNodeOrEntry); if (keyNodeOrEntry === undefined) return false; if (keyNodeOrEntry === null) return true; @@ -404,14 +418,16 @@ export class BinaryTree>} keyNodeOrEntry - The `keyNodeOrEntry` - * parameter in the `isEntry` function can be of type `BTNRep>` or type `R`. + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `keyNodeOrEntry` + * parameter in the `isEntry` function can be of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` or type `R`. * The function checks if the provided `keyNodeOrEntry` is of type `BTN * @returns The `isEntry` function is checking if the `keyNodeOrEntry` 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(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BTNEntry { + isEntry( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is BTNEntry { return Array.isArray(keyNodeOrEntry) && keyNodeOrEntry.length === 2; } @@ -437,7 +453,7 @@ export class BinaryTree>} keyNodeOrEntry - The `add` method you provided + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `add` method you provided * seems to be for adding a new node to a binary tree structure. The `keyNodeOrEntry` * parameter in the method can accept different types of values: * @param {V} [value] - The `value` parameter in the `add` method represents the value associated @@ -448,7 +464,10 @@ export class BinaryTree>, value?: V): boolean { + add( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + value?: V + ): boolean { const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (newNode === undefined) return false; @@ -515,7 +534,7 @@ export class BinaryTree>` or `R`. + * `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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 @@ -525,7 +544,9 @@ export class BinaryTree> | R>, + keysNodesEntriesOrRaws: Iterable< + K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | R + >, values?: Iterable ): boolean[] { // TODO not sure addMany not be run multi times @@ -571,13 +592,15 @@ export class BinaryTree>` objects or `R` + * method can accept an iterable containing a mix of `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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>, + keysNodesEntriesOrRaws: Iterable< + K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | R + >, values?: Iterable ): void { this.clear(); @@ -590,7 +613,7 @@ export class BinaryTree>} keyNodeOrEntry + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry * - 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 @@ -599,7 +622,9 @@ export class BinaryTree>): BinaryTreeDeleteResult>[] { + delete( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): BinaryTreeDeleteResult>[] { const deletedResult: BinaryTreeDeleteResult>[] = []; if (!this._root) return deletedResult; @@ -650,15 +675,15 @@ export class BinaryTree> | NodePredicate>} keyNodeEntryOrPredicate - The + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate - The * `keyNodeEntryOrPredicate` 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>} startNode - The `startNode` parameter in the `search` function is + * extends `NodeCallback | null>`. The default value for `callback` is `this._DEFAULT_NODE_CALLBACK` if + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -668,11 +693,17 @@ export class BinaryTree>>( - keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, + search | null>>( + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate | null>, onlyOne = false, callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { if (keyNodeEntryOrPredicate === undefined) return []; @@ -713,18 +744,31 @@ export class BinaryTree + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate>, + onlyOne?: boolean, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + iterationType?: IterationType + ): BinaryTreeNode[]; + /** * Time Complexity: O(n) * Space Complexity: O(k + log n) * * 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> | NodePredicate>} keyNodeEntryOrPredicate + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate * - 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 * `keyNodeEntryOrPredicate` parameter. If `onlyOne` is set to `true`, the function will - * @param {BTNRep>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -735,11 +779,17 @@ export class BinaryTree> | NodePredicate>, + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate | null>, onlyOne = false, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType - ): BinaryTreeNode[] { + ): (BinaryTreeNode | null)[] { return this.search(keyNodeEntryOrPredicate, onlyOne, node => node, startNode, iterationType); } @@ -749,10 +799,10 @@ export class BinaryTree> | NodePredicate>} keyNodeEntryOrPredicate + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate * - The `keyNodeEntryOrPredicate` parameter in the `getNode` function can accept a key, * node, entry, raw data, or a predicate function. - * @param {BTNRep>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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. @@ -764,10 +814,16 @@ export class BinaryTree> | NodePredicate>, - startNode: BTNRep> = this._root, + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate | null>, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType - ): OptNodeOrNull> { + ): BinaryTreeNode | null | undefined { return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType)[0]; } @@ -777,10 +833,10 @@ export class BinaryTree> | NodePredicate>} keyNodeEntryOrPredicate + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate * - The `keyNodeEntryOrPredicate` parameter in the `get` method can accept one of the * following types: - * @param {BTNRep>} startNode - The `startNode` parameter in the `get` + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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`). @@ -794,8 +850,8 @@ export class BinaryTree>, - startNode: BTNRep> = this._root, + keyNodeEntryOrPredicate: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): V | undefined { if (this._isMapMode) { @@ -806,16 +862,28 @@ export class BinaryTree + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate>, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + iterationType?: IterationType + ): boolean; + /** * Time Complexity: O(n) * Space Complexity: O(log n) * * The `has` function in TypeScript checks if a specified key, node, entry, raw data, or predicate * exists in the data structure. - * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate * - The `keyNodeEntryOrPredicate` parameter in the `override has` method can accept one of * the following types: - * @param {BTNRep>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -828,8 +896,14 @@ export class BinaryTree> | NodePredicate>, - startNode: BTNRep> = this._root, + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate | null>, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): boolean { return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType).length > 0; @@ -865,7 +939,7 @@ export class BinaryTree>} startNode - The `startNode` parameter is the starting + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -874,7 +948,9 @@ export class BinaryTree> = this._root): boolean { + isPerfectlyBalanced( + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root + ): boolean { return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode); } @@ -884,7 +960,7 @@ export class BinaryTree>} startNode - The `startNode` parameter in the `isBST` + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -897,7 +973,7 @@ export class BinaryTree> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): boolean { // TODO there is a bug @@ -905,7 +981,7 @@ export class BinaryTree>, min: number, max: number): boolean => { + const dfs = (cur: BinaryTreeNode | null | undefined, min: number, max: number): boolean => { if (!this.isRealNode(cur)) return true; const numKey = Number(cur.key); if (numKey <= min || numKey >= max) return false; @@ -920,7 +996,7 @@ export class BinaryTree> = startNode; + let curr: BinaryTreeNode | null | undefined = startNode; while (this.isRealNode(curr) || stack.length > 0) { while (this.isRealNode(curr)) { stack.push(curr); @@ -945,10 +1021,10 @@ export class BinaryTree>} dist - The `dist` parameter in the `getDepth` + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -957,8 +1033,8 @@ export class BinaryTree>, - startNode: BTNRep> = this._root + dist: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root ): number { let distEnsured = this.ensureNode(dist); const beginRootEnsured = this.ensureNode(startNode); @@ -979,7 +1055,7 @@ export class BinaryTree>} startNode - The `startNode` parameter is the starting + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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. @@ -991,14 +1067,14 @@ export class BinaryTree> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = 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: BinaryTreeNode | null | undefined): number => { if (!this.isRealNode(cur)) return -1; const leftHeight = _getMaxHeight(cur.left); const rightHeight = _getMaxHeight(cur.right); @@ -1029,7 +1105,7 @@ export class BinaryTree>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 +1118,14 @@ export class BinaryTree> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = 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: BinaryTreeNode | null | undefined): number => { if (!this.isRealNode(cur)) return 0; if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return 0; const leftMinHeight = _getMinHeight(cur.left); @@ -1060,8 +1136,8 @@ export class BinaryTree[] = []; - let node: OptNodeOrNull> = startNode, - last: OptNodeOrNull> = null; + let node: BinaryTreeNode | null | undefined = startNode, + last: BinaryTreeNode | null | undefined = null; const depths: Map, number> = new Map(); while (stack.length > 0 || node) { @@ -1097,7 +1173,7 @@ export class BinaryTree>} beginNode - The `beginNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1108,7 +1184,7 @@ export class BinaryTree>>>( - beginNode: BTNRep>, + beginNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, callback: C = this._DEFAULT_NODE_CALLBACK as C, isReverse = false ): ReturnType[] { @@ -1135,7 +1211,7 @@ export class BinaryTree>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1149,7 +1225,7 @@ export class BinaryTree>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); @@ -1185,7 +1261,7 @@ export class BinaryTree>>`, * which means it is a callback function that can accept either an optional binary tree node or null * as - * @param {BTNRep>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1199,7 +1275,7 @@ export class BinaryTree>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); @@ -1239,7 +1315,7 @@ export class BinaryTree): BinaryTreeNode { if (this.isRealNode(node.left)) { - let predecessor: OptNodeOrNull> = node.left; + let predecessor: BinaryTreeNode | null | undefined = node.left; while (!this.isRealNode(predecessor) || (this.isRealNode(predecessor.right) && predecessor.right !== node)) { if (this.isRealNode(predecessor)) { predecessor = predecessor.right; @@ -1264,7 +1340,7 @@ export class BinaryTree | null): OptNodeOrNull> { + getSuccessor(x?: K | BinaryTreeNode | null): BinaryTreeNode | null | undefined { x = this.ensureNode(x); if (!this.isRealNode(x)) return undefined; @@ -1272,7 +1348,7 @@ export class BinaryTree node, x.right); } - let y: OptNodeOrNull> = x.parent; + let y: BinaryTreeNode | null | undefined = x.parent; while (this.isRealNode(y) && x === y.right) { x = y; y = y.parent; @@ -1283,14 +1359,16 @@ export class BinaryTree>>( callback?: C, pattern?: DFSOrderPattern, - startNode?: BTNRep>, + onlyOne?: boolean, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType?: IterationType ): ReturnType[]; dfs | null>>( callback?: C, pattern?: DFSOrderPattern, - startNode?: BTNRep>, + onlyOne?: boolean, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType?: IterationType, includeNull?: boolean ): ReturnType[]; @@ -1299,50 +1377,53 @@ export class BinaryTree>`. 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>} 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 - * @param {IterationType} iterationType - The `iterationType` parameter in the `dfs` method specifies - * the type of iteration to be performed during the depth-first search traversal. It is used to - * determine the order in which nodes are visited during the traversal. - * @param [includeNull=false] - The `includeNull` parameter in the `dfs` method is a boolean flag - * that determines whether null values should be included in the traversal or not. If `includeNull` - * is set to `true`, then null values will be included in the traversal process. If it is set to - * `false`, - * @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. + * The function performs a depth-first search on a binary tree structure based on the specified + * parameters. + * @param {C} callback - The `callback` parameter is a function that will be called for each node + * visited during the depth-first search. It should accept a `BinaryTreeNode` as an argument and + * return an optional node or null. The default value for this parameter is `_DEFAULT_NODE_CALLBACK`. + * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `dfs` function specifies + * the order in which the nodes are visited during a depth-first search traversal. The possible + * values for the `pattern` parameter are: + * @param {boolean} [onlyOne=false] - The `onlyOne` parameter in the `dfs` function is a boolean flag + * that determines whether the depth-first search should stop after finding the first matching node + * or continue searching for all matching nodes. If `onlyOne` is set to `true`, the search will stop + * after finding the first matching node + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined} + * startNode - The `startNode` parameter in the `dfs` function can be one of the following types: + * @param {IterationType} iterationType - The `iterationType` parameter in the `dfs` function + * specifies the type of iteration to be performed during the Depth-First Search traversal. It is + * used to determine the order in which nodes are visited during the traversal. The possible values + * for `iterationType` are typically defined as an enum or a + * @param [includeNull=false] - The `includeNull` parameter in the `dfs` function determines whether + * null nodes should be included in the depth-first search traversal. If `includeNull` is set to + * `true`, null nodes will be included in the traversal process. If it is set to `false`, null nodes + * will be skipped + * @returns The `dfs` method is returning an array of the return type of the callback function `C`. */ dfs>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> = this._root, + onlyOne: boolean = false, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; - return this._dfs(callback, pattern, startNode, iterationType, includeNull); + return this._dfs(callback, pattern, onlyOne, startNode, iterationType, includeNull); } bfs>>( callback?: C, - startNode?: BTNRep>, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType?: IterationType, includeNull?: false ): ReturnType[]; bfs | null>>( callback?: C, - startNode?: BTNRep>, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1356,7 +1437,7 @@ export class BinaryTree` or `null`. - * @param {BTNRep>} startNode - The `startNode` parameter in the `bfs` + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1372,14 +1453,14 @@ export class BinaryTree | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; - const ans: ReturnType>>[] = []; + const ans: ReturnType | null>>[] = []; if (iterationType === 'RECURSIVE') { const queue: Queue>> = new Queue>>([ @@ -1434,7 +1515,7 @@ export class BinaryTree>} startNode - The `startNode` parameter in the `leaves` + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1446,11 +1527,11 @@ export class BinaryTree | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { startNode = this.ensureNode(startNode); - const leaves: ReturnType>>[] = []; + const leaves: ReturnType | null>>[] = []; if (!this.isRealNode(startNode)) return []; if (iterationType === 'RECURSIVE') { @@ -1483,14 +1564,14 @@ export class BinaryTree>>( callback?: C, - startNode?: BTNRep>, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType?: IterationType, includeNull?: false ): ReturnType[][]; listLevels | null>>( callback?: C, - startNode?: BTNRep>, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, iterationType?: IterationType, includeNull?: true ): ReturnType[][]; @@ -1504,7 +1585,7 @@ export class BinaryTree>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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. @@ -1521,7 +1602,7 @@ export class BinaryTree | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[][] { @@ -1566,6 +1647,12 @@ export class BinaryTree>>( + callback?: C, + pattern?: DFSOrderPattern, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): ReturnType[]; + /** * Time complexity: O(n) * Space complexity: O(n) @@ -1574,11 +1661,11 @@ export class BinaryTree>` type. The default value for `callback` is `this._DEFAULT + * `NodeCallback | null>` 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>} startNode - The `startNode` parameter in the `morris` + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1586,19 +1673,19 @@ export class BinaryTree>>( + morris | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> = this._root + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; - const ans: ReturnType>>[] = []; + const ans: ReturnType | null>>[] = []; - let cur: OptNodeOrNull> = startNode; - const _reverseEdge = (node: OptNodeOrNull>) => { - let pre: OptNodeOrNull> = null; - let next: OptNodeOrNull> = null; + let cur: BinaryTreeNode | null | undefined = startNode; + const _reverseEdge = (node: BinaryTreeNode | null | undefined) => { + let pre: BinaryTreeNode | null | undefined = null; + let next: BinaryTreeNode | null | undefined = null; while (node) { next = node.right; node.right = pre; @@ -1607,9 +1694,9 @@ export class BinaryTree>) => { - const tail: OptNodeOrNull> = _reverseEdge(node); - let cur: OptNodeOrNull> = tail; + const _printEdge = (node: BinaryTreeNode | null | undefined) => { + const tail: BinaryTreeNode | null | undefined = _reverseEdge(node); + let cur: BinaryTreeNode | null | undefined = tail; while (cur) { ans.push(callback(cur)); cur = cur.right; @@ -1689,22 +1776,6 @@ export class BinaryTree) { - this.bfs( - node => { - if (node === null) cloned.add(null); - else { - if (this._isMapMode) cloned.add([node.key, this._store.get(node.key)]); - else cloned.add([node.key, node.value]); - } - }, - this._root, - this.iterationType, - true - ); - if (this._isMapMode) cloned._store = this._store; - } - /** * Time Complexity: O(n) * Space Complexity: O(n) @@ -1770,7 +1841,7 @@ export class BinaryTree>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -1783,7 +1854,7 @@ export class BinaryTree> = this._root, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root, options?: BinaryTreePrintOptions ): string { const opts = { isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false, ...options }; @@ -1795,7 +1866,7 @@ export class BinaryTree>): void => { + const display = (root: BinaryTreeNode | null | undefined): void => { const [lines] = this._displayAux(root, opts); let paragraph = ''; for (const line of lines) { @@ -1818,37 +1889,56 @@ export class BinaryTree>} startNode - The `startNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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> = this._root) { + override print( + options?: BinaryTreePrintOptions, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = this._root + ) { console.log(this.toVisual(startNode, options)); } + protected _clone(cloned: BinaryTree) { + this.bfs( + node => { + if (node === null) cloned.add(null); + else { + if (this._isMapMode) cloned.add([node.key, this._store.get(node.key)]); + else cloned.add([node.key, node.value]); + } + }, + this._root, + this.iterationType, + true + ); + if (this._isMapMode) cloned._store = this._store; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) * * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object * or returns null. - * @param {BTNRep>} keyNodeOrEntry - The + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeOrEntry`, which - * can be of type `BTNRep>` or `R`. This parameter represents either a key, a + * can be of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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 + * (`BinaryTreeNode | null | undefined`) based on the input parameters provided. The function checks the type of the * input parameter (`keyNodeOrEntry`) and processes it accordingly to return a node or null * value. */ protected _keyValueNodeOrEntryToNodeAndValue( - keyNodeOrEntry: BTNRep>, + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, value?: V - ): [OptNodeOrNull>, V | undefined] { + ): [BinaryTreeNode | null | undefined, V | undefined] { if (keyNodeOrEntry === undefined) return [undefined, undefined]; if (keyNodeOrEntry === null) return [null, undefined]; @@ -1865,92 +1955,118 @@ export class BinaryTree>>( + callback: C, + pattern?: DFSOrderPattern, + onlyOne?: boolean, + startNode?: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + iterationType?: IterationType, + includeNull?: boolean, + shouldVisitLeft?: (node: BinaryTreeNode | null | undefined) => boolean, + shouldVisitRight?: (node: BinaryTreeNode | null | undefined) => boolean, + shouldVisitRoot?: (node: BinaryTreeNode | null | undefined) => boolean, + shouldProcessRoot?: (node: BinaryTreeNode | null | undefined) => boolean + ): ReturnType[]; + /** * Time complexity: O(n) * Space complexity: O(n) * - * The `_dfs` function performs a depth-first search traversal on a binary tree structure based on - * the specified order pattern and callback function. + * The `_dfs` function performs a depth-first search traversal on a binary tree, with customizable + * options for traversal order and node processing. * @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 + * called on each node visited during the depth-first search traversal. It is a generic type `C` that + * extends `NodeCallback | null>`. The default value for `callback` * @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>} 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 + * order in which the nodes are visited during a depth-first search traversal. It can have one of the + * following values: + * @param {boolean} [onlyOne=false] - The `onlyOne` parameter in the `_dfs` method is a boolean flag + * that determines whether the traversal should stop after processing a single node. If `onlyOne` is + * set to `true`, the traversal will return as soon as a single node is processed. If it is set to + * `false + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined} + * startNode - The `startNode` parameter in the `_dfs` method is used to specify the starting node + * for the depth-first search traversal. It can be provided in different forms: * @param {IterationType} iterationType - The `iterationType` parameter in the `_dfs` method - * specifies the type of iteration to be performed during the Depth-First Search (DFS) traversal of a - * binary tree. It can have two possible values: - * @param [includeNull=false] - The `includeNull` parameter in the `_dfs` method is a boolean flag - * that determines whether null nodes should be included in the depth-first search traversal. If - * `includeNull` is set to `true`, null nodes will be considered during the traversal process. If it - * is set to `false`, - * @param shouldVisitLeft - The `shouldVisitLeft` parameter is a function that takes a node as input - * and returns a boolean value. It is used to determine whether the left child of a node should be - * visited during the depth-first search traversal. By default, it checks if the node is truthy (not - * null or undefined - * @param shouldVisitRight - The `shouldVisitRight` parameter is a function that takes a node as an - * argument and returns a boolean value. It is used to determine whether the right child of a node - * should be visited during the depth-first search traversal. The default implementation checks if - * the node is truthy before visiting the right child - * @param shouldVisitRoot - The `shouldVisitRoot` parameter is a function that takes a node as an - * argument and returns a boolean value. It is used to determine whether the root node should be - * visited during the depth-first search traversal based on certain conditions. The default - * implementation checks if the node is a real node or null based - * @param shouldProcessRoot - The `shouldProcessRoot` parameter is a function that takes a node as an - * argument and returns a boolean value indicating whether the node should be processed during the - * depth-first search traversal. The default implementation checks if the node is a real node or null - * based on the `includeNull` flag. If ` - * @returns The function `_dfs` returns an array of the return type of the callback function provided - * as input. + * specifies whether the traversal should be done recursively or iteratively. It can have two + * possible values: + * @param [includeNull=false] - The `includeNull` parameter in the `_dfs` method determines whether + * null nodes should be included in the traversal process. If `includeNull` is set to `true`, the + * method will consider null nodes as valid nodes to visit or process. If `includeNull` is set to + * `false`, + * @param shouldVisitLeft - The `shouldVisitLeft` parameter in the `_dfs` method is a function that + * determines whether the left child of a node should be visited during the Depth-First Search + * traversal. By default, it checks if the node is not null or undefined before visiting the left + * child. You can customize this behavior + * @param shouldVisitRight - The `shouldVisitRight` parameter in the `_dfs` method is a function that + * determines whether to visit the right child node of the current node during a depth-first search + * traversal. The default implementation of this function checks if the node is not null or undefined + * before deciding to visit it. + * @param shouldVisitRoot - The `shouldVisitRoot` parameter in the `_dfs` method is a function that + * determines whether a given node should be visited during the depth-first search traversal. The + * function takes a node as an argument and returns a boolean value indicating whether the node + * should be visited. + * @param shouldProcessRoot - The `shouldProcessRoot` parameter in the `_dfs` method is a function + * that determines whether the root node should be processed during the Depth-First Search traversal. + * It takes a node (BinaryTreeNode | null | undefined) as input and returns a boolean value. If + * the function + * @returns The `_dfs` method returns an array of the return type of the provided callback function + * `C`. */ - protected _dfs>>>( + protected _dfs | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> = this._root, + onlyOne: boolean = false, + startNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined = 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: BinaryTreeNode | null | undefined) => boolean = node => !!node, + shouldVisitRight: (node: BinaryTreeNode | null | undefined) => boolean = node => !!node, + shouldVisitRoot: (node: BinaryTreeNode | null | undefined) => boolean = node => { if (includeNull) return this.isRealNodeOrNull(node); return this.isRealNode(node); }, - shouldProcessRoot: (node: OptNodeOrNull>) => boolean = node => this.isRealNodeOrNull(node) + shouldProcessRoot: (node: BinaryTreeNode | null | undefined) => 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: BinaryTreeNode | null) => { if (!shouldVisitRoot(node)) return; const visitLeft = () => { - if (shouldVisitLeft(node)) dfs(node?.left); + if (shouldVisitLeft(node) && node?.left !== undefined) dfs(node?.left); }; const visitRight = () => { - if (shouldVisitRight(node)) dfs(node?.right); + if (shouldVisitRight(node) && node?.right !== undefined) dfs(node?.right); }; switch (pattern) { case 'IN': visitLeft(); - if (shouldProcessRoot(node)) ans.push(callback(node)); + if (shouldProcessRoot(node)) { + ans.push(callback(node)); + if (onlyOne) return; + } visitRight(); break; case 'PRE': - if (shouldProcessRoot(node)) ans.push(callback(node)); + if (shouldProcessRoot(node)) { + ans.push(callback(node)); + if (onlyOne) return; + } visitLeft(); visitRight(); break; case 'POST': visitLeft(); visitRight(); - if (shouldProcessRoot(node)) ans.push(callback(node)); + if (shouldProcessRoot(node)) { + ans.push(callback(node)); + if (onlyOne) return; + } break; } }; @@ -1974,7 +2090,10 @@ export class BinaryTree>[] = []; - let current: OptNodeOrNull> = node; + const stack: (BinaryTreeNode | null | undefined)[] = []; + let current: BinaryTreeNode | null | undefined = node; while (current || stack.length > 0) { while (this.isRealNode(current)) { @@ -2063,7 +2182,10 @@ export class BinaryTree>, options: BinaryTreePrintOptions): NodeDisplayLayout { + protected _displayAux( + node: BinaryTreeNode | null | undefined, + options: BinaryTreePrintOptions + ): NodeDisplayLayout { const { isShowNull, isShowUndefined, isShowRedBlackNIL } = options; const emptyDisplayLayout = [['─'], 1, 0, 0]; @@ -2131,26 +2253,26 @@ export class BinaryTree>) => (node ? node.key : undefined); + protected _DEFAULT_NODE_CALLBACK = (node: BinaryTreeNode | null | undefined) => (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>} srcNode - The `srcNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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>} destNode - The `destNode` parameter in the + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } 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 `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ` 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>, - destNode: BTNRep> + srcNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined, + destNode: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined ): BinaryTreeNode | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -2210,45 +2332,67 @@ export class BinaryTree>`, which means + * @param v - The parameter `v` in the `_setRoot` method is of type `BinaryTreeNode | null | undefined`, which means * it can either be an optional `BinaryTreeNode` type or `null`. */ - protected _setRoot(v: OptNodeOrNull>) { + protected _setRoot(v: BinaryTreeNode | null | undefined) { if (v) { v.parent = undefined; } this._root = v; } + protected _ensurePredicate( + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate> + ): NodePredicate>; + /** * Time Complexity: O(1) * Space Complexity: O(1) * * The function `_ensurePredicate` in TypeScript ensures that the input is converted into a valid * predicate function for a binary tree node. - * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate - The + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate - The * `_ensurePredicate` method in the provided code snippet is responsible for ensuring that the input * parameter `keyNodeEntryOrPredicate` 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. */ protected _ensurePredicate( - keyNodeEntryOrPredicate: BTNRep> | NodePredicate> - ): NodePredicate> { + keyNodeEntryOrPredicate: + | K + | BinaryTreeNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate | null> + ): NodePredicate | null> { if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined) - return (node: BinaryTreeNode) => (node ? false : false); + return (node: BinaryTreeNode | null | undefined) => (node ? false : false); if (this._isPredicate(keyNodeEntryOrPredicate)) return keyNodeEntryOrPredicate; if (this.isRealNode(keyNodeEntryOrPredicate)) - return (node: BinaryTreeNode) => node === keyNodeEntryOrPredicate; + return (node: BinaryTreeNode | null) => node === keyNodeEntryOrPredicate; if (this.isEntry(keyNodeEntryOrPredicate)) { const [key] = keyNodeEntryOrPredicate; - return (node: BinaryTreeNode) => node.key === key; + return (node: BinaryTreeNode | null) => { + if (!node) return false; + return node.key === key; + }; } - return (node: BinaryTreeNode) => node.key === keyNodeEntryOrPredicate; + return (node: BinaryTreeNode | null) => { + if (!node) return false; + return node.key === keyNodeEntryOrPredicate; + }; } /** @@ -2273,14 +2417,16 @@ export class BinaryTree>} keyNodeOrEntry - The `_extractKey` method you provided is a - * TypeScript method that takes in a parameter `keyNodeOrEntry` of type `BTNRep>`, + * @param {K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The `_extractKey` method you provided is a + * TypeScript method that takes in a parameter `keyNodeOrEntry` of type `K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined `, * where `BTNRep` is a generic type with keys `K`, `V`, and `BinaryTreeNode`, and ` * @returns The `_extractKey` method returns the key value extracted from the `keyNodeOrEntry` * 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(keyNodeOrEntry: BTNRep>): K | null | undefined { + protected _extractKey( + keyNodeOrEntry: K | BinaryTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): K | null | undefined { if (keyNodeOrEntry === null) return null; if (keyNodeOrEntry === undefined) return; if (keyNodeOrEntry === this._NIL) return; diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 43fd0ec..b896be2 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -17,8 +17,7 @@ import type { IterationType, NodeCallback, NodePredicate, - OptNode, - OptNodeOrNull + OptNode } from '../../types'; import { BinaryTree, BinaryTreeNode } from './binary-tree'; import { IBinaryTree } from '../../interfaces'; @@ -27,6 +26,8 @@ import { isComparable } from '../../utils'; import { Range } from '../../common'; export class BSTNode extends BinaryTreeNode { + override parent?: BSTNode = undefined; + /** * This TypeScript constructor function initializes an instance with a key and an optional value. * @param {K} key - The `key` parameter is typically used to uniquely identify an object or element @@ -40,28 +41,26 @@ export class BSTNode extends BinaryTreeNode { super(key, value); } - override parent?: BSTNode = undefined; + override _left?: BSTNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): BSTNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: BSTNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: BSTNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): BSTNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: BSTNode | null | undefined) { if (v) { v.parent = this; } @@ -142,12 +141,17 @@ export class BST * This TypeScript constructor initializes a binary search tree with optional options and adds * elements if provided. * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an - * iterable that can contain elements of type `BTNRep>` or `R`. It is used to + * iterable that can contain elements of type `K | BSTNode | [K | null | undefined, V | undefined] | null | undefined ` or `R`. It is used to * initialize the binary search tree with keys, nodes, entries, or raw data. * @param [options] - The `options` parameter is an optional object that can contain the following * properties: */ - constructor(keysNodesEntriesOrRaws: Iterable> | R> = [], options?: BSTOptions) { + constructor( + keysNodesEntriesOrRaws: Iterable< + K | BSTNode | [K | null | undefined, V | undefined] | null | undefined | R + > = [], + options?: BSTOptions + ) { super([], options); if (options) { @@ -243,7 +247,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>} keyNodeOrEntry - The parameter + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter * `keyNodeOrEntry` 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 @@ -253,7 +257,7 @@ export class BST * not be ensured. */ override ensureNode( - keyNodeOrEntry: BTNRep>, + keyNodeOrEntry: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined, iterationType: IterationType = this.iterationType ): OptNode> { return super.ensureNode(keyNodeOrEntry, iterationType) ?? undefined; @@ -264,12 +268,14 @@ export class BST * Space Complexity: O(1) * * The function checks if the input is an instance of the BSTNode class. - * @param {BTNRep>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `K | BSTNode | [K | null | undefined, V | undefined] | null | undefined `. * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `BSTNode` class. */ - override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BSTNode { + override isNode( + keyNodeOrEntry: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is BSTNode { return keyNodeOrEntry instanceof BSTNode; } @@ -293,13 +299,16 @@ export class BST * Space Complexity: O(log n) * * The `add` function in TypeScript adds a new node to a binary search tree based on the key value. - * @param {BTNRep>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can accept a value of type `R` or `BTNRep>`. + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can accept a value of type `R` or `K | BSTNode | [K | null | undefined, V | undefined] | null | undefined `. * @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(keyNodeOrEntry: BTNRep>, value?: V): boolean { + override add( + keyNodeOrEntry: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined, + value?: V + ): boolean { const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (newNode === undefined) return false; @@ -383,7 +392,7 @@ export class BST } const realBTNExemplars: { - key: R | BTNRep>; + key: R | K | BSTNode | [K | null | undefined, V | undefined] | null | undefined; value: V | undefined; orgIndex: number; }[] = []; @@ -394,7 +403,11 @@ export class BST i++; } - let sorted: { key: R | BTNRep>; value: V | undefined; orgIndex: number }[] = []; + let sorted: { + key: R | K | BSTNode | [K | null | undefined, V | undefined] | null | undefined; + value: V | undefined; + orgIndex: number; + }[] = []; sorted = realBTNExemplars.sort(({ key: a }, { key: b }) => { let keyA: K | undefined | null, keyB: K | undefined | null; @@ -418,7 +431,13 @@ export class BST return 0; }); - const _dfs = (arr: { key: R | BTNRep>; value: V | undefined; orgIndex: number }[]) => { + const _dfs = ( + arr: { + key: R | K | BSTNode | [K | null | undefined, V | undefined] | null | undefined; + value: V | undefined; + orgIndex: number; + }[] + ) => { if (arr.length === 0) return; const mid = Math.floor((arr.length - 1) / 2); @@ -473,7 +492,7 @@ export class BST * * The function `search` in TypeScript overrides the search behavior in a binary tree structure based * on specified criteria. - * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate - The + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate - The * `keyNodeEntryOrPredicate` 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 @@ -481,9 +500,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 `BSTNode` as its + * extends `NodeCallback | null>`. The callback function should accept a node of type `BSTNode` as its * argument and - * @param {BTNRep>} startNode - The `startNode` parameter in the `override search` + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } 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 ` @@ -496,10 +515,17 @@ export class BST * collected in an array and returned as the output of the method. */ override search>>( - keyNodeEntryOrPredicate: BTNRep> | NodePredicate> | Range, + keyNodeEntryOrPredicate: + | K + | BSTNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate> + | Range, onlyOne = false, callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { if (keyNodeEntryOrPredicate === undefined) return []; @@ -511,21 +537,32 @@ export class BST const isRange = this.isRange(keyNodeEntryOrPredicate); // Set predicate based on parameter type if (isRange) { - predicate = node => keyNodeEntryOrPredicate.isInRange(node.key, this._comparator); + predicate = node => { + if (!node) return false; + return keyNodeEntryOrPredicate.isInRange(node.key, this._comparator); + }; } else { predicate = this._ensurePredicate(keyNodeEntryOrPredicate); } - const isToLeftByRange = (cur: BSTNode) => { + const shouldVisitLeft = (cur: BSTNode | null | undefined) => { + if (!cur) return false; + if (!this.isRealNode(cur.left)) return false; if (isRange) { const range = keyNodeEntryOrPredicate; const leftS = this.isReverse ? range.high : range.low; const leftI = this.isReverse ? range.includeHigh : range.includeLow; return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0); } - return false; + if (!isRange && !this._isPredicate(keyNodeEntryOrPredicate)) { + const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate); + return benchmarkKey !== null && benchmarkKey !== undefined && this._compare(cur.key, benchmarkKey) > 0; + } + return true; }; - const isToRightByRange = (cur: BSTNode) => { + const shouldVisitRight = (cur: BSTNode | null | undefined) => { + if (!cur) return false; + if (!this.isRealNode(cur.right)) return false; if (isRange) { const range = keyNodeEntryOrPredicate; const rightS = this.isReverse ? range.low : range.high; @@ -533,79 +570,27 @@ export class BST return (rightI && this._compare(cur.key, rightS) <= 0) || (!rightI && this._compare(cur.key, rightS) < 0); } - return false; - }; - const ans: ReturnType[] = []; - if (iterationType === 'RECURSIVE') { - const dfs = (cur: BSTNode) => { - if (predicate(cur)) { - ans.push(callback(cur)); - if (onlyOne) return; - } - - if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return; - - if (isRange) { - if (this.isRealNode(cur.left) && isToLeftByRange(cur)) dfs(cur.left); - if (this.isRealNode(cur.right) && isToRightByRange(cur)) dfs(cur.right); - } else if (!this._isPredicate(keyNodeEntryOrPredicate)) { - const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate); - if ( - this.isRealNode(cur.left) && - benchmarkKey !== null && - benchmarkKey !== undefined && - this._compare(cur.key, benchmarkKey) > 0 - ) - dfs(cur.left); - if ( - this.isRealNode(cur.right) && - benchmarkKey !== null && - benchmarkKey !== undefined && - this._compare(cur.key, benchmarkKey) < 0 - ) - dfs(cur.right); - } else { - if (this.isRealNode(cur.left)) dfs(cur.left); - if (this.isRealNode(cur.right)) dfs(cur.right); - } - }; - - dfs(startNode); - } else { - const stack = [startNode]; - while (stack.length > 0) { - const cur = stack.pop()!; - if (predicate(cur)) { - ans.push(callback(cur)); - if (onlyOne) return ans; - } - if (isRange) { - if (this.isRealNode(cur.left) && isToLeftByRange(cur)) stack.push(cur.left); - if (this.isRealNode(cur.right) && isToRightByRange(cur)) stack.push(cur.right); - } else if (!this._isPredicate(keyNodeEntryOrPredicate)) { - const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate); - if ( - this.isRealNode(cur.right) && - benchmarkKey !== null && - benchmarkKey !== undefined && - this._compare(cur.key, benchmarkKey) < 0 - ) - stack.push(cur.right); - if ( - this.isRealNode(cur.left) && - benchmarkKey !== null && - benchmarkKey !== undefined && - this._compare(cur.key, benchmarkKey) > 0 - ) - stack.push(cur.left); - } else { - if (this.isRealNode(cur.right)) stack.push(cur.right); - if (this.isRealNode(cur.left)) stack.push(cur.left); - } + if (!isRange && !this._isPredicate(keyNodeEntryOrPredicate)) { + const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate); + return benchmarkKey !== null && benchmarkKey !== undefined && this._compare(cur.key, benchmarkKey) < 0; } - } - - return ans; + return true; + }; + return super._dfs( + callback, + 'IN', + onlyOne, + startNode, + iterationType, + false, + shouldVisitLeft, + shouldVisitRight, + () => true, + cur => { + if (cur) return predicate(cur); + return false; + } + ); } /** @@ -617,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 `BSTNode` is the type of nodes in the + * search operation. It is of type `NodeCallback | null>`, where `BSTNode` is the type of nodes in the * data structure. - * @param {BTNRep>} startNode - The `startNode` parameter in the `rangeSearch` + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -632,7 +617,7 @@ export class BST rangeSearch>>( range: Range | [K, K], callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ) { const searchRange: Range = range instanceof Range ? range : new Range(range[0], range[1]); @@ -644,8 +629,8 @@ export class BST * Space Complexity: O(log n) * * This function retrieves a node based on a given keyNodeEntryOrPredicate within a binary search tree structure. - * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate - The `keyNodeEntryOrPredicate` - * parameter can be of type `BTNRep>`, `R`, or `NodePredicate>`. + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined | NodePredicate>} keyNodeEntryOrPredicate - The `keyNodeEntryOrPredicate` + * parameter can be of type `K | BSTNode | [K | null | undefined, V | undefined] | null | undefined `, `R`, or `NodePredicate>`. * @param {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 @@ -660,7 +645,13 @@ export class BST * returns the first node found or `undefined` if no node is found. */ override getNode( - keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, + keyNodeEntryOrPredicate: + | K + | BSTNode + | [K | null | undefined, V | undefined] + | null + | undefined + | NodePredicate>, startNode: BSTNOptKeyOrNode> = this._root, iterationType: IterationType = this.iterationType ): OptNode> { @@ -671,29 +662,36 @@ export class BST * Time complexity: O(n) * Space complexity: O(n) * - * The function overrides the depth-first search method and returns an array of the return types of - * the callback function. + * The function `dfs` in TypeScript overrides the base class method with default parameters and + * returns the result of the super class `dfs` method. * @param {C} callback - The `callback` parameter is a function that will be called for each node - * during the depth-first search traversal. It is an optional parameter and defaults to - * `this._DEFAULT_NODE_CALLBACK`. The type `C` represents the type of the callback function. - * @param {DFSOrderPattern} [pattern=IN] - The "pattern" parameter in the code snippet refers to the - * order in which the Depth-First Search (DFS) algorithm visits the nodes in a tree or graph. It can - * take one of the following values: - * @param {BTNRep>} 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 - * type of iteration to be used during the Depth-First Search (DFS) traversal. It can have one of the - * following values: - * @returns The method is returning an array of the return type of the callback function. + * visited during the Depth-First Search traversal. It is a generic type `C` that extends the + * `NodeCallback` interface for `BSTNode`. The default value for `callback` is `this._ + * @param {DFSOrderPattern} [pattern=IN] - The `pattern` parameter in the `override dfs` method + * specifies the order in which the Depth-First Search (DFS) traversal should be performed on the + * Binary Search Tree (BST). The possible values for the `pattern` parameter are: + * @param {boolean} [onlyOne=false] - The `onlyOne` parameter in the `override dfs` method is a + * boolean flag that indicates whether you want to stop the depth-first search traversal after + * finding the first matching node or continue searching for all matching nodes. If `onlyOne` is set + * to `true`, the traversal will stop after finding + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined} startNode - + * The `startNode` parameter in the `override dfs` method can be one of the following types: + * @param {IterationType} iterationType - The `iterationType` parameter in the `override dfs` method + * specifies the type of iteration to be performed during the Depth-First Search (DFS) traversal of a + * Binary Search Tree (BST). It is used to determine the order in which nodes are visited during the + * traversal. The possible values for ` + * @returns The `override` function is returning the result of calling the `dfs` method from the + * superclass, with the provided arguments `callback`, `pattern`, `onlyOne`, `startNode`, and + * `iterationType`. The return type is an array of the return type of the callback function `C`. */ override dfs>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> = this._root, + onlyOne: boolean = false, + startNode: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { - return super.dfs(callback, pattern, startNode, iterationType); + return super.dfs(callback, pattern, onlyOne, startNode, iterationType); } /** @@ -705,7 +703,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>} startNode - The `startNode` parameter is the starting + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -715,7 +713,7 @@ export class BST */ override bfs>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { return super.bfs(callback, startNode, iterationType, false); @@ -728,9 +726,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 | null>`. It represents a callback function that will be called for each node in the * tree during the iteration process. - * @param {BTNRep>} startNode - The `startNode` parameter is the starting + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } 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 @@ -741,7 +739,7 @@ export class BST */ override listLevels>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> = this._root, + startNode: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[][] { return super.listLevels(callback, startNode, iterationType, false); @@ -759,7 +757,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>} targetNode - The `targetNode` parameter is the node in + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } 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, @@ -771,7 +769,7 @@ export class BST lesserOrGreaterTraverse>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, lesserOrGreater: CP = -1, - targetNode: BTNRep> = this._root, + targetNode: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { const targetNodeEnsured = this.ensureNode(targetNode); @@ -831,8 +829,8 @@ export class BST if (l > r) return; const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - if (this._isMapMode) this.add(midNode.key); - else this.add([midNode.key, midNode.value]); + if (this._isMapMode && midNode !== null) this.add(midNode.key); + else if (midNode !== null) this.add([midNode.key, midNode.value]); buildBalanceBST(l, m - 1); buildBalanceBST(m + 1, r); }; @@ -848,8 +846,8 @@ export class BST if (l <= r) { const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - if (this._isMapMode) this.add(midNode.key); - else this.add([midNode.key, midNode.value]); + if (this._isMapMode && midNode !== null) this.add(midNode.key); + else if (midNode !== null) this.add([midNode.key, midNode.value]); stack.push([m + 1, r]); stack.push([l, m - 1]); } @@ -877,7 +875,7 @@ export class BST let balanced = true; if (iterationType === 'RECURSIVE') { - const _height = (cur: OptNodeOrNull>): number => { + const _height = (cur: BSTNode | null | undefined): number => { if (!cur) return 0; const leftHeight = _height(cur.left), rightHeight = _height(cur.right); @@ -966,15 +964,15 @@ export class BST * Space Complexity: O(1) * * The function overrides a method and converts a key, value pair or entry or raw element to a node. - * @param {BTNRep>} keyNodeOrEntry - A variable that can be of - * type R or BTNRep>. It represents either a key, a node, an entry, or a raw + * @param {K | BSTNode | [K | null | undefined, V | undefined] | null | undefined } keyNodeOrEntry - A variable that can be of + * type R or K | BSTNode | [K | null | undefined, V | undefined] | null | undefined . 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 _keyValueNodeOrEntryToNodeAndValue( - keyNodeOrEntry: BTNRep>, + keyNodeOrEntry: K | BSTNode | [K | null | undefined, V | undefined] | null | undefined, value?: V ): [OptNode>, V | undefined] { const [node, entryValue] = super._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); diff --git a/src/data-structures/binary-tree/red-black-tree.ts b/src/data-structures/binary-tree/red-black-tree.ts index ccd773e..8dcdf00 100644 --- a/src/data-structures/binary-tree/red-black-tree.ts +++ b/src/data-structures/binary-tree/red-black-tree.ts @@ -1,17 +1,10 @@ -import type { - BinaryTreeDeleteResult, - BTNRep, - CRUD, - EntryCallback, - OptNode, - OptNodeOrNull, - RBTNColor, - RedBlackTreeOptions -} from '../../types'; +import type { BinaryTreeDeleteResult, CRUD, EntryCallback, OptNode, RBTNColor, RedBlackTreeOptions } from '../../types'; import { BST, BSTNode } from './bst'; import { IBinaryTree } from '../../interfaces'; export class RedBlackTreeNode extends BSTNode { + override parent?: RedBlackTreeNode = undefined; + /** * The constructor initializes a node with a key, value, and color for a Red-Black Tree. * @param {K} key - The `key` parameter is a key of type `K` that is used to identify the node in a @@ -27,28 +20,26 @@ export class RedBlackTreeNode extends BSTNode { this._color = color; } - override parent?: RedBlackTreeNode = undefined; + override _left?: RedBlackTreeNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): RedBlackTreeNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: RedBlackTreeNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: RedBlackTreeNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): RedBlackTreeNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: RedBlackTreeNode | null | undefined) { if (v) { v.parent = this; } @@ -117,7 +108,7 @@ export class RedBlackTree>` objects or `R` objects. It + * iterable that can contain either `K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined` objects or `R` objects. It * is used to initialize the Red-Black Tree with keys, nodes, entries, or * @param [options] - The `options` parameter in the constructor is of type `RedBlackTreeOptions`. It is an optional parameter that allows you to specify additional options for the @@ -125,7 +116,9 @@ export class RedBlackTree> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined | R + > = [], options?: RedBlackTreeOptions ) { super([], options); @@ -188,12 +181,14 @@ export class RedBlackTree>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined`. * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `RedBlackTreeNode` class. */ - override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is RedBlackTreeNode { + override isNode( + keyNodeOrEntry: K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is RedBlackTreeNode { return keyNodeOrEntry instanceof RedBlackTreeNode; } @@ -215,8 +210,8 @@ export class RedBlackTree>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can accept a value of type `R` or `BTNRep>`. + * @param {K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can accept a value of type `R` or `K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined`. * @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. @@ -224,7 +219,10 @@ export class RedBlackTree>, value?: V): boolean { + override add( + keyNodeOrEntry: K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined, + value?: V + ): boolean { const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (!this.isRealNode(newNode)) return false; @@ -254,7 +252,7 @@ export class RedBlackTree>} keyNodeOrEntry - The `keyNodeOrEntry` + * @param {K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The `keyNodeOrEntry` * 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. @@ -263,7 +261,7 @@ export class RedBlackTree> + keyNodeOrEntry: K | RedBlackTreeNode | [K | null | undefined, V | undefined] | null | undefined ): BinaryTreeDeleteResult>[] { if (keyNodeOrEntry === null) return []; diff --git a/src/data-structures/binary-tree/tree-counter.ts b/src/data-structures/binary-tree/tree-counter.ts index 83b5d9d..0f8bec6 100644 --- a/src/data-structures/binary-tree/tree-counter.ts +++ b/src/data-structures/binary-tree/tree-counter.ts @@ -8,11 +8,9 @@ import type { BinaryTreeDeleteResult, BSTNOptKeyOrNode, - BTNRep, EntryCallback, IterationType, OptNode, - OptNodeOrNull, RBTNColor, TreeCounterOptions } from '../../types'; @@ -20,6 +18,8 @@ import { IBinaryTree } from '../../interfaces'; import { RedBlackTree, RedBlackTreeNode } from './red-black-tree'; export class TreeCounterNode extends RedBlackTreeNode { + override parent?: TreeCounterNode = undefined; + /** * 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 @@ -37,28 +37,26 @@ export class TreeCounterNode extends RedBlackTreeNode { this.count = count; } - override parent?: TreeCounterNode = undefined; + override _left?: TreeCounterNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): TreeCounterNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: TreeCounterNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: TreeCounterNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): TreeCounterNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: TreeCounterNode | null | undefined) { if (v) { v.parent = this; } @@ -83,7 +81,9 @@ export class TreeCounter> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined | R + > = [], options?: TreeCounterOptions ) { super([], options); @@ -111,7 +111,7 @@ export class TreeCounter (sum += node.count)); + this.dfs(node => (sum += node ? node.count : 0)); return sum; } @@ -152,12 +152,14 @@ export class TreeCounter>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined`. * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `TreeCounterNode` class. */ - override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is TreeCounterNode { + override isNode( + keyNodeOrEntry: K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined + ): keyNodeOrEntry is TreeCounterNode { return keyNodeOrEntry instanceof TreeCounterNode; } @@ -167,7 +169,7 @@ export class TreeCounter>} keyNodeOrEntry - The + * @param {K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The * `keyNodeOrEntry` 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. @@ -177,7 +179,11 @@ export class TreeCounter>, value?: V, count = 1): boolean { + override add( + keyNodeOrEntry: K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined, + value?: V, + count = 1 + ): boolean { const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value, count); const orgCount = newNode?.count || 0; const isSuccessAdded = super.add(newNode, newValue); @@ -196,7 +202,7 @@ export class TreeCounter>} keyNodeOrEntry - The `predicate` + * @param {K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - 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 @@ -206,7 +212,7 @@ export class TreeCounter>` objects. */ override delete( - keyNodeOrEntry: BTNRep>, + keyNodeOrEntry: K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined, ignoreCount = false ): BinaryTreeDeleteResult>[] { if (keyNodeOrEntry === null) return []; @@ -340,8 +346,8 @@ export class TreeCounter r) return; const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - if (this._isMapMode) this.add(midNode.key, undefined, midNode.count); - else this.add(midNode.key, midNode.value, midNode.count); + if (this._isMapMode && midNode !== null) this.add(midNode.key, undefined, midNode.count); + else if (midNode !== null) this.add(midNode.key, midNode.value, midNode.count); buildBalanceBST(l, m - 1); buildBalanceBST(m + 1, r); }; @@ -357,8 +363,8 @@ export class TreeCounter cloned.add(node.key, undefined, node.count)); + this.bfs(node => cloned.add(node === null ? null : node.key, undefined, node === null ? 0 : node.count)); if (this._isMapMode) cloned._store = this._store; return cloned; } @@ -413,8 +419,8 @@ export class TreeCounter>} keyNodeOrEntry - The parameter - * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined`. * @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. @@ -423,7 +429,7 @@ export class TreeCounter object or undefined. */ protected override _keyValueNodeOrEntryToNodeAndValue( - keyNodeOrEntry: BTNRep>, + keyNodeOrEntry: K | TreeCounterNode | [K | null | undefined, V | undefined] | null | undefined, value?: V, count = 1 ): [TreeCounterNode | undefined, V | undefined] { diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index 215008b..57df1c6 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -5,11 +5,13 @@ * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ -import type { BTNOptKeyOrNull, BTNRep, OptNodeOrNull, TreeMultiMapOptions } from '../../types'; +import type { BTNOptKeyOrNull, TreeMultiMapOptions } from '../../types'; import { RedBlackTree, RedBlackTreeNode } from './red-black-tree'; import { IBinaryTree } from '../../interfaces'; export class TreeMultiMapNode extends RedBlackTreeNode { + override parent?: TreeMultiMapNode = undefined; + /** * This TypeScript constructor initializes an object with a key of type K and an array of values of * type V. @@ -23,28 +25,26 @@ export class TreeMultiMapNode extends RedBlackTreeNode super(key, value); } - override parent?: TreeMultiMapNode = undefined; + override _left?: TreeMultiMapNode | null | undefined = undefined; - override _left?: OptNodeOrNull> = undefined; - - override get left(): OptNodeOrNull> { + override get left(): TreeMultiMapNode | null | undefined { return this._left; } - override set left(v: OptNodeOrNull>) { + override set left(v: TreeMultiMapNode | null | undefined) { if (v) { v.parent = this; } this._left = v; } - override _right?: OptNodeOrNull> = undefined; + override _right?: TreeMultiMapNode | null | undefined = undefined; - override get right(): OptNodeOrNull> { + override get right(): TreeMultiMapNode | null | undefined { return this._right; } - override set right(v: OptNodeOrNull>) { + override set right(v: TreeMultiMapNode | null | undefined) { if (v) { v.parent = this; } @@ -77,7 +77,9 @@ export class TreeMultiMap> | R> = [], + keysNodesEntriesOrRaws: Iterable< + K | TreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined | R + > = [], options?: TreeMultiMapOptions ) { super([], { ...options, isMapMode: true }); @@ -124,7 +126,7 @@ export class TreeMultiMap(key, []); } - override add(node: BTNRep>): boolean; + override add(node: K | TreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined): boolean; override add(key: K, value: V): boolean; @@ -134,7 +136,7 @@ export class TreeMultiMap> | K} keyNodeOrEntry - The `keyNodeOrEntry` + * @param {K | TreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `override add` method can be either a `BTNRep` object containing a key, an array * of values, and a `TreeMultiMapNode`, or just a key. * @param {V} [value] - The `value` parameter in the `override add` method represents the value that @@ -143,7 +145,10 @@ export class TreeMultiMap> | K, value?: V): boolean { + override add( + keyNodeOrEntry: K | TreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined, + value?: V + ): boolean { if (this.isRealNode(keyNodeOrEntry)) return super.add(keyNodeOrEntry); const _commonAdd = (key?: BTNOptKeyOrNull, values?: V[]) => { @@ -186,7 +191,7 @@ export class TreeMultiMap> | K} keyNodeOrEntry - The `keyNodeOrEntry` + * @param {K | TreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `deleteValue` function can be either a `BTNRep` object containing a key and an * array of values, or just a key itself. * @param {V} value - The `value` parameter in the `deleteValue` function represents the specific @@ -196,7 +201,10 @@ export class TreeMultiMap> | K, value: V): boolean { + deleteValue( + keyNodeOrEntry: K | TreeMultiMapNode | [K | null | undefined, V[] | undefined] | null | undefined, + value: V + ): boolean { const values = this.get(keyNodeOrEntry); if (Array.isArray(values)) { const index = values.indexOf(value); diff --git a/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts b/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts index 2cab97f..06e074a 100644 --- a/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts @@ -91,7 +91,7 @@ describe('AVLTreeCounter operations test1', () => { expect(minNodeBySpecificNode?.key).toBe(15); let subTreeSum = 0; - if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', false, 15); expect(subTreeSum).toBe(31); let lesserSum = 0; avlCounter.lesserOrGreaterTraverse((node: AVLTreeCounterNode) => (lesserSum += node.key), -1, 10); @@ -99,7 +99,7 @@ describe('AVLTreeCounter operations test1', () => { expect(node15 instanceof AVLTreeCounterNode); if (node15 instanceof AVLTreeCounterNode) { - const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', 15); + const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', false, 15); expect(subTreeAdd); } const node11 = avlCounter.getNode(11); @@ -347,7 +347,7 @@ describe('AVLTreeCounter operations test recursively1', () => { expect(minNodeBySpecificNode?.key).toBe(15); let subTreeSum = 0; - if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', false, 15); expect(subTreeSum).toBe(31); let lesserSum = 0; avlCounter.lesserOrGreaterTraverse((node: AVLTreeCounterNode) => (lesserSum += node.key), -1, 10); @@ -355,7 +355,7 @@ describe('AVLTreeCounter operations test recursively1', () => { expect(node15 instanceof AVLTreeCounterNode); if (node15 instanceof AVLTreeCounterNode) { - const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', 15); + const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', false, 15); expect(subTreeAdd); } const node11 = avlCounter.getNode(11); @@ -709,7 +709,7 @@ describe('AVLTreeCounter toEntryFn', () => { expect(avlCounter.morris(node => node.key, 'IN')).toEqual(expected); expect(avlCounter.dfs(node => node.key, 'IN')).toEqual(expected); - expect(avlCounter.dfs(node => node.key, 'IN', avlCounter.root, 'RECURSIVE')).toEqual(expected); + expect(avlCounter.dfs(node => node.key, 'IN', false, avlCounter.root, 'RECURSIVE')).toEqual(expected); }); it('should toEntryFn 2', () => { @@ -724,7 +724,7 @@ describe('AVLTreeCounter toEntryFn', () => { expect(avlCounter.morris(node => node.key, 'IN')).toEqual(expected); expect(avlCounter.dfs(node => node.key, 'IN')).toEqual(expected); - expect(avlCounter.dfs(node => node.key, 'IN', avlCounter.root, 'RECURSIVE')).toEqual(expected); + expect(avlCounter.dfs(node => node.key, 'IN', false, avlCounter.root, 'RECURSIVE')).toEqual(expected); }); it('should toEntryFn throw error', () => { @@ -760,7 +760,7 @@ describe('AVLTreeCounter toEntryFn', () => { expect(avlCounter.morris(node => node.key, 'IN')).toEqual(expected); expect(avlCounter.dfs(node => node.key, 'IN')).toEqual(expected); - expect(avlCounter.dfs(node => node.key, 'IN', avlCounter.root, 'RECURSIVE')).toEqual(expected); + expect(avlCounter.dfs(node => node.key, 'IN', false, avlCounter.root, 'RECURSIVE')).toEqual(expected); }); }); diff --git a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts index a5e9821..0e14d84 100644 --- a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts @@ -50,7 +50,7 @@ describe('AVLTreeMultiMap Test', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -176,7 +176,7 @@ describe('AVLTreeMultiMap Test recursively', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -509,7 +509,7 @@ describe('AVLTreeMultiMap not map mode', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -527,7 +527,6 @@ describe('AVLTreeMultiMap not map mode test recursively', () => { const avlTmm = new AVLTreeMultiMap([], { iterationType: 'RECURSIVE' }); for (const i of arr) avlTmm.add([i, [i]]); - const node6 = avlTmm.getNode(6); expect(node6 && avlTmm.getHeight(node6)).toBe(3); @@ -544,7 +543,7 @@ describe('AVLTreeMultiMap not map mode test recursively', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index 57dc9ce..a04630f 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -1,4 +1,4 @@ -import { AVLTree, AVLTreeNode, BinaryTreeNode, BSTNode } from '../../../../src'; +import { AVLTree, AVLTreeNode, BinaryTreeNode, BSTNode, Range } from '../../../../src'; describe('AVL Tree Test', () => { it('should perform various operations on a AVL Tree', () => { @@ -24,7 +24,7 @@ describe('AVL Tree Test', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -150,7 +150,7 @@ describe('AVL Tree Test recursively', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -480,7 +480,7 @@ describe('AVL Tree not map mode', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -515,7 +515,7 @@ describe('AVL Tree not map mode test recursively', () => { expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -542,3 +542,70 @@ describe('AVLTree iterative methods not map mode', () => { expect(cloned.get(cloned.root?.right?.key)).toBe('c'); }); }); + +describe('classic use', () => { + // Test case for finding elements in a given range + it('@example Find elements in a range', () => { + type Datum = { timestamp: Date; temperature: number }; + // Fixed dataset of CPU temperature readings + const cpuData: Datum[] = [ + { timestamp: new Date('2024-12-02T00:00:00'), temperature: 55.1 }, + { timestamp: new Date('2024-12-02T00:01:00'), temperature: 56.3 }, + { timestamp: new Date('2024-12-02T00:02:00'), temperature: 54.8 }, + { timestamp: new Date('2024-12-02T00:03:00'), temperature: 57.2 }, + { timestamp: new Date('2024-12-02T00:04:00'), temperature: 58.0 }, + { timestamp: new Date('2024-12-02T00:05:00'), temperature: 59.4 }, + { timestamp: new Date('2024-12-02T00:06:00'), temperature: 60.1 }, + { timestamp: new Date('2024-12-02T00:07:00'), temperature: 61.3 }, + { timestamp: new Date('2024-12-02T00:08:00'), temperature: 62.0 }, + { timestamp: new Date('2024-12-02T00:09:00'), temperature: 63.5 }, + { timestamp: new Date('2024-12-02T00:10:00'), temperature: 64.0 }, + { timestamp: new Date('2024-12-02T00:11:00'), temperature: 62.8 }, + { timestamp: new Date('2024-12-02T00:12:00'), temperature: 61.5 }, + { timestamp: new Date('2024-12-02T00:13:00'), temperature: 60.2 }, + { timestamp: new Date('2024-12-02T00:14:00'), temperature: 59.8 }, + { timestamp: new Date('2024-12-02T00:15:00'), temperature: 58.6 }, + { timestamp: new Date('2024-12-02T00:16:00'), temperature: 57.4 }, + { timestamp: new Date('2024-12-02T00:17:00'), temperature: 56.2 }, + { timestamp: new Date('2024-12-02T00:18:00'), temperature: 55.7 }, + { timestamp: new Date('2024-12-02T00:19:00'), temperature: 54.5 }, + { timestamp: new Date('2024-12-02T00:20:00'), temperature: 53.2 }, + { timestamp: new Date('2024-12-02T00:21:00'), temperature: 52.8 }, + { timestamp: new Date('2024-12-02T00:22:00'), temperature: 51.9 }, + { timestamp: new Date('2024-12-02T00:23:00'), temperature: 50.5 }, + { timestamp: new Date('2024-12-02T00:24:00'), temperature: 49.8 }, + { timestamp: new Date('2024-12-02T00:25:00'), temperature: 48.7 }, + { timestamp: new Date('2024-12-02T00:26:00'), temperature: 47.5 }, + { timestamp: new Date('2024-12-02T00:27:00'), temperature: 46.3 }, + { timestamp: new Date('2024-12-02T00:28:00'), temperature: 45.9 }, + { timestamp: new Date('2024-12-02T00:29:00'), temperature: 45.0 } + ]; + + // Create an AVL tree to store CPU temperature data + const cpuTemperatureTree = new AVLTree(cpuData, { + toEntryFn: ({ timestamp, temperature }) => [timestamp, temperature] + }); + + // Query a specific time range (e.g., from 00:05 to 00:15) + const rangeStart = new Date('2024-12-02T00:05:00'); + const rangeEnd = new Date('2024-12-02T00:15:00'); + const rangeResults = cpuTemperatureTree.rangeSearch([rangeStart, rangeEnd], node => ({ + minute: node ? node.key.getMinutes() : 0, + temperature: cpuTemperatureTree.get(node ? node.key : undefined) + })); + + expect(rangeResults).toEqual( [ + { minute: 5, temperature: 59.4 }, + { minute: 6, temperature: 60.1 }, + { minute: 7, temperature: 61.3 }, + { minute: 8, temperature: 62 }, + { minute: 9, temperature: 63.5 }, + { minute: 10, temperature: 64 }, + { minute: 11, temperature: 62.8 }, + { minute: 12, temperature: 61.5 }, + { minute: 13, temperature: 60.2 }, + { minute: 14, temperature: 59.8 }, + { minute: 15, temperature: 58.6 } + ]); + }); +}); diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index c1c8d8e..eb84e7a 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -217,20 +217,20 @@ describe('BinaryTree', () => { expect(cloned.root?.right?.key).toBe(6); expect(cloned.root?.right?.left?.key).toBe(3); expect(cloned.root?.right?.right).toBe(null); - expect(cloned.dfs(node => node.key, 'PRE', cloned.getNode(6), 'ITERATIVE')).toEqual([6, 3, 7]); - expect(cloned.dfs(node => (node ? node.key : null), 'PRE', cloned.getNode(6), 'ITERATIVE', true)).toEqual([ + expect(cloned.dfs(node => node.key, 'PRE', false, cloned.getNode(6), 'ITERATIVE')).toEqual([6, 3, 7]); + expect(cloned.dfs(node => (node ? node.key : null), 'PRE', false, cloned.getNode(6), 'ITERATIVE', true)).toEqual([ 6, 3, 7, null ]); - expect(cloned.dfs(node => (node ? node.key : node), 'PRE', cloned.getNode(6), 'ITERATIVE', true)).toEqual([ + expect(cloned.dfs(node => (node ? node.key : node), 'PRE', false, cloned.getNode(6), 'ITERATIVE', true)).toEqual([ 6, 3, 7, null ]); - expect(cloned.dfs(node => (node ? node.key : null), 'PRE', cloned.getNode(6), 'RECURSIVE', true)).toEqual([ + expect(cloned.dfs(node => (node ? node.key : null), 'PRE', false, cloned.getNode(6), 'RECURSIVE', true)).toEqual([ 6, 3, 7, @@ -532,11 +532,11 @@ describe('BinaryTree', () => { expect(binTree.dfs()).toEqual([]); expect([...binTree.values()]).toEqual([]); binTree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); - expect(binTree.dfs(node => node.key, 'PRE', undefined, 'ITERATIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', false)).toEqual([ - 4, 2, 1, 5, 6, 3, 7 - ]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', true)).toEqual([ + expect(binTree.dfs(node => node.key, 'PRE', false, undefined, 'ITERATIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, undefined, 'ITERATIVE', false)).toEqual( + [4, 2, 1, 5, 6, 3, 7] + ); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, undefined, 'ITERATIVE', true)).toEqual([ 4, 2, null, @@ -549,11 +549,11 @@ describe('BinaryTree', () => { null ]); - expect(binTree.dfs(node => node.key, 'PRE', undefined, 'RECURSIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', false)).toEqual([ - 4, 2, 1, 5, 6, 3, 7 - ]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', true)).toEqual([ + expect(binTree.dfs(node => node.key, 'PRE', false, undefined, 'RECURSIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, undefined, 'RECURSIVE', false)).toEqual( + [4, 2, 1, 5, 6, 3, 7] + ); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, undefined, 'RECURSIVE', true)).toEqual([ 4, 2, null, @@ -566,11 +566,11 @@ describe('BinaryTree', () => { null ]); - expect(binTree.dfs(node => node.key, 'IN', undefined, 'ITERATIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', false)).toEqual([ + expect(binTree.dfs(node => node.key, 'IN', false, undefined, 'ITERATIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, undefined, 'ITERATIVE', false)).toEqual([ 2, 5, 1, 4, 7, 3, 6 ]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, undefined, 'ITERATIVE', true)).toEqual([ null, 2, 5, @@ -583,11 +583,11 @@ describe('BinaryTree', () => { null ]); - expect(binTree.dfs(node => node.key, 'IN', undefined, 'RECURSIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', false)).toEqual([ + expect(binTree.dfs(node => node.key, 'IN', false, undefined, 'RECURSIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, undefined, 'RECURSIVE', false)).toEqual([ 2, 5, 1, 4, 7, 3, 6 ]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, undefined, 'RECURSIVE', true)).toEqual([ null, 2, 5, @@ -600,89 +600,71 @@ describe('BinaryTree', () => { null ]); - expect(binTree.dfs(node => node.key, 'POST', undefined, 'ITERATIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', false)).toEqual([ - 5, 1, 2, 7, 3, 6, 4 - ]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', true)).toEqual([ - null, - 5, - null, - 1, - 2, - 7, - 3, - null, - 6, - 4 - ]); + expect(binTree.dfs(node => node.key, 'POST', false, undefined, 'ITERATIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, undefined, 'ITERATIVE', false) + ).toEqual([5, 1, 2, 7, 3, 6, 4]); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, undefined, 'ITERATIVE', true)).toEqual( + [null, 5, null, 1, 2, 7, 3, null, 6, 4] + ); - expect(binTree.dfs(node => node.key, 'POST', undefined, 'RECURSIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', false)).toEqual([ - 5, 1, 2, 7, 3, 6, 4 - ]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', true)).toEqual([ - null, - 5, - null, - 1, - 2, - 7, - 3, - null, - 6, - 4 - ]); + expect(binTree.dfs(node => node.key, 'POST', false, undefined, 'RECURSIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, undefined, 'RECURSIVE', false) + ).toEqual([5, 1, 2, 7, 3, 6, 4]); + expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, undefined, 'RECURSIVE', true)).toEqual( + [null, 5, null, 1, 2, 7, 3, null, 6, 4] + ); }); it('should sub binTree traverse', () => { binTree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); - expect(binTree.dfs(node => node.key, 'PRE', binTree.getNode(6), 'ITERATIVE')).toEqual([6, 3, 7]); + expect(binTree.dfs(node => node.key, 'PRE', false, binTree.getNode(6), 'ITERATIVE')).toEqual([6, 3, 7]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'PRE', binTree.getNode(6), 'ITERATIVE', false) + binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, binTree.getNode(6), 'ITERATIVE', false) ).toEqual([6, 3, 7]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'PRE', binTree.getNode(6), 'ITERATIVE', true) + binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, binTree.getNode(6), 'ITERATIVE', true) ).toEqual([6, 3, 7, null]); - expect(binTree.dfs(node => node.key, 'PRE', binTree.getNode(6), 'RECURSIVE')).toEqual([6, 3, 7]); + expect(binTree.dfs(node => node.key, 'PRE', false, binTree.getNode(6), 'RECURSIVE')).toEqual([6, 3, 7]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'PRE', binTree.getNode(6), 'RECURSIVE', false) + binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, binTree.getNode(6), 'RECURSIVE', false) ).toEqual([6, 3, 7]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'PRE', binTree.getNode(6), 'RECURSIVE', true) + binTree.dfs(node => (node !== null ? node.key : null), 'PRE', false, binTree.getNode(6), 'RECURSIVE', true) ).toEqual([6, 3, 7, null]); - expect(binTree.dfs(node => node.key, 'IN', binTree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); + expect(binTree.dfs(node => node.key, 'IN', false, binTree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'IN', binTree.getNode(6), 'ITERATIVE', false) + binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, binTree.getNode(6), 'ITERATIVE', false) ).toEqual([7, 3, 6]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', binTree.getNode(6), 'ITERATIVE', true)).toEqual( - [7, 3, 6, null] - ); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, binTree.getNode(6), 'ITERATIVE', true) + ).toEqual([7, 3, 6, null]); - expect(binTree.dfs(node => node.key, 'IN', binTree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]); + expect(binTree.dfs(node => node.key, 'IN', false, binTree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'IN', binTree.getNode(6), 'RECURSIVE', false) + binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, binTree.getNode(6), 'RECURSIVE', false) ).toEqual([7, 3, 6]); - expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', binTree.getNode(6), 'RECURSIVE', true)).toEqual( - [7, 3, 6, null] - ); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'IN', false, binTree.getNode(6), 'RECURSIVE', true) + ).toEqual([7, 3, 6, null]); - expect(binTree.dfs(node => node.key, 'POST', binTree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); + expect(binTree.dfs(node => node.key, 'POST', false, binTree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'ITERATIVE', false) + binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, binTree.getNode(6), 'ITERATIVE', false) ).toEqual([7, 3, 6]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'ITERATIVE', true) + binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, binTree.getNode(6), 'ITERATIVE', true) ).toEqual([7, 3, null, 6]); - expect(binTree.dfs(node => node.key, 'POST', binTree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]); + expect(binTree.dfs(node => node.key, 'POST', false, binTree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'RECURSIVE', false) + binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, binTree.getNode(6), 'RECURSIVE', false) ).toEqual([7, 3, 6]); expect( - binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'RECURSIVE', true) + binTree.dfs(node => (node !== null ? node.key : null), 'POST', false, binTree.getNode(6), 'RECURSIVE', true) ).toEqual([7, 3, null, 6]); }); @@ -814,7 +796,7 @@ describe('BinaryTree Morris Traversal', () => { expect(result).toEqual(expected); expect(binTree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(binTree.dfs(node => node.key, 'IN', binTree.root, 'RECURSIVE')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'IN', false, binTree.root, 'RECURSIVE')).toEqual(expected); }); it('should perform pre-order Morris traversal correctly as dfs traversal', () => { @@ -875,7 +857,7 @@ describe('BinaryTree toEntryFn', () => { expect(binTree.morris(node => node.key, 'IN')).toEqual(expected); expect(binTree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(binTree.dfs(node => node.key, 'IN', binTree.root, 'RECURSIVE')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'IN', false, binTree.root, 'RECURSIVE')).toEqual(expected); }); it('should toEntryFn with initial', () => { @@ -890,7 +872,7 @@ describe('BinaryTree toEntryFn', () => { expect(binTree.morris(node => node.key, 'IN')).toEqual(expected); expect(binTree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(binTree.dfs(node => node.key, 'IN', binTree.root, 'RECURSIVE')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'IN', false, binTree.root, 'RECURSIVE')).toEqual(expected); }); it('should no toEntryFn', () => { @@ -905,7 +887,7 @@ describe('BinaryTree toEntryFn', () => { expect(binTree.morris(node => node.key, 'IN')).toEqual(data.sort((a, b) => a.obj.id - b.obj.id)); expect(binTree.dfs(node => node.key, 'IN')).toEqual(data); - expect(binTree.dfs(node => node.key, 'IN', binTree.root, 'RECURSIVE')).toEqual(data); + expect(binTree.dfs(node => node.key, 'IN', false, binTree.root, 'RECURSIVE')).toEqual(data); }); }); @@ -953,19 +935,21 @@ describe('BinaryTree traversals', () => { ); expect(binTree.dfs(node => node.key, 'PRE')).toEqual([35, 20, 15, 16, 29, 28, 30, 40, 50, 45, 55]); - expect(binTree.dfs(node => node.key, 'PRE', binTree.root, 'RECURSIVE')).toEqual([ + expect(binTree.dfs(node => node.key, 'PRE', false, binTree.root, 'RECURSIVE')).toEqual([ 35, 20, 15, 16, 29, 28, 30, 40, 50, 45, 55 ]); expect( - binTree.dfs(node => node, 'PRE', binTree.root, 'ITERATIVE', true).map(node => (node === null ? null : node.key)) + binTree + .dfs(node => node, 'PRE', false, binTree.root, 'ITERATIVE', true) + .map(node => (node === null ? null : node.key)) ).toEqual([35, 20, 15, null, 16, 29, 28, 30, 40, null, 50, 45, 55]); expect( - binTree.dfs(node => node, 'PRE', binTree.root, 'RECURSIVE', true).map(node => (node ? node.key : null)) + binTree.dfs(node => node, 'PRE', false, binTree.root, 'RECURSIVE', true).map(node => (node ? node.key : null)) ).toEqual([35, 20, 15, null, 16, 29, 28, 30, 40, null, 50, 45, 55]); expect(binTree.dfs(node => node.key, 'IN')).toEqual([15, 16, 20, 28, 29, 30, 35, 40, 45, 50, 55]); expect(binTree.dfs(node => node.key, 'POST')).toEqual([16, 15, 28, 30, 29, 20, 45, 55, 50, 40, 35]); - expect(binTree.dfs(node => node.key, 'POST', binTree.root, 'RECURSIVE')).toEqual([ + expect(binTree.dfs(node => node.key, 'POST', false, binTree.root, 'RECURSIVE')).toEqual([ 16, 15, 28, 30, 29, 20, 45, 55, 50, 40, 35 ]); expect(binTree.bfs(node => node.key, binTree.root, 'RECURSIVE')).toEqual([ diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 5a27122..517d5ea 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -90,7 +90,7 @@ describe('BST operations test', () => { expect(nodes.map(node => node.key)).toEqual([15]); let subTreeSum = 0; - if (node15) bst.dfs(node => (subTreeSum += node.key), 'PRE', 15); + if (node15) bst.dfs(node => (subTreeSum += node.key), 'PRE', false, 15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -299,7 +299,7 @@ describe('BST operations test', () => { expect(minNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) objBST.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) objBST.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -492,7 +492,7 @@ describe('BST operations test', () => { it('should search in range', () => { const bst = new BST([10, 5, 15, 3, 7, 12, 18]); - expect(bst.rangeSearch([4, 12])).toEqual([10, 12, 5, 7]); + expect(bst.rangeSearch([4, 12])).toEqual([5, 7, 10, 12]); expect(() => bst.rangeSearch([12, 4])).toThrow('low must be less than or equal to high'); expect(bst.rangeSearch([12, 12])).toEqual([12]); }); @@ -535,7 +535,7 @@ describe('BST operations test recursively', () => { expect(minNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) bst.dfs(node => (subTreeSum += node.key), 'PRE', 15); + if (node15) bst.dfs(node => (subTreeSum += node.key), 'PRE', false, 15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -742,7 +742,7 @@ describe('BST operations test recursively', () => { expect(minNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) objBST.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) objBST.dfs(node => (subTreeSum += node.key), 'PRE', false, node15); expect(subTreeSum).toBe(70); let lesserSum = 0; @@ -1080,10 +1080,10 @@ describe('BST Performance test', function () { it('should dfs as sub tree traversal, null should be ignored', () => { const bst = new BST(); bst.addMany([4, 2, 6, 1, 3, 5, 7]); - expect(bst.dfs(node => node.key, 'PRE', bst.getNode(6), 'ITERATIVE')).toEqual([6, 5, 7]); - expect(bst.dfs(node => node.key, 'PRE', bst.getNode(6), 'RECURSIVE')).toEqual([6, 5, 7]); - expect(bst.dfs(node => node?.key ?? undefined, 'PRE', bst.getNode(6), 'ITERATIVE')).toEqual([6, 5, 7]); - expect(bst.dfs(node => node?.key ?? undefined, 'PRE', bst.getNode(6), 'RECURSIVE')).toEqual([6, 5, 7]); + expect(bst.dfs(node => node.key, 'PRE', false, bst.getNode(6), 'ITERATIVE')).toEqual([6, 5, 7]); + expect(bst.dfs(node => node.key, 'PRE', false, bst.getNode(6), 'RECURSIVE')).toEqual([6, 5, 7]); + expect(bst.dfs(node => node?.key ?? undefined, 'PRE', false, bst.getNode(6), 'ITERATIVE')).toEqual([6, 5, 7]); + expect(bst.dfs(node => node?.key ?? undefined, 'PRE', false, bst.getNode(6), 'RECURSIVE')).toEqual([6, 5, 7]); }); }); @@ -1565,9 +1565,9 @@ describe('classic use', () => { // Test case for finding elements in a given range it('@example Find elements in a range', () => { const bst = new BST([10, 5, 15, 3, 7, 12, 18]); - expect(bst.search(new Range(5, 10))).toEqual([10, 5, 7]); - expect(bst.rangeSearch([4, 12], node => node.key.toString())).toEqual(['10', '12', '5', '7']); - expect(bst.search(new Range(4, 12, true, false))).toEqual([10, 5, 7]); + expect(bst.search(new Range(5, 10))).toEqual([5, 7, 10]); + expect(bst.rangeSearch([4, 12], node => node.key.toString())).toEqual(['5', '7', '10', '12']); + expect(bst.search(new Range(4, 12, true, false))).toEqual([5, 7, 10]); expect(bst.rangeSearch([15, 20])).toEqual([15, 18]); expect(bst.search(new Range(15, 20, false))).toEqual([18]); }); diff --git a/test/unit/data-structures/binary-tree/red-black-tree.test.ts b/test/unit/data-structures/binary-tree/red-black-tree.test.ts index 9bc5e22..43a12ca 100644 --- a/test/unit/data-structures/binary-tree/red-black-tree.test.ts +++ b/test/unit/data-structures/binary-tree/red-black-tree.test.ts @@ -504,11 +504,11 @@ describe('RedBlackTree 2', () => { expect(rbTree.isBST()).toBe(true); expect(rbTree.isBST(rbTree.root, 'RECURSIVE')).toBe(true); - expect(rbTree.dfs(n => n.key, 'IN', rbTree.root, 'ITERATIVE')).toEqual([ + expect(rbTree.dfs(n => n.key, 'IN', false, rbTree.root, 'ITERATIVE')).toEqual([ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 ]); - expect(rbTree.dfs(n => n.key, 'IN', rbTree.root, 'RECURSIVE')).toEqual([ + expect(rbTree.dfs(n => n.key, 'IN', false, rbTree.root, 'RECURSIVE')).toEqual([ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 ]); @@ -524,7 +524,7 @@ describe('RedBlackTree 2', () => { expect(rbTree.size).toBe(0); expect(rbTree.isBST()).toBe(true); - expect(rbTree.dfs(n => n.key, 'IN', rbTree.root, 'ITERATIVE')).toEqual([]); + expect(rbTree.dfs(n => n.key, 'IN', false, rbTree.root, 'ITERATIVE')).toEqual([]); rbTree.clear(); for (let i = 0; i < 1000; i++) { @@ -838,14 +838,6 @@ describe('real world data', () => { }); describe('classic use', () => { - // Test case for finding elements in a given range - it('@example Find elements in a range', () => { - const bst = new RedBlackTree([10, 5, 15, 3, 7, 12, 18]); - expect(bst.search(new Range(5, 10))).toEqual([5, 10, 7]); - expect(bst.search(new Range(4, 12))).toEqual([5, 10, 12, 7]); - expect(bst.search(new Range(15, 20))).toEqual([15, 18]); - }); - it('@example using Red-Black Tree as a price-based index for stock data', () => { // Define the structure of individual stock records interface StockRecord { @@ -887,6 +879,6 @@ describe('classic use', () => { [200, 400], // Price range node => priceIndex.get(node)?.symbol // Extract stock symbols for the result ); - expect(stocksInRange).toEqual(['GOOGL', 'MSFT', 'META']); // Verify stocks in the range + expect(stocksInRange).toEqual(['GOOGL', 'META', 'MSFT']); // Verify stocks in the range }); }); diff --git a/test/unit/data-structures/binary-tree/tree-counter.test.ts b/test/unit/data-structures/binary-tree/tree-counter.test.ts index 42bec96..8eeac1b 100644 --- a/test/unit/data-structures/binary-tree/tree-counter.test.ts +++ b/test/unit/data-structures/binary-tree/tree-counter.test.ts @@ -149,7 +149,7 @@ describe('TreeCounter operations test1', () => { expect(minNodeBySpecificNode?.key).toBe(14); let subTreeSum = 0; - if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', false, 15); expect(subTreeSum).toBe(45); let lesserSum = 0; treeCounter.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); @@ -157,7 +157,7 @@ describe('TreeCounter operations test1', () => { expect(node15 instanceof TreeCounterNode); if (node15 instanceof TreeCounterNode) { - const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', 15); + const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', false, 15); expect(subTreeAdd); } const node11 = treeCounter.getNode(11); @@ -407,7 +407,7 @@ describe('TreeCounter operations test recursively1', () => { expect(minNodeBySpecificNode?.key).toBe(14); let subTreeSum = 0; - if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', false, 15); expect(subTreeSum).toBe(45); let lesserSum = 0; expect(treeCounter.has(9)).toBe(true); @@ -423,7 +423,7 @@ describe('TreeCounter operations test recursively1', () => { expect(node15 instanceof TreeCounterNode); if (node15 instanceof TreeCounterNode) { - const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', 15); + const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', false, 15); expect(subTreeAdd); } const node11 = treeCounter.getNode(11); diff --git a/test/unit/data-structures/binary-tree/tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts index fa50657..1638e25 100644 --- a/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts @@ -519,11 +519,11 @@ describe('TreeMultiMap 2', () => { expect(tmm.isBST()).toBe(true); expect(tmm.isBST(tmm.root, 'RECURSIVE')).toBe(true); - expect(tmm.dfs(n => n.key, 'IN', tmm.root, 'ITERATIVE')).toEqual([ + expect(tmm.dfs(n => n.key, 'IN', false, tmm.root, 'ITERATIVE')).toEqual([ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 ]); - expect(tmm.dfs(n => n.key, 'IN', tmm.root, 'RECURSIVE')).toEqual([ + expect(tmm.dfs(n => n.key, 'IN', false, tmm.root, 'RECURSIVE')).toEqual([ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 ]); @@ -539,7 +539,7 @@ describe('TreeMultiMap 2', () => { expect(tmm.size).toBe(0); expect(tmm.isBST()).toBe(true); - expect(tmm.dfs(n => n.key, 'IN', tmm.root, 'ITERATIVE')).toEqual([]); + expect(tmm.dfs(n => n.key, 'IN', false, tmm.root, 'ITERATIVE')).toEqual([]); tmm.clear(); for (let i = 0; i < 1000; i++) { @@ -840,8 +840,8 @@ describe('classic use', () => { // Test case for finding elements in a given range it('@example Find elements in a range', () => { const tmm = new TreeMultiMap([10, 5, 15, 3, 7, 12, 18]); - expect(tmm.search(new Range(5, 10))).toEqual([5, 10, 7]); - expect(tmm.search(new Range(4, 12))).toEqual([5, 10, 12, 7]); + expect(tmm.search(new Range(5, 10))).toEqual([5, 7, 10]); + expect(tmm.search(new Range(4, 12))).toEqual([5, 7, 10, 12]); expect(tmm.search(new Range(15, 20))).toEqual([15, 18]); }); });