diff --git a/README.md b/README.md index 0ac942e..eb15c76 100644 --- a/README.md +++ b/README.md @@ -828,6 +828,7 @@ macOS Big Sur Version 11.7.9 +***Our performance testing is conducted directly on the TypeScript source code. The actual performance of the compiled JavaScript code is generally 3 times higher. We have compared it with C++, and it is only 30% slower than C++.*** [//]: # (No deletion!!! Start of Replace Section)
diff --git a/package.json b/package.json index 6fb1fc5..c6f5dfe 100644 --- a/package.json +++ b/package.json @@ -3,19 +3,19 @@ "version": "1.54.0", "description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python", "main": "dist/cjs/index.js", - "module": "dist/mjs/index.js", + "module": "dist/esm/index.js", "browser": "dist/umd/data-structure-typed.min.js", - "types": "dist/mjs/index.d.ts", + "types": "dist/esm/index.d.ts", "umd:main": "dist/umd/data-structure-typed.min.js", "exports": { ".": { - "import": "./dist/mjs/index.js", + "import": "./dist/esm/index.js", "require": "./dist/cjs/index.js" } }, "scripts": { - "build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs-class", - "build:mjs": "rm -rf dist/mjs && tsc -p tsconfig-mjs.json", + "build": "npm run build:esm && npm run build:cjs && npm run build:umd && npm run build:docs-class", + "build:esm": "rm -rf dist/esm && tsc -p tsconfig-esm.json", "build:cjs": "rm -rf dist/cjs && tsc -p tsconfig-cjs.json", "build:umd": "tsup", "build:docs": "npm run gen:examples && typedoc --out docs ./src", @@ -24,7 +24,7 @@ "test:in-band": "jest --runInBand", "test": "npm run test:in-band", "test:integration": "npm run update:subs && jest --config jest.integration.config.js && tsc test/integration/compile.ts", - "test:perf": "npm run build:cjs && npm run build:mjs && ts-node test/performance/reportor.ts", + "test:perf": "npm run build:cjs && npm run build:esm && ts-node test/performance/reportor.ts", "check": "tsc --noEmit", "check:circular-refs": "dependency-cruiser src", "lint:src": "eslint --fix 'src/**/*.{js,ts}'", @@ -54,7 +54,7 @@ "url": "https://github.com/zrwusa/data-structure-typed/issues" }, "homepage": "https://data-structure-typed-docs.vercel.app", - "author": "Tyler Zeng ", + "author": "Pablo Zeng ", "license": "MIT", "publishConfig": { "@zrwusa:registry": "https://npm.pkg.github.com" diff --git a/src/data-structures/binary-tree/avl-tree-counter.ts b/src/data-structures/binary-tree/avl-tree-counter.ts new file mode 100644 index 0000000..314d621 --- /dev/null +++ b/src/data-structures/binary-tree/avl-tree-counter.ts @@ -0,0 +1,463 @@ +/** + * data-structure-typed + * + * @author Pablo Zeng + * @copyright Copyright (c) 2022 Pablo Zeng + * @license MIT License + */ +import type { + AVLTreeCounterOptions, + BinaryTreeDeleteResult, + BSTNOptKeyOrNode, + BTNRep, + EntryCallback, + IterationType, + OptNodeOrNull +} from '../../types'; +import { IBinaryTree } from '../../interfaces'; +import { AVLTree, AVLTreeNode } from './avl-tree'; + +export class AVLTreeCounterNode extends AVLTreeNode { + /** + * The constructor function initializes a BinaryTreeNode object with a key, value, and count. + * @param {K} key - The `key` parameter is of type `K` and represents the unique identifier + * of the binary tree node. + * @param {V} [value] - The `value` parameter is an optional parameter of type `V`. It represents the value of the binary + * tree node. If no value is provided, it will be `undefined`. + * @param {number} [count=1] - The `count` parameter is a number that represents the number of times a particular value + * occurs in a binary tree node. It has a default value of 1, which means that if no value is provided for the `count` + * parameter when creating a new instance of the `BinaryTreeNode` class. + */ + constructor(key: K, value?: V, count = 1) { + super(key, value); + this.count = count; + } + + override parent?: AVLTreeCounterNode = undefined; + + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { + return this._left; + } + + override set left(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._left = v; + } + + override _right?: OptNodeOrNull> = undefined; + + override get right(): OptNodeOrNull> { + return this._right; + } + + override set right(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._right = v; + } +} + +/** + * The only distinction between a AVLTreeCounter and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters. + */ +export class AVLTreeCounter + extends AVLTree + implements IBinaryTree +{ + /** + * The constructor initializes a new AVLTreeCounter object with optional initial elements. + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an + * iterable object that can contain either keys, nodes, entries, or raw elements. + * @param [options] - The `options` parameter is an optional object that can be used to customize the + * behavior of the AVLTreeCounter. It can include properties such as `compareKeys` and + * `compareValues` functions to define custom comparison logic for keys and values, respectively. + */ + constructor( + keysNodesEntriesOrRaws: Iterable> | R> = [], + options?: AVLTreeCounterOptions + ) { + super([], options); + if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); + } + + protected _count = 0; + + /** + * The function calculates the sum of the count property of all nodes in a tree using depth-first + * search. + * @returns the sum of the count property of all nodes in the tree. + */ + get count(): number { + return this._count; + } + + /** + * Time Complexity: O(n) + * Space Complexity: O(1) + * + * The function calculates the sum of the count property of all nodes in a tree using depth-first + * search. + * @returns the sum of the count property of all nodes in the tree. + */ + getComputedCount(): number { + let sum = 0; + this.dfs(node => (sum += node.count)); + return sum; + } + + /** + * The function creates a new AVLTreeCounterNode with the specified key, value, and count. + * @param {K} key - The key parameter represents the key of the node being created. It is of type K, + * which is a generic type that can be replaced with any specific type when using the function. + * @param {V} [value] - The `value` parameter is an optional parameter that represents the value + * associated with the key in the node. It is of type `V`, which can be any data type. + * @param {number} [count] - The `count` parameter represents the number of occurrences of a + * key-value pair in the AVLTreeCounterNode. It is an optional parameter, so it can be omitted when + * calling the `createNode` method. If provided, it specifies the initial count for the node. + * @returns a new instance of the AVLTreeCounterNode class, casted as AVLTreeCounterNode. + */ + override createNode(key: K, value?: V, count?: number): AVLTreeCounterNode { + return new AVLTreeCounterNode(key, this._isMapMode ? undefined : value, count) as AVLTreeCounterNode; + } + + /** + * The function creates a new AVLTreeCounter object with the specified options and returns it. + * @param [options] - The `options` parameter is an optional object that contains additional + * configuration options for creating the AVLTreeCounter. It can have the following properties: + * @returns a new instance of the AVLTreeCounter class, with the specified options, as a TREE + * object. + */ + override createTree(options?: AVLTreeCounterOptions) { + return new AVLTreeCounter([], { + iterationType: this.iterationType, + isMapMode: this._isMapMode, + specifyComparable: this._specifyComparable, + toEntryFn: this._toEntryFn, + isReverse: this._isReverse, + ...options + }); + } + + /** + * The function checks if the input is an instance of AVLTreeCounterNode. + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is + * an instance of the `AVLTreeCounterNode` class. + */ + override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is AVLTreeCounterNode { + return keyNodeOrEntry instanceof AVLTreeCounterNode; + } + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + * The function overrides the add method of a TypeScript class to add a new node to a data structure + * and update the count. + * @param {BTNRep>} 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, + * 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. + * @param [count=1] - The `count` parameter represents the number of times the key-value pair should + * be added to the data structure. By default, it is set to 1, meaning that the key-value pair will + * be added once. However, you can specify a different value for `count` if you want to add + * @returns a boolean value. + */ + override add(keyNodeOrEntry: BTNRep>, value?: V, count = 1): boolean { + const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value, count); + if (newNode === undefined) return false; + + const orgNodeCount = newNode?.count || 0; + const inserted = super.add(newNode, newValue); + if (inserted) { + this._count += orgNodeCount; + } + return true; + } + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + * The function overrides the delete method in a binary tree data structure, handling deletion of + * nodes and maintaining balance in the tree. + * @param {BTNRep>} 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. + * @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a + * boolean flag that determines whether to ignore the count of the node being deleted. If + * `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If + * `ignoreCount` is set to + * @returns The `delete` method overrides the default delete behavior in a binary tree data + * structure. It takes a predicate or node to be deleted and an optional flag to ignore count. The + * method returns an array of `BinaryTreeDeleteResult` objects, each containing information about the + * deleted node and whether balancing is needed in the tree. + */ + override delete( + keyNodeOrEntry: BTNRep>, + ignoreCount = false + ): BinaryTreeDeleteResult>[] { + const deletedResult: BinaryTreeDeleteResult>[] = []; + if (!this.root) return deletedResult; + + const curr: AVLTreeCounterNode | undefined = this.getNode(keyNodeOrEntry) ?? undefined; + if (!curr) return deletedResult; + + const parent: AVLTreeCounterNode | undefined = curr?.parent ? curr.parent : undefined; + let needBalanced: AVLTreeCounterNode | undefined = undefined, + orgCurrent: AVLTreeCounterNode | undefined = curr; + + if (curr.count > 1 && !ignoreCount) { + curr.count--; + this._count--; + } else { + if (!curr.left) { + if (!parent) { + if (curr.right !== undefined && curr.right !== null) this._setRoot(curr.right); + } else { + const { familyPosition: fp } = curr; + if (fp === 'LEFT' || fp === 'ROOT_LEFT') { + parent.left = curr.right; + } else if (fp === 'RIGHT' || fp === 'ROOT_RIGHT') { + parent.right = curr.right; + } + needBalanced = parent; + } + } else { + const leftSubTreeRightMost = curr.left ? this.getRightMost(node => node, curr.left) : undefined; + if (leftSubTreeRightMost) { + const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; + orgCurrent = this._swapProperties(curr, leftSubTreeRightMost); + if (parentOfLeftSubTreeMax) { + if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) { + parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; + } else { + parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left; + } + needBalanced = parentOfLeftSubTreeMax; + } + } + } + this._size = this._size - 1; + // TODO How to handle when the count of target node is lesser than current node's count + if (orgCurrent) this._count -= orgCurrent.count; + } + + deletedResult.push({ deleted: orgCurrent, needBalanced }); + + if (needBalanced) { + this._balancePath(needBalanced); + } + + return deletedResult; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The "clear" function overrides the parent class's "clear" function and also resets the count to + * zero. + */ + override clear() { + super.clear(); + this._count = 0; + } + + /** + * Time Complexity: O(n log n) + * Space Complexity: O(log n) + * The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search + * tree using either a recursive or iterative approach. + * @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that + * specifies the type of iteration to use when building the balanced binary search tree. It has a + * default value of `this.iterationType`, which means it will use the iteration type currently set in + * the object. + * @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the + * balancing operation is successful, and `false` if there are no nodes to balance. + */ + override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean { + const sorted = this.dfs(node => node, 'IN'), + n = sorted.length; + if (sorted.length < 1) return false; + + this.clear(); + + if (iterationType === 'RECURSIVE') { + const buildBalanceBST = (l: number, r: number) => { + if (l > 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); + buildBalanceBST(l, m - 1); + buildBalanceBST(m + 1, r); + }; + + buildBalanceBST(0, n - 1); + return true; + } else { + const stack: [[number, number]] = [[0, n - 1]]; + while (stack.length > 0) { + const popped = stack.pop(); + if (popped) { + const [l, r] = popped; + if (l <= r) { + 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); + stack.push([m + 1, r]); + stack.push([l, m - 1]); + } + } + } + return true; + } + } + + /** + * Time complexity: O(n) + * Space complexity: O(n) + * + * The function overrides the clone method to create a deep copy of a tree object. + * @returns The `clone()` method is returning a cloned instance of the `TREE` object. + */ + override clone() { + const cloned = this.createTree(); + if (this._isMapMode) this.bfs(node => cloned.add(node.key, undefined, node.count)); + else this.bfs(node => cloned.add(node.key, node.value, node.count)); + if (this._isMapMode) cloned._store = this._store; + return cloned; + } + + /** + * The `map` function in TypeScript overrides the default behavior to create a new AVLTreeCounter + * with modified entries based on a provided callback. + * @param callback - The `callback` parameter is a function that will be called for each entry in the + * AVLTreeCounter. It takes four arguments: + * @param [options] - The `options` parameter in the `override map` function is of type + * `AVLTreeCounterOptions`. This parameter allows you to provide additional + * configuration options when creating a new `AVLTreeCounter` instance within the `map` function. + * These options + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) for the callback function. This can be useful when you want to access properties + * or + * @returns The `map` method is returning a new `AVLTreeCounter` instance with the entries + * transformed by the provided `callback` function. Each entry in the original tree is passed to the + * `callback` function along with the index and the original tree itself. The transformed entries are + * then added to the new `AVLTreeCounter` instance, which is returned at the end. + */ + override map( + callback: EntryCallback, + options?: AVLTreeCounterOptions, + thisArg?: any + ): AVLTreeCounter { + const newTree = new AVLTreeCounter([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + + /** + * The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into + * a node object. + * @param {BTNRep>} keyNodeOrEntry - The + * `keyNodeOrEntry` parameter can be of type `R` or `BTNRep>`. + * @param {V} [value] - The `value` parameter is an optional value that can be passed to the + * `override` function. It represents the value associated with the key in the data structure. If no + * value is provided, it will default to `undefined`. + * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of + * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. + * @returns either a AVLTreeCounterNode object or undefined. + */ + protected override _keyValueNodeOrEntryToNodeAndValue( + keyNodeOrEntry: BTNRep>, + value?: V, + count = 1 + ): [AVLTreeCounterNode | undefined, V | undefined] { + if (keyNodeOrEntry === undefined || keyNodeOrEntry === null) return [undefined, undefined]; + if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value]; + + if (this.isEntry(keyNodeOrEntry)) { + const [key, entryValue] = keyNodeOrEntry; + if (key === undefined || key === null) return [undefined, undefined]; + const finalValue = value ?? entryValue; + return [this.createNode(key, finalValue, count), finalValue]; + } + + return [this.createNode(keyNodeOrEntry, value, count), value]; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The `_swapProperties` function swaps the properties (key, value, count, height) between two nodes + * in a binary search tree. + * @param {BSTNOptKeyOrNode>} srcNode - The `srcNode` parameter represents the source node + * that will be swapped with the `destNode`. + * @param {BSTNOptKeyOrNode>} destNode - The `destNode` parameter represents the destination + * node where the properties will be swapped with the source node. + * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. + * If either `srcNode` or `destNode` is undefined, it returns `undefined`. + */ + protected override _swapProperties( + srcNode: BSTNOptKeyOrNode>, + destNode: BSTNOptKeyOrNode> + ): AVLTreeCounterNode | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); + if (srcNode && destNode) { + const { key, value, count, height } = destNode; + const tempNode = this.createNode(key, value, count); + if (tempNode) { + tempNode.height = height; + + destNode.key = srcNode.key; + if (!this._isMapMode) destNode.value = srcNode.value; + destNode.count = srcNode.count; + destNode.height = srcNode.height; + + srcNode.key = tempNode.key; + if (!this._isMapMode) srcNode.value = tempNode.value; + srcNode.count = tempNode.count; + srcNode.height = tempNode.height; + } + + return destNode; + } + return undefined; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function replaces an old node with a new node and updates the count property of the new node. + * @param {AVLTreeCounterNode} oldNode - The oldNode parameter represents the node that needs to be replaced in the + * data structure. It is of type AVLTreeCounterNode. + * @param {AVLTreeCounterNode} newNode - The `newNode` parameter is an instance of the `AVLTreeCounterNode` class. + * @returns The method is returning the result of calling the `_replaceNode` method from the + * superclass, which is of type `AVLTreeCounterNode`. + */ + protected override _replaceNode( + oldNode: AVLTreeCounterNode, + newNode: AVLTreeCounterNode + ): AVLTreeCounterNode { + newNode.count = oldNode.count + newNode.count; + return super._replaceNode(oldNode, newNode); + } +} 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 819d910..2d2119a 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -5,32 +5,21 @@ * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ -import type { - AVLTreeMultiMapOptions, - BinaryTreeDeleteResult, - BSTNOptKeyOrNode, - BTNRep, - EntryCallback, - IterationType, - OptNodeOrNull -} from '../../types'; -import { IBinaryTree } from '../../interfaces'; +import { AVLTreeMultiMapOptions, BTNOptKeyOrNull, BTNRep, OptNodeOrNull } from '../../types'; import { AVLTree, AVLTreeNode } from './avl-tree'; -export class AVLTreeMultiMapNode extends AVLTreeNode { +export class AVLTreeMultiMapNode extends AVLTreeNode { /** - * The constructor function initializes a BinaryTreeNode object with a key, value, and count. - * @param {K} key - The `key` parameter is of type `K` and represents the unique identifier - * of the binary tree node. - * @param {V} [value] - The `value` parameter is an optional parameter of type `V`. It represents the value of the binary - * tree node. If no value is provided, it will be `undefined`. - * @param {number} [count=1] - The `count` parameter is a number that represents the number of times a particular value - * occurs in a binary tree node. It has a default value of 1, which means that if no value is provided for the `count` - * parameter when creating a new instance of the `BinaryTreeNode` class. + * This TypeScript constructor initializes an object with a key of type K and an array of values of + * type V. + * @param {K} key - The `key` parameter is typically used to store a unique identifier or key for the + * data being stored in the data structure. It helps in quickly accessing or retrieving the + * associated value in the data structure. + * @param {V[]} value - The `value` parameter in the constructor represents an array of values of + * type `V`. */ - constructor(key: K, value?: V, count = 1) { + constructor(key: K, value: V[]) { super(key, value); - this.count = count; } override parent?: AVLTreeMultiMapNode = undefined; @@ -63,79 +52,52 @@ export class AVLTreeMultiMapNode extends AVLTreeNode { } /** - * The only distinction between a AVLTreeMultiMap and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters. + * */ -export class AVLTreeMultiMap - extends AVLTree - implements IBinaryTree -{ +export class AVLTreeMultiMap extends AVLTree< + K, + V[], + R, + MK, + MV, + MR +> { /** - * The constructor initializes a new AVLTreeMultiMap object with optional initial elements. - * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an - * iterable object that can contain either keys, nodes, entries, or raw elements. - * @param [options] - The `options` parameter is an optional object that can be used to customize the - * behavior of the AVLTreeMultiMap. It can include properties such as `compareKeys` and - * `compareValues` functions to define custom comparison logic for keys and values, respectively. + * The constructor initializes an AVLTreeMultiMap with the provided keys, nodes, entries, or raw data + * and options. + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an + * iterable that can contain either key-value pairs represented as `BTNRep>` or raw data represented as `R`. This parameter is used to initialize + * the AVLTreeMulti + * @param [options] - The `options` parameter in the constructor is of type + * `AVLTreeMultiMapOptions`. It is an optional parameter that allows you to specify + * additional options for configuring the AVLTreeMultiMap instance. */ constructor( - keysNodesEntriesOrRaws: Iterable>> = [], - options?: AVLTreeMultiMapOptions + keysNodesEntriesOrRaws: Iterable> | R> = [], + options?: AVLTreeMultiMapOptions ) { - super([], options); - if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); - } - - protected _count = 0; - - /** - * The function calculates the sum of the count property of all nodes in a tree using depth-first - * search. - * @returns the sum of the count property of all nodes in the tree. - */ - get count(): number { - return this._count; + super([], { ...options, isMapMode: true }); + if (keysNodesEntriesOrRaws) { + this.addMany(keysNodesEntriesOrRaws); + } } /** - * Time Complexity: O(n) + * Time Complexity: O(1) * Space Complexity: O(1) * - * The function calculates the sum of the count property of all nodes in a tree using depth-first - * search. - * @returns the sum of the count property of all nodes in the tree. + * The function `createTree` in TypeScript overrides the creation of an AVLTreeMultiMap with + * specified options. + * @param [options] - The `options` parameter in the `createTree` function is of type + * `AVLTreeMultiMapOptions`. This means it is an object that can have properties of type + * `K`, `V[]`, and `R`. The function creates a new `AVL + * @returns The `createTree` method is returning a new instance of `AVLTreeMultiMap` with the + * provided options. */ - getComputedCount(): number { - let sum = 0; - this.dfs(node => (sum += node.count)); - return sum; - } - - /** - * The function creates a new AVLTreeMultiMapNode with the specified key, value, and count. - * @param {K} key - The key parameter represents the key of the node being created. It is of type K, - * which is a generic type that can be replaced with any specific type when using the function. - * @param {V} [value] - The `value` parameter is an optional parameter that represents the value - * associated with the key in the node. It is of type `V`, which can be any data type. - * @param {number} [count] - The `count` parameter represents the number of occurrences of a - * key-value pair in the AVLTreeMultiMapNode. It is an optional parameter, so it can be omitted when - * calling the `createNode` method. If provided, it specifies the initial count for the node. - * @returns a new instance of the AVLTreeMultiMapNode class, casted as AVLTreeMultiMapNode. - */ - override createNode(key: K, value?: V, count?: number): AVLTreeMultiMapNode { - return new AVLTreeMultiMapNode(key, this._isMapMode ? undefined : value, count) as AVLTreeMultiMapNode; - } - - /** - * The function creates a new AVLTreeMultiMap object with the specified options and returns it. - * @param [options] - The `options` parameter is an optional object that contains additional - * configuration options for creating the AVLTreeMultiMap. It can have the following properties: - * @returns a new instance of the AVLTreeMultiMap class, with the specified options, as a TREE - * object. - */ - override createTree(options?: AVLTreeMultiMapOptions) { + override createTree(options?: AVLTreeMultiMapOptions) { return new AVLTreeMultiMap([], { iterationType: this.iterationType, - isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, @@ -143,331 +105,121 @@ export class AVLTreeMultiMap> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep>`. - * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is - * an instance of the `AVLTreeMultiMapNode` class. - */ - override isNode( - keyNodeEntryOrRaw: BTNRep> | R - ): keyNodeEntryOrRaw is AVLTreeMultiMapNode { - return keyNodeEntryOrRaw instanceof AVLTreeMultiMapNode; - } - - /** - * Time Complexity: O(log n) - * Space Complexity: O(1) - * - * The function overrides the add method of a TypeScript class to add a new node to a data structure - * and update the count. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The - * `keyNodeEntryOrRaw` parameter can accept a value of type `R`, which can be any type. It - * can also accept a value of type `BTNRep>`, which represents a key, node, - * entry, or raw element - * @param {V} [value] - The `value` parameter represents the value associated with the key in the - * data structure. It is an optional parameter, so it can be omitted if not needed. - * @param [count=1] - The `count` parameter represents the number of times the key-value pair should - * be added to the data structure. By default, it is set to 1, meaning that the key-value pair will - * be added once. However, you can specify a different value for `count` if you want to add - * @returns a boolean value. - */ - override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V, count = 1): boolean { - const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count); - if (newNode === undefined) return false; - - const orgNodeCount = newNode?.count || 0; - const inserted = super.add(newNode, newValue); - if (inserted) { - this._count += orgNodeCount; - } - return true; - } - - /** - * Time Complexity: O(log n) - * Space Complexity: O(1) - * - * The function overrides the delete method in a binary tree data structure, handling deletion of - * nodes and maintaining balance in the tree. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The `predicate` - * 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. - * @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a - * boolean flag that determines whether to ignore the count of the node being deleted. If - * `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If - * `ignoreCount` is set to - * @returns The `delete` method overrides the default delete behavior in a binary tree data - * structure. It takes a predicate or node to be deleted and an optional flag to ignore count. The - * method returns an array of `BinaryTreeDeleteResult` objects, each containing information about the - * deleted node and whether balancing is needed in the tree. - */ - override delete( - keyNodeEntryOrRaw: BTNRep> | R, - ignoreCount = false - ): BinaryTreeDeleteResult>[] { - const deletedResult: BinaryTreeDeleteResult>[] = []; - if (!this.root) return deletedResult; - - const curr: AVLTreeMultiMapNode | undefined = this.getNode(keyNodeEntryOrRaw) ?? undefined; - if (!curr) return deletedResult; - - const parent: AVLTreeMultiMapNode | undefined = curr?.parent ? curr.parent : undefined; - let needBalanced: AVLTreeMultiMapNode | undefined = undefined, - orgCurrent: AVLTreeMultiMapNode | undefined = curr; - - if (curr.count > 1 && !ignoreCount) { - curr.count--; - this._count--; - } else { - if (!curr.left) { - if (!parent) { - if (curr.right !== undefined && curr.right !== null) this._setRoot(curr.right); - } else { - const { familyPosition: fp } = curr; - if (fp === 'LEFT' || fp === 'ROOT_LEFT') { - parent.left = curr.right; - } else if (fp === 'RIGHT' || fp === 'ROOT_RIGHT') { - parent.right = curr.right; - } - needBalanced = parent; - } - } else { - const leftSubTreeRightMost = curr.left ? this.getRightMost(node => node, curr.left) : undefined; - if (leftSubTreeRightMost) { - const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; - orgCurrent = this._swapProperties(curr, leftSubTreeRightMost); - if (parentOfLeftSubTreeMax) { - if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) { - parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; - } else { - parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left; - } - needBalanced = parentOfLeftSubTreeMax; - } - } - } - this._size = this._size - 1; - // TODO How to handle when the count of target node is lesser than current node's count - if (orgCurrent) this._count -= orgCurrent.count; - } - - deletedResult.push({ deleted: orgCurrent, needBalanced }); - - if (needBalanced) { - this._balancePath(needBalanced); - } - - return deletedResult; - } - /** * Time Complexity: O(1) * Space Complexity: O(1) * - * The "clear" function overrides the parent class's "clear" function and also resets the count to - * zero. + * The function `createNode` overrides the method to create a new AVLTreeMultiMapNode with a + * specified key and an empty array of values. + * @param {K} key - The `key` parameter in the `createNode` method represents the key of the node + * that will be created in the AVLTreeMultiMap. + * @returns An AVLTreeMultiMapNode object is being returned, initialized with the provided key and an + * empty array. */ - override clear() { - super.clear(); - this._count = 0; + override createNode(key: K): AVLTreeMultiMapNode { + return new AVLTreeMultiMapNode(key, []); + } + + override add(node: BTNRep>): boolean; + + override add(key: K, value: V): boolean; + + /** + * Time Complexity: O(log n) + * Space Complexity: O(log n) + * + * The function `add` in TypeScript overrides the superclass method to add key-value pairs to an AVL + * tree multi-map. + * @param {BTNRep> | 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` + * @param {V} [value] - The `value` parameter in the `override add` method represents the value that + * you want to add to the AVLTreeMultiMap. It can be a single value or an array of values associated + * with a specific key. + * @returns The `override add` method is returning a boolean value, which indicates whether the + * addition operation was successful or not. + */ + override add(keyNodeOrEntry: BTNRep> | K, value?: V): boolean { + if (this.isRealNode(keyNodeOrEntry)) return super.add(keyNodeOrEntry); + + const _commonAdd = (key?: BTNOptKeyOrNull, values?: V[]) => { + if (key === undefined || key === null) return false; + + const existingValues = this.get(key); + if (existingValues !== undefined && values !== undefined) { + for (const value of values) existingValues.push(value); + return true; + } + + const existingNode = this.getNode(key); + if (this.isRealNode(existingNode)) { + if (existingValues === undefined) { + super.add(key, values); + return true; + } + if (values !== undefined) { + for (const value of values) existingValues.push(value); + return true; + } else { + return false; + } + } else { + return super.add(key, values); + } + }; + + if (this.isEntry(keyNodeOrEntry)) { + const [key, values] = keyNodeOrEntry; + return _commonAdd(key, value !== undefined ? [value] : values); + } + + return _commonAdd(keyNodeOrEntry, value !== undefined ? [value] : undefined); } /** - * Time Complexity: O(n log n) + * Time Complexity: O(log n) * Space Complexity: O(log n) - * The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search - * tree using either a recursive or iterative approach. - * @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that - * specifies the type of iteration to use when building the balanced binary search tree. It has a - * default value of `this.iterationType`, which means it will use the iteration type currently set in - * the object. - * @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the - * balancing operation is successful, and `false` if there are no nodes to balance. + * + * The function `deleteValue` removes a specific value from a key in an AVLTreeMultiMap data + * structure and deletes the entire node if no values are left for that key. + * @param {BTNRep> | 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 + * value that you want to delete from the multi-map data structure associated with a particular key. + * The function checks if the value exists in the array of values associated with the key, and if + * found, removes it from the array. + * @returns The `deleteValue` function returns a boolean value. It returns `true` if the specified + * `value` was successfully deleted from the array of values associated with the `keyNodeOrEntry`. If + * the value was not found in the array, it returns `false`. */ - override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean { - const sorted = this.dfs(node => node, 'IN'), - n = sorted.length; - if (sorted.length < 1) return false; + deleteValue(keyNodeOrEntry: BTNRep> | K, value: V): boolean { + const values = this.get(keyNodeOrEntry); + if (Array.isArray(values)) { + const index = values.indexOf(value); + if (index === -1) return false; + values.splice(index, 1); - this.clear(); + // If no values left, remove the entire node + if (values.length === 0) this.delete(keyNodeOrEntry); - if (iterationType === 'RECURSIVE') { - const buildBalanceBST = (l: number, r: number) => { - if (l > 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); - buildBalanceBST(l, m - 1); - buildBalanceBST(m + 1, r); - }; - - buildBalanceBST(0, n - 1); - return true; - } else { - const stack: [[number, number]] = [[0, n - 1]]; - while (stack.length > 0) { - const popped = stack.pop(); - if (popped) { - const [l, r] = popped; - if (l <= r) { - 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); - stack.push([m + 1, r]); - stack.push([l, m - 1]); - } - } - } return true; } + return false; } /** - * Time complexity: O(n) - * Space complexity: O(n) + * Time Complexity: O(n) + * Space Complexity: O(n) * - * The function overrides the clone method to create a deep copy of a tree object. - * @returns The `clone()` method is returning a cloned instance of the `TREE` object. + * The function `clone` overrides the default cloning behavior to create a deep copy of a tree + * structure. + * @returns A cloned tree object is being returned. */ override clone() { const cloned = this.createTree(); - if (this._isMapMode) this.bfs(node => cloned.add(node.key, undefined, node.count)); - else this.bfs(node => cloned.add(node.key, node.value, node.count)); - if (this._isMapMode) cloned._store = this._store; + this._clone(cloned); return cloned; } - - /** - * The `map` function in TypeScript overrides the default behavior to create a new AVLTreeMultiMap - * with modified entries based on a provided callback. - * @param callback - The `callback` parameter is a function that will be called for each entry in the - * AVLTreeMultiMap. It takes four arguments: - * @param [options] - The `options` parameter in the `override map` function is of type - * `AVLTreeMultiMapOptions`. This parameter allows you to provide additional - * configuration options when creating a new `AVLTreeMultiMap` instance within the `map` function. - * These options - * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify - * the value of `this` when executing the `callback` function. It allows you to set the context - * (value of `this`) for the callback function. This can be useful when you want to access properties - * or - * @returns The `map` method is returning a new `AVLTreeMultiMap` instance with the entries - * transformed by the provided `callback` function. Each entry in the original tree is passed to the - * `callback` function along with the index and the original tree itself. The transformed entries are - * then added to the new `AVLTreeMultiMap` instance, which is returned at the end. - */ - override map( - callback: EntryCallback, - options?: AVLTreeMultiMapOptions, - thisArg?: any - ): AVLTreeMultiMap { - const newTree = new AVLTreeMultiMap([], options); - let index = 0; - for (const [key, value] of this) { - newTree.add(callback.call(thisArg, key, value, index++, this)); - } - return newTree; - } - - /** - * The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into - * a node object. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The - * `keyNodeEntryOrRaw` parameter can be of type `R` or `BTNRep>`. - * @param {V} [value] - The `value` parameter is an optional value that can be passed to the - * `override` function. It represents the value associated with the key in the data structure. If no - * value is provided, it will default to `undefined`. - * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of - * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. - * @returns either a AVLTreeMultiMapNode object or undefined. - */ - protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep> | R, - value?: V, - count = 1 - ): [AVLTreeMultiMapNode | undefined, V | undefined] { - if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - if (this.isEntry(keyNodeEntryOrRaw)) { - const [key, entryValue] = keyNodeEntryOrRaw; - if (key === undefined || key === null) return [undefined, undefined]; - const finalValue = value ?? entryValue; - return [this.createNode(key, finalValue, count), finalValue]; - } - - if (this.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value]; - - return [undefined, undefined]; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The `_swapProperties` function swaps the properties (key, value, count, height) between two nodes - * in a binary search tree. - * @param {R | BSTNOptKeyOrNode>} srcNode - The `srcNode` parameter represents the source node - * that will be swapped with the `destNode`. - * @param {R | BSTNOptKeyOrNode>} destNode - The `destNode` parameter represents the destination - * node where the properties will be swapped with the source node. - * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. - * If either `srcNode` or `destNode` is undefined, it returns `undefined`. - */ - protected override _swapProperties( - srcNode: R | BSTNOptKeyOrNode>, - destNode: R | BSTNOptKeyOrNode> - ): AVLTreeMultiMapNode | undefined { - srcNode = this.ensureNode(srcNode); - destNode = this.ensureNode(destNode); - if (srcNode && destNode) { - const { key, value, count, height } = destNode; - const tempNode = this.createNode(key, value, count); - if (tempNode) { - tempNode.height = height; - - destNode.key = srcNode.key; - if (!this._isMapMode) destNode.value = srcNode.value; - destNode.count = srcNode.count; - destNode.height = srcNode.height; - - srcNode.key = tempNode.key; - if (!this._isMapMode) srcNode.value = tempNode.value; - srcNode.count = tempNode.count; - srcNode.height = tempNode.height; - } - - return destNode; - } - return undefined; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function replaces an old node with a new node and updates the count property of the new node. - * @param {AVLTreeMultiMapNode} oldNode - The oldNode parameter represents the node that needs to be replaced in the - * data structure. It is of type AVLTreeMultiMapNode. - * @param {AVLTreeMultiMapNode} newNode - The `newNode` parameter is an instance of the `AVLTreeMultiMapNode` class. - * @returns The method is returning the result of calling the `_replaceNode` method from the - * superclass, which is of type `AVLTreeMultiMapNode`. - */ - protected override _replaceNode( - oldNode: AVLTreeMultiMapNode, - newNode: AVLTreeMultiMapNode - ): AVLTreeMultiMapNode { - newNode.count = oldNode.count + newNode.count; - return super._replaceNode(oldNode, newNode); - } } diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 87a56a0..741edfc 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -18,12 +18,13 @@ import { IBinaryTree } from '../../interfaces'; export class AVLTreeNode extends BSTNode { /** - * The constructor function initializes a new instance of a class with a key and an optional value, - * and sets the height property to 0. - * @param {K} key - The "key" parameter is of type K, which represents the type of the key for the - * constructor. It is used to initialize the key property of the object being created. - * @param {V} [value] - The "value" parameter is an optional parameter of type V. It represents the - * value associated with the key in the constructor. + * 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 + * within a data structure. It serves as a reference or identifier for accessing or manipulating the + * associated value or data. + * @param {V} [value] - The `value` parameter in the constructor is optional, meaning it does not + * have to be provided when creating an instance of the class. If a value is not provided, it will + * default to `undefined`. */ constructor(key: K, value?: V) { super(key, value); @@ -72,18 +73,18 @@ export class AVLTree { /** - * This is a constructor function for an AVLTree class that initializes the tree with keys, nodes, - * entries, or raw elements. - * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an - * iterable object that can contain either keys, nodes, entries, or raw elements. These elements will - * be used to initialize the AVLTree. - * @param [options] - The `options` parameter is an optional object that can be used to customize the - * behavior of the AVLTree. It can include properties such as `compareFn` (a function used to compare - * keys), `allowDuplicates` (a boolean indicating whether duplicate keys are allowed), and - * `nodeBuilder` ( + * This TypeScript constructor initializes an AVLTree with keys, nodes, entries, or raw data provided + * in an iterable format. + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an + * iterable that can contain either `BTNRep>` 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 + * AVL tree. These options could include things like custom comparators, initial capacity, or any + * other configuration settings specific */ constructor( - keysNodesEntriesOrRaws: Iterable>> = [], + keysNodesEntriesOrRaws: Iterable> | R> = [], options?: AVLTreeOptions ) { super([], options); @@ -91,6 +92,9 @@ export class AVLTree> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep>`. - * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `AVLTreeNode` class. */ - override isNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is AVLTreeNode { - return keyNodeEntryOrRaw instanceof AVLTreeNode; + override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is AVLTreeNode { + return keyNodeOrEntry instanceof AVLTreeNode; } /** * Time Complexity: O(log n) - * Space Complexity: O(1) + * Space Complexity: O(log n) * * The function overrides the add method of a class and inserts a key-value pair into a data * structure, then balances the path. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept values of type `R`, `BTNRep>`, or - * `RawElement`. + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can accept values of type `R`, `BTNRep>` * @param {V} [value] - The `value` parameter is an optional value that you want to associate with * the key or node being added to the data structure. * @returns The method is returning a boolean value. */ - override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V): boolean { - if (keyNodeEntryOrRaw === null) return false; - const inserted = super.add(keyNodeEntryOrRaw, value); - if (inserted) this._balancePath(keyNodeEntryOrRaw); + override add(keyNodeOrEntry: BTNRep>, value?: V): boolean { + if (keyNodeOrEntry === null) return false; + const inserted = super.add(keyNodeOrEntry, value); + if (inserted) this._balancePath(keyNodeOrEntry); return inserted; } /** * Time Complexity: O(log n) - * Space Complexity: O(1) + * Space Complexity: O(log n) * * The function overrides the delete method in a TypeScript class, performs deletion, and then * balances the tree if necessary. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @param {BTNRep>} 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(keyNodeEntryOrRaw: BTNRep> | R): BinaryTreeDeleteResult>[] { - const deletedResults = super.delete(keyNodeEntryOrRaw); + override delete(keyNodeOrEntry: BTNRep>): BinaryTreeDeleteResult>[] { + const deletedResults = super.delete(keyNodeOrEntry); for (const { needBalanced } of deletedResults) { if (needBalanced) { this._balancePath(needBalanced); @@ -175,6 +184,26 @@ export class AVLTree`. It is an optional parameter that allows you to specify additional + * options for the AVL tree being created during the mapping process. These options could include + * custom comparators, initial + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) within the callback function. This can be useful when you want to access + * properties or + * @returns The `map` method is returning a new AVLTree instance (`newTree`) with the entries + * modified by the provided callback function. + */ override map( callback: EntryCallback, options?: AVLTreeOptions, @@ -188,6 +217,14 @@ export class AVLTree>} srcNode - The `srcNode` parameter represents either a node + * @param {BSTNOptKeyOrNode>} srcNode - The `srcNode` parameter represents either a node * object (`AVLTreeNode`) or a key-value pair (`R`) that is being swapped with another node. - * @param {R | BSTNOptKeyOrNode>} destNode - The `destNode` parameter is either an instance of + * @param {BSTNOptKeyOrNode>} destNode - The `destNode` parameter is either an instance of * `R` or an instance of `BSTNOptKeyOrNode>`. * @returns The method is returning the `destNodeEnsured` object if both `srcNodeEnsured` and * `destNodeEnsured` are truthy. Otherwise, it returns `undefined`. */ protected override _swapProperties( - srcNode: R | BSTNOptKeyOrNode>, - destNode: R | BSTNOptKeyOrNode> + srcNode: BSTNOptKeyOrNode>, + destNode: BSTNOptKeyOrNode> ): AVLTreeNode | undefined { const srcNodeEnsured = this.ensureNode(srcNode); const destNodeEnsured = this.ensureNode(destNode); @@ -450,10 +487,10 @@ export class AVLTree> | R} node - The `node` parameter can be of type `R` or + * @param {BTNRep>} node - The `node` parameter can be of type `R` or * `BTNRep>`. */ - protected _balancePath(node: BTNRep> | R): void { + protected _balancePath(node: BTNRep>): 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-indexed-tree.ts b/src/data-structures/binary-tree/binary-indexed-tree.ts index ce722c8..aaa6694 100644 --- a/src/data-structures/binary-tree/binary-indexed-tree.ts +++ b/src/data-structures/binary-tree/binary-indexed-tree.ts @@ -7,6 +7,9 @@ */ import { getMSB } from '../../utils'; +/** + * + */ export class BinaryIndexedTree { protected readonly _freq: number; protected readonly _max: number; diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index a175e84..892d434 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -36,6 +36,14 @@ import { DFSOperation, Range } from '../../common'; * @template BinaryTreeNode - The type of the family relationship in the binary tree. */ export class BinaryTreeNode { + /** + * 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 + * for the key-value pair. + * @param {V} [value] - The `value` parameter in the constructor is optional, meaning it does not + * have to be provided when creating an instance of the class. If a `value` is not provided, it will + * default to `undefined`. + */ constructor(key: K, value?: V) { this.key = key; this.value = value; @@ -129,16 +137,14 @@ export class BinaryTree implements IBinaryTree { - iterationType: IterationType = 'ITERATIVE'; - /** - * The constructor initializes a binary tree with optional options and adds keys, nodes, entries, or - * raw data if provided. - * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor - * is an iterable that can contain elements of type `BTNRep>` or `R`. It is - * initialized with an empty array `[]` by default. - * @param [options] - The `options` parameter in the constructor is an object that can contain the - * following properties: + * 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 + * 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> = [], @@ -156,6 +162,8 @@ export class BinaryTree. */ createNode(key: K, value?: V): BinaryTreeNode { - return new BinaryTreeNode(key, this._isMapMode ? undefined : value) as BinaryTreeNode; + return new BinaryTreeNode(key, this._isMapMode ? undefined : value); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function creates a binary tree with the specified options. * @param [options] - The `options` parameter in the `createTree` function is an optional parameter * that allows you to provide partial configuration options for creating a binary tree. It is of type @@ -231,7 +242,7 @@ export class BinaryTree> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @param {BTNRep>} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `ensureNode` function can be of type `BTNRep>` or `R`. It * is used to determine whether the input is a key, node, entry, or raw data. The * @param {IterationType} iterationType - The `iterationType` parameter in the `ensureNode` function @@ -241,48 +252,49 @@ export class BinaryTree> | R, + keyNodeOrEntry: BTNRep>, iterationType: IterationType = this.iterationType ): OptNodeOrNull> { - if (keyNodeEntryOrRaw === null) return null; - if (keyNodeEntryOrRaw === undefined) return; - if (keyNodeEntryOrRaw === this._NIL) return; - if (this.isNode(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw; + if (keyNodeOrEntry === null) return null; + if (keyNodeOrEntry === undefined) return; + if (keyNodeOrEntry === this._NIL) return; - if (this.isEntry(keyNodeEntryOrRaw)) { - const key = keyNodeEntryOrRaw[0]; + if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry; + + if (this.isEntry(keyNodeOrEntry)) { + const key = keyNodeOrEntry[0]; if (key === null) return null; if (key === undefined) return; return this.getNode(key, this._root, iterationType); } - if (this._toEntryFn) { - const [key] = this._toEntryFn(keyNodeEntryOrRaw as R); - if (this.isKey(key)) return this.getNode(key); - } - - if (this.isKey(keyNodeEntryOrRaw)) return this.getNode(keyNodeEntryOrRaw, this._root, iterationType); - return; + return this.getNode(keyNodeOrEntry, this._root, iterationType); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function isNode checks if the input is an instance of BinaryTreeNode. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be either a key, a node, an entry, or raw data. The function is + * @param {BTNRep>} 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. - * @returns The function `isNode` is checking if the input `keyNodeEntryOrRaw` is an instance of + * @returns The function `isNode` is checking if the input `keyNodeOrEntry` is an instance of * `BinaryTreeNode`. If it is, the function returns `true`, indicating that the input is a node. If * it is not an instance of `BinaryTreeNode`, the function returns `false`, indicating that the input * is not a node. */ - isNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BinaryTreeNode { - return keyNodeEntryOrRaw instanceof BinaryTreeNode; + isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BinaryTreeNode { + return keyNodeOrEntry instanceof BinaryTreeNode; } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function `isRaw` checks if the input parameter is of type `R` by verifying if it is an object. - * @param {BTNRep> | R} keyNodeEntryOrRaw - BTNRep> | R + * @param {BTNRep> | R} keyNodeEntryOrRaw - BTNRep> * @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`. @@ -292,95 +304,122 @@ export class BinaryTree> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @param {BTNRep>} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `isRealNode` function can be of type `BTNRep>` or `R`. * The function checks if the input parameter is a `BinaryTreeNode` type by verifying if it is not equal - * @returns The function `isRealNode` is checking if the input `keyNodeEntryOrRaw` is a valid + * @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(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BinaryTreeNode { - if (keyNodeEntryOrRaw === this._NIL || keyNodeEntryOrRaw === null || keyNodeEntryOrRaw === undefined) return false; - return this.isNode(keyNodeEntryOrRaw); + isRealNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BinaryTreeNode { + if (keyNodeOrEntry === this._NIL || keyNodeOrEntry === null || keyNodeOrEntry === undefined) return false; + return this.isNode(keyNodeOrEntry); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function checks if a given input is a valid node or null. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` in the `isRealNodeOrNull` function can be of type `BTNRep>} 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 - * `keyNodeEntryOrRaw` is either `null` or a real node, and returns `true` if it is a node or + * `keyNodeOrEntry` is either `null` or a real node, and returns `true` if it is a node or * `null`, and `false` otherwise. */ - isRealNodeOrNull( - keyNodeEntryOrRaw: BTNRep> | R - ): keyNodeEntryOrRaw is BinaryTreeNode | null { - return keyNodeEntryOrRaw === null || this.isRealNode(keyNodeEntryOrRaw); + isRealNodeOrNull(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BinaryTreeNode | null { + return keyNodeOrEntry === null || this.isRealNode(keyNodeOrEntry); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function isNIL checks if a given key, node, entry, or raw value is equal to the _NIL value. - * @param {BTNRep> | R} keyNodeEntryOrRaw - BTNRep> | R - * @returns The function is checking if the `keyNodeEntryOrRaw` parameter is equal to the `_NIL` + * @param {BTNRep>} 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(keyNodeEntryOrRaw: BTNRep> | R): boolean { - return keyNodeEntryOrRaw === this._NIL; - } - - isRange( - keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate> | Range - ): keyNodeEntryRawOrPredicate is Range { - return keyNodeEntryRawOrPredicate instanceof Range; + isNIL(keyNodeOrEntry: BTNRep>): boolean { + return keyNodeOrEntry === this._NIL; } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function `isRange` checks if the input parameter is an instance of the `Range` class. + * @param {BTNRep> | NodePredicate> | Range} + * keyNodeEntryOrPredicate - The `keyNodeEntryOrPredicate` parameter in the `isRange` function can be + * of type `BTNRep>`, `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`, + * indicating that the parameter is a `Range`. If it is not an instance of `Range`, the function + * will return `false`. + */ + isRange( + keyNodeEntryOrPredicate: BTNRep> | NodePredicate> | Range + ): keyNodeEntryOrPredicate is Range { + return keyNodeEntryOrPredicate instanceof Range; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function determines whether a given key, node, entry, or raw data is a leaf node in a binary * tree. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `BTNRep>` or `R`. It represents a + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `BTNRep>` or `R`. It represents a * key, node, entry, or raw data in a binary tree structure. The function `isLeaf` checks whether the * provided * @returns The function `isLeaf` returns a boolean value indicating whether the input - * `keyNodeEntryOrRaw` is a leaf node in a binary tree. + * `keyNodeOrEntry` is a leaf node in a binary tree. */ - isLeaf(keyNodeEntryOrRaw: BTNRep> | R): boolean { - keyNodeEntryOrRaw = this.ensureNode(keyNodeEntryOrRaw); - if (keyNodeEntryOrRaw === undefined) return false; - if (keyNodeEntryOrRaw === null) return true; - return !this.isRealNode(keyNodeEntryOrRaw.left) && !this.isRealNode(keyNodeEntryOrRaw.right); + isLeaf(keyNodeOrEntry: BTNRep>): boolean { + keyNodeOrEntry = this.ensureNode(keyNodeOrEntry); + if (keyNodeOrEntry === undefined) return false; + if (keyNodeOrEntry === null) return true; + return !this.isRealNode(keyNodeOrEntry.left) && !this.isRealNode(keyNodeOrEntry.right); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function `isEntry` checks if the input is a BTNEntry object by verifying if it is an array * with a length of 2. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @param {BTNRep>} keyNodeOrEntry - The `keyNodeOrEntry` * parameter in the `isEntry` function can be of type `BTNRep>` or type `R`. - * The function checks if the provided `keyNodeEntryOrRaw` is of type `BTN - * @returns The `isEntry` function is checking if the `keyNodeEntryOrRaw` parameter is an array + * 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(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BTNEntry { - return Array.isArray(keyNodeEntryOrRaw) && keyNodeEntryOrRaw.length === 2; + isEntry(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BTNEntry { + return Array.isArray(keyNodeOrEntry) && keyNodeOrEntry.length === 2; } /** * Time Complexity O(1) * Space Complexity O(1) * - * The function `isKey` checks if a given key is comparable. + * The function `isValidKey` checks if a given key is comparable. * @param {any} key - The `key` parameter is of type `any`, which means it can be any data type in * TypeScript. - * @returns The function `isKey` is checking if the `key` parameter is `null` or if it is comparable. + * @returns The function `isValidKey` is checking if the `key` parameter is `null` or if it is comparable. * If the `key` is `null`, the function returns `true`. Otherwise, it returns the result of the * `isComparable` function, which is not provided in the code snippet. */ - isKey(key: any): key is K { + isValidKey(key: any): key is K { if (key === null) return true; return isComparable(key); } @@ -391,8 +430,8 @@ export class BinaryTree> | R} keyNodeEntryOrRaw - The `add` method you provided - * seems to be for adding a new node to a binary tree structure. The `keyNodeEntryOrRaw` + * @param {BTNRep>} 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 * with the key that you want to add to the binary tree. When adding a key-value pair to the binary @@ -402,8 +441,8 @@ export class BinaryTree> | R, value?: V): boolean { - const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + add(keyNodeOrEntry: BTNRep>, value?: V): boolean { + const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (newNode === undefined) return false; // If the tree is empty, directly set the new node as the root node @@ -460,7 +499,7 @@ export class BinaryTree, TREE>` + * @param anotherTree - BinaryTree */ - merge(anotherTree: BinaryTree>) { + merge(anotherTree: BinaryTree) { this.addMany(anotherTree, []); } @@ -542,7 +581,7 @@ export class BinaryTree> | R} keyNodeEntryOrRaw + * @param {BTNRep>} 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 @@ -551,11 +590,11 @@ export class BinaryTree> | R): BinaryTreeDeleteResult>[] { + delete(keyNodeOrEntry: BTNRep>): BinaryTreeDeleteResult>[] { const deletedResult: BinaryTreeDeleteResult>[] = []; if (!this._root) return deletedResult; - const curr = this.getNode(keyNodeEntryOrRaw); + const curr = this.getNode(keyNodeOrEntry); if (!curr) return deletedResult; const parent: BinaryTreeNode | undefined = curr?.parent; @@ -602,15 +641,15 @@ export class BinaryTree> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The - * `keyNodeEntryRawOrPredicate` parameter in the `search` function can accept three types of values: + * @param {BTNRep> | 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> | R} startNode - The `startNode` parameter in the `search` function is + * @param {BTNRep>} 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 @@ -621,17 +660,17 @@ export class BinaryTree>>( - keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, + keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, onlyOne = false, callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { - if (keyNodeEntryRawOrPredicate === undefined) return []; - if (keyNodeEntryRawOrPredicate === null) return []; + if (keyNodeEntryOrPredicate === undefined) return []; + if (keyNodeEntryOrPredicate === null) return []; startNode = this.ensureNode(startNode); if (!startNode) return []; - const predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate); + const predicate = this._ensurePredicate(keyNodeEntryOrPredicate); const ans: ReturnType[] = []; @@ -671,12 +710,12 @@ export class BinaryTree> | R | NodePredicate>} keyNodeEntryRawOrPredicate + * @param {BTNRep> | 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 - * `keyNodeEntryRawOrPredicate` parameter. If `onlyOne` is set to `true`, the function will - * @param {BTNRep> | R} startNode - The `startNode` parameter in the + * `keyNodeEntryOrPredicate` parameter. If `onlyOne` is set to `true`, the function will + * @param {BTNRep>} 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 @@ -687,24 +726,24 @@ export class BinaryTree> | R | NodePredicate>, + keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, onlyOne = false, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): BinaryTreeNode[] { - return this.search(keyNodeEntryRawOrPredicate, onlyOne, node => node, startNode, iterationType); + return this.search(keyNodeEntryOrPredicate, onlyOne, node => node, startNode, iterationType); } /** * Time Complexity: O(n) - * Space Complexity: O(log n). + * Space Complexity: O(log n) * * The `getNode` function retrieves a node based on the provided key, node, entry, raw data, or * predicate. - * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate - * - The `keyNodeEntryRawOrPredicate` parameter in the `getNode` function can accept a key, + * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate + * - The `keyNodeEntryOrPredicate` parameter in the `getNode` function can accept a key, * node, entry, raw data, or a predicate function. - * @param {BTNRep> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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. @@ -716,11 +755,11 @@ export class BinaryTree> | R | NodePredicate>, - startNode: BTNRep> | R = this._root, + keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): OptNodeOrNull> { - return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType)[0] ?? null; + return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType)[0]; } /** @@ -729,10 +768,10 @@ export class BinaryTree> | R | NodePredicate>} keyNodeEntryRawOrPredicate - * - The `keyNodeEntryRawOrPredicate` parameter in the `get` method can accept one of the + * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate + * - The `keyNodeEntryOrPredicate` parameter in the `get` method can accept one of the * following types: - * @param {BTNRep> | R} startNode - The `startNode` parameter in the `get` + * @param {BTNRep>} 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`). @@ -746,16 +785,16 @@ export class BinaryTree> | R, - startNode: BTNRep> | R = this._root, + keyNodeEntryOrPredicate: BTNRep>, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): V | undefined { if (this._isMapMode) { - const key = this._extractKey(keyNodeEntryRawOrPredicate); + const key = this._extractKey(keyNodeEntryOrPredicate); if (key === null || key === undefined) return; return this._store.get(key); } - return this.getNode(keyNodeEntryRawOrPredicate, startNode, iterationType)?.value; + return this.getNode(keyNodeEntryOrPredicate, startNode, iterationType)?.value; } /** @@ -764,10 +803,10 @@ export class BinaryTree> | R | NodePredicate>} keyNodeEntryRawOrPredicate - * - The `keyNodeEntryRawOrPredicate` parameter in the `override has` method can accept one of + * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate + * - The `keyNodeEntryOrPredicate` parameter in the `override has` method can accept one of * the following types: - * @param {BTNRep> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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 @@ -780,18 +819,18 @@ export class BinaryTree> | R | NodePredicate>, - startNode: BTNRep> | R = this._root, + keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): boolean { - return this.search(keyNodeEntryRawOrPredicate, true, node => node, startNode, iterationType).length > 0; + return this.search(keyNodeEntryOrPredicate, true, node => node, startNode, iterationType).length > 0; } /** * Time Complexity: O(1) * Space Complexity: O(1) * - * The `clear` function resets the root node and size of a data structure to empty. + * The clear function removes nodes and values in map mode. */ clear() { this._clearNodes(); @@ -817,7 +856,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter is the starting + * @param {BTNRep>} 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 @@ -826,17 +865,17 @@ export class BinaryTree> | R = this._root): boolean { + isPerfectlyBalanced(startNode: BTNRep> = this._root): boolean { return this.getMinHeight(startNode) + 1 >= this.getHeight(startNode); } /** * Time Complexity: O(n) - * Space Complexity: O(1) + * Space Complexity: O(log n) * * The function `isBST` in TypeScript checks if a binary search tree is valid using either recursive * or iterative methods. - * @param {BTNRep> | R} startNode - The `startNode` parameter in the `isBST` + * @param {BTNRep>} 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 @@ -849,7 +888,7 @@ export class BinaryTree> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): boolean { // TODO there is a bug @@ -894,13 +933,13 @@ export class BinaryTree> | R} dist - The `dist` parameter in the `getDepth` + * @param {BTNRep>} dist - The `dist` parameter in the `getDepth` * function represents the node or entry in a binary tree map, or a reference to a node in the tree. * It is the target node for which you want to calculate the depth from the `startNode` node. - * @param {BTNRep> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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 @@ -909,8 +948,8 @@ export class BinaryTree> | R, - startNode: BTNRep> | R = this._root + dist: BTNRep>, + startNode: BTNRep> = this._root ): number { let distEnsured = this.ensureNode(dist); const beginRootEnsured = this.ensureNode(startNode); @@ -927,11 +966,11 @@ export class BinaryTree> | R} startNode - The `startNode` parameter is the starting + * @param {BTNRep>} 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. @@ -943,7 +982,7 @@ export class BinaryTree> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): number { startNode = this.ensureNode(startNode); @@ -981,7 +1020,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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 @@ -994,7 +1033,7 @@ export class BinaryTree> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): number { startNode = this.ensureNode(startNode); @@ -1049,7 +1088,7 @@ export class BinaryTree> | R} beginNode - The `beginNode` parameter in the + * @param {BTNRep>} 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 @@ -1060,7 +1099,7 @@ export class BinaryTree>>>( - beginNode: BTNRep> | R, + beginNode: BTNRep>, callback: C = this._DEFAULT_NODE_CALLBACK as C, isReverse = false ): ReturnType[] { @@ -1080,14 +1119,14 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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 @@ -1101,7 +1140,7 @@ export class BinaryTree>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); @@ -1129,7 +1168,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> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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 @@ -1151,7 +1190,7 @@ export class BinaryTree>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType { if (this.isNIL(startNode)) return callback(undefined); @@ -1178,7 +1217,7 @@ export class BinaryTree>>( callback?: C, pattern?: DFSOrderPattern, - startNode?: BTNRep> | R, + startNode?: BTNRep>, iterationType?: IterationType ): ReturnType[]; dfs | null>>( callback?: C, pattern?: DFSOrderPattern, - startNode?: BTNRep> | R, + startNode?: BTNRep>, iterationType?: IterationType, includeNull?: boolean ): ReturnType[]; @@ -1259,7 +1298,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the `dfs` + * @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 @@ -1276,7 +1315,7 @@ export class BinaryTree>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { @@ -1287,14 +1326,14 @@ export class BinaryTree>>( callback?: C, - startNode?: BTNRep> | R, + startNode?: BTNRep>, iterationType?: IterationType, includeNull?: false ): ReturnType[]; bfs | null>>( callback?: C, - startNode?: BTNRep> | R, + startNode?: BTNRep>, iterationType?: IterationType, includeNull?: true ): ReturnType[]; @@ -1308,7 +1347,7 @@ export class BinaryTree` or `null`. - * @param {BTNRep> | R} startNode - The `startNode` parameter in the `bfs` + * @param {BTNRep>} 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 @@ -1324,7 +1363,7 @@ export class BinaryTree | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[] { @@ -1386,7 +1425,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the `leaves` + * @param {BTNRep>} 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 @@ -1398,7 +1437,7 @@ export class BinaryTree | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { startNode = this.ensureNode(startNode); @@ -1435,14 +1474,14 @@ export class BinaryTree>>( callback?: C, - startNode?: BTNRep> | R, + startNode?: BTNRep>, iterationType?: IterationType, includeNull?: false ): ReturnType[][]; listLevels | null>>( callback?: C, - startNode?: BTNRep> | R, + startNode?: BTNRep>, iterationType?: IterationType, includeNull?: true ): ReturnType[][]; @@ -1456,7 +1495,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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. @@ -1473,7 +1512,7 @@ export class BinaryTree | null>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType, includeNull = false ): ReturnType[][] { @@ -1530,7 +1569,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the `morris` + * @param {BTNRep>} 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 @@ -1541,7 +1580,7 @@ export class BinaryTree>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> | R = this._root + startNode: BTNRep> = this._root ): ReturnType[] { startNode = this.ensureNode(startNode); if (!startNode) return []; @@ -1722,7 +1761,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} 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 @@ -1735,7 +1774,7 @@ export class BinaryTree> | R = this._root, + startNode: BTNRep> = this._root, options?: BinaryTreePrintOptions ): string { const opts = { isShowUndefined: false, isShowNull: true, isShowRedBlackNIL: false, ...options }; @@ -1770,20 +1809,23 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the + * @param {BTNRep>} startNode - The `startNode` parameter in the * `override print` method is used to specify the starting point for printing the binary tree. It can * be either a key, a node, an entry, or the root of the tree. If no specific starting point is * provided, the default value is set to */ - override print(options?: BinaryTreePrintOptions, startNode: BTNRep> | R = this._root) { + override print(options?: BinaryTreePrintOptions, startNode: BTNRep> = this._root) { console.log(this.toVisual(startNode, options)); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object * or returns null. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The - * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeEntryOrRaw`, which + * @param {BTNRep>} keyNodeOrEntry - The + * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeOrEntry`, which * can be of type `BTNRep>` or `R`. This parameter represents either a key, a * node, an entry * @param {V} [value] - The `value` parameter in the `keyValueNodeEntryRawToNodeAndValue` function is @@ -1791,35 +1833,27 @@ export class BinaryTree>`) based on the input parameters provided. The function checks the type of the - * input parameter (`keyNodeEntryOrRaw`) and processes it accordingly to return a node or null + * input parameter (`keyNodeOrEntry`) and processes it accordingly to return a node or null * value. */ - protected _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep> | R, + protected _keyValueNodeOrEntryToNodeAndValue( + keyNodeOrEntry: BTNRep>, value?: V ): [OptNodeOrNull>, V | undefined] { - if (keyNodeEntryOrRaw === undefined) return [undefined, undefined]; - if (keyNodeEntryOrRaw === null) return [null, undefined]; + if (keyNodeOrEntry === undefined) return [undefined, undefined]; + if (keyNodeOrEntry === null) return [null, undefined]; - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; + if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value]; - if (this.isEntry(keyNodeEntryOrRaw)) { - const [key, entryValue] = keyNodeEntryOrRaw; + if (this.isEntry(keyNodeOrEntry)) { + const [key, entryValue] = keyNodeOrEntry; if (key === undefined) return [undefined, undefined]; else if (key === null) return [null, undefined]; const finalValue = value ?? entryValue; return [this.createNode(key, finalValue), finalValue]; } - if (this.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value]; - - return [undefined, undefined]; + return [this.createNode(keyNodeOrEntry, value), value]; } /** @@ -1834,7 +1868,7 @@ export class BinaryTree> | R} startNode - The `startNode` parameter in the `_dfs` + * @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 @@ -1867,7 +1901,7 @@ export class BinaryTree>>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType, includeNull = false, shouldVisitLeft: (node: OptNodeOrNull>) => boolean = node => !!node, @@ -2095,10 +2129,10 @@ export class BinaryTree> | R} srcNode - The `srcNode` parameter in the + * @param {BTNRep>} srcNode - The `srcNode` parameter in the * `_swapProperties` method can be either a BTNRep object containing key and value * properties, or it can be of type R. - * @param {BTNRep> | R} destNode - The `destNode` parameter in the + * @param {BTNRep>} 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 * both `srcNode @@ -2106,8 +2140,8 @@ export class BinaryTree> | R, - destNode: BTNRep> | R + srcNode: BTNRep>, + destNode: BTNRep> ): BinaryTreeNode | undefined { srcNode = this.ensureNode(srcNode); destNode = this.ensureNode(destNode); @@ -2183,36 +2217,29 @@ export class BinaryTree> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The + * @param {BTNRep> | NodePredicate>} keyNodeEntryOrPredicate - The * `_ensurePredicate` method in the provided code snippet is responsible for ensuring that the input - * parameter `keyNodeEntryRawOrPredicate` is transformed into a valid predicate function that can be + * 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( - keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate> + keyNodeEntryOrPredicate: BTNRep> | NodePredicate> ): NodePredicate> { - if (keyNodeEntryRawOrPredicate === null || keyNodeEntryRawOrPredicate === undefined) + if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined) return (node: BinaryTreeNode) => (node ? false : false); - if (this._isPredicate(keyNodeEntryRawOrPredicate)) return keyNodeEntryRawOrPredicate; + if (this._isPredicate(keyNodeEntryOrPredicate)) return keyNodeEntryOrPredicate; - if (this.isRealNode(keyNodeEntryRawOrPredicate)) - return (node: BinaryTreeNode) => node === keyNodeEntryRawOrPredicate; + if (this.isRealNode(keyNodeEntryOrPredicate)) + return (node: BinaryTreeNode) => node === keyNodeEntryOrPredicate; - if (this.isEntry(keyNodeEntryRawOrPredicate)) { - const [key] = keyNodeEntryRawOrPredicate; + if (this.isEntry(keyNodeEntryOrPredicate)) { + const [key] = keyNodeEntryOrPredicate; return (node: BinaryTreeNode) => node.key === key; } - if (this.isKey(keyNodeEntryRawOrPredicate)) - return (node: BinaryTreeNode) => node.key === keyNodeEntryRawOrPredicate; - - if (this._toEntryFn) { - const [key] = this._toEntryFn(keyNodeEntryRawOrPredicate); - return (node: BinaryTreeNode) => node.key === key; - } - return (node: BinaryTreeNode) => node.key === keyNodeEntryRawOrPredicate; + return (node: BinaryTreeNode) => node.key === keyNodeEntryOrPredicate; } /** @@ -2237,30 +2264,22 @@ export class BinaryTree> | R} keyNodeEntryOrRaw - The `_extractKey` method you provided is a - * TypeScript method that takes in a parameter `keyNodeEntryOrRaw` of type `BTNRep> | R`, + * @param {BTNRep>} keyNodeOrEntry - The `_extractKey` method you provided is a + * TypeScript method that takes in a parameter `keyNodeOrEntry` of type `BTNRep>`, * where `BTNRep` is a generic type with keys `K`, `V`, and `BinaryTreeNode`, and ` - * @returns The `_extractKey` method returns the key value extracted from the `keyNodeEntryOrRaw` + * @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(keyNodeEntryOrRaw: BTNRep> | R): K | null | undefined { - if (keyNodeEntryOrRaw === null) return null; - if (keyNodeEntryOrRaw === undefined) return; - if (keyNodeEntryOrRaw === this._NIL) return; - if (this.isNode(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw.key; + protected _extractKey(keyNodeOrEntry: BTNRep>): K | null | undefined { + if (keyNodeOrEntry === null) return null; + if (keyNodeOrEntry === undefined) return; + if (keyNodeOrEntry === this._NIL) return; + if (this.isNode(keyNodeOrEntry)) return keyNodeOrEntry.key; - if (this.isEntry(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw[0]; + if (this.isEntry(keyNodeOrEntry)) return keyNodeOrEntry[0]; - if (this.isRaw(keyNodeEntryOrRaw)) { - if (this._toEntryFn) { - const [key] = this._toEntryFn(keyNodeEntryOrRaw); - return key; - } - return; - } - - return keyNodeEntryOrRaw; + return keyNodeOrEntry; } /** diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 823127e..43fd0ec 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -27,6 +27,15 @@ import { isComparable } from '../../utils'; import { Range } from '../../common'; export class BSTNode extends BinaryTreeNode { + /** + * 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 + * within a data structure. It serves as a reference or identifier for accessing or manipulating the + * associated value. + * @param {V} [value] - The `value` parameter in the constructor is optional, meaning it does not + * have to be provided when creating an instance of the class. If a value is not provided, it will + * default to `undefined`. + */ constructor(key: K, value?: V) { super(key, value); } @@ -130,14 +139,15 @@ export class BST implements IBinaryTree { /** - * This is the constructor function for a Binary Search Tree class in TypeScript. - * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an - * iterable that can contain either keys, nodes, entries, or raw elements. These elements will be - * added to the binary search tree during the construction of the object. - * @param [options] - An optional object that contains additional options for the Binary Search Tree. - * It can include a comparator function that defines the order of the elements in the tree. + * 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 + * 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>> = [], options?: BSTOptions) { + constructor(keysNodesEntriesOrRaws: Iterable> | R> = [], options?: BSTOptions) { super([], options); if (options) { @@ -151,21 +161,12 @@ export class BST protected override _root?: BSTNode = undefined; - /** - * The function returns the root node of a tree structure. - * @returns The `_root` property of the object, which is of type `BSTNode` or `undefined`. - */ override get root(): OptNode> { return this._root; } protected _isReverse: boolean = false; - /** - * The above function is a getter method in TypeScript that returns the value of the private property - * `_isReverse`. - * @returns The `isReverse` property of the object, which is a boolean value. - */ get isReverse(): boolean { return this._isReverse; } @@ -190,26 +191,20 @@ export class BST return 0; }; - /** - * The function returns the value of the _comparator property. - * @returns The `_comparator` property is being returned. - */ get comparator() { return this._comparator; } protected _specifyComparable?: (key: K) => Comparable; - /** - * This function returns the value of the `_specifyComparable` property. - * @returns The method `specifyComparable()` is being returned, which is a getter method for the - * `_specifyComparable` property. - */ get specifyComparable() { return this._specifyComparable; } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function creates a new BSTNode with the given key and value and returns it. * @param {K} key - The key parameter is of type K, which represents the type of the key for the node * being created. @@ -218,10 +213,13 @@ export class BST * @returns The method is returning a new instance of the BSTNode class, casted as the BSTNode type. */ override createNode(key: K, value?: V): BSTNode { - return new BSTNode(key, this._isMapMode ? undefined : value) as BSTNode; + return new BSTNode(key, this._isMapMode ? undefined : value); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function creates a new binary search tree with the specified options. * @param [options] - The `options` parameter is an optional object that allows you to customize the * behavior of the `createTree` method. It accepts a partial `BSTOptions` object, which has the @@ -245,8 +243,8 @@ export class BST * * The function ensures the existence of a node in a data structure and returns it, or undefined if * it doesn't exist. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept a value of type `R`, which represents the key, node, + * @param {BTNRep>} 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 * parameter that specifies the type of iteration to be used when ensuring a node. It has a default @@ -255,48 +253,54 @@ export class BST * not be ensured. */ override ensureNode( - keyNodeEntryOrRaw: BTNRep> | R, + keyNodeOrEntry: BTNRep>, iterationType: IterationType = this.iterationType ): OptNode> { - return super.ensureNode(keyNodeEntryOrRaw, iterationType) ?? undefined; + return super.ensureNode(keyNodeOrEntry, iterationType) ?? undefined; } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function checks if the input is an instance of the BSTNode class. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep>`. - * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `BSTNode` class. */ - override isNode(keyNodeEntryOrRaw: BTNRep> | R): keyNodeEntryOrRaw is BSTNode { - return keyNodeEntryOrRaw instanceof BSTNode; + override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is BSTNode { + return keyNodeOrEntry instanceof BSTNode; } /** - * The function "override isKey" checks if a key is comparable based on a given comparator. + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function "override isValidKey" checks if a key is comparable based on a given comparator. * @param {any} key - The `key` parameter is a value that will be checked to determine if it is of * type `K`. - * @returns The `override isKey(key: any): key is K` function is returning a boolean value based on + * @returns The `override isValidKey(key: any): key is K` function is returning a boolean value based on * the result of the `isComparable` function with the condition `this._compare !== * this._DEFAULT_COMPARATOR`. */ - override isKey(key: any): key is K { + override isValidKey(key: any): key is K { return isComparable(key, this._specifyComparable !== undefined); } /** * Time Complexity: O(log n) - * Space Complexity: O(1) + * 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> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep>`. + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can accept a value of type `R` or `BTNRep>`. * @param {V} [value] - The `value` parameter is an optional value that can be associated with the * key in the binary search tree. If provided, it will be stored in the node along with the key. * @returns a boolean value. */ - override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V): boolean { - const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + override add(keyNodeOrEntry: BTNRep>, value?: V): boolean { + const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (newNode === undefined) return false; if (this._root === undefined) { @@ -370,8 +374,9 @@ export class BST } if (!isBalanceAdd) { - for (const kve of keysNodesEntriesOrRaws) { + for (let kve of keysNodesEntriesOrRaws) { const value = valuesIterator?.next().value; + if (this.isRaw(kve)) kve = this._toEntryFn!(kve); inserted.push(this.add(kve, value)); } return inserted; @@ -393,19 +398,17 @@ export class BST sorted = realBTNExemplars.sort(({ key: a }, { key: b }) => { let keyA: K | undefined | null, keyB: K | undefined | null; - if (this.isEntry(a)) keyA = a[0]; + if (this.isRaw(a)) keyA = this._toEntryFn!(a)[0]; + else if (this.isEntry(a)) keyA = a[0]; else if (this.isRealNode(a)) keyA = a.key; - else if (this._toEntryFn) { - keyA = this._toEntryFn(a as R)[0]; - } else { + else { keyA = a as K; } - if (this.isEntry(b)) keyB = b[0]; + if (this.isRaw(b)) keyB = this._toEntryFn!(b)[0]; + else if (this.isEntry(b)) keyB = b[0]; else if (this.isRealNode(b)) keyB = b.key; - else if (this._toEntryFn) { - keyB = this._toEntryFn(b as R)[0]; - } else { + else { keyB = b as K; } @@ -419,7 +422,13 @@ export class BST if (arr.length === 0) return; const mid = Math.floor((arr.length - 1) / 2); - const { key, value, orgIndex } = arr[mid]; + let { key, value } = arr[mid]; + const { orgIndex } = arr[mid]; + if (this.isRaw(key)) { + const entry = this._toEntryFn!(key); + key = entry[0]; + value = entry[1] ?? value; + } inserted[orgIndex] = this.add(key, value); _dfs(arr.slice(0, mid)); _dfs(arr.slice(mid + 1)); @@ -434,7 +443,13 @@ export class BST const [l, r] = popped; if (l <= r) { const m = l + Math.floor((r - l) / 2); - const { key, value, orgIndex } = sorted[m]; + let { key, value } = sorted[m]; + const { orgIndex } = sorted[m]; + if (this.isRaw(key)) { + const entry = this._toEntryFn!(key); + key = entry[0]; + value = entry[1] ?? value; + } inserted[orgIndex] = this.add(key, value); stack.push([m + 1, r]); stack.push([l, m - 1]); @@ -458,8 +473,8 @@ export class BST * * The function `search` in TypeScript overrides the search behavior in a binary tree structure based * on specified criteria. - * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The - * `keyNodeEntryRawOrPredicate` parameter in the `override search` method can accept one of the + * @param {BTNRep> | 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 * search should stop after finding the first matching node. If `onlyOne` is set to `true`, the @@ -468,7 +483,7 @@ export class BST * 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 * argument and - * @param {BTNRep> | R} startNode - The `startNode` parameter in the `override search` + * @param {BTNRep>} 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 ` @@ -481,28 +496,28 @@ export class BST * collected in an array and returned as the output of the method. */ override search>>( - keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate> | Range, + keyNodeEntryOrPredicate: BTNRep> | NodePredicate> | Range, onlyOne = false, callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { - if (keyNodeEntryRawOrPredicate === undefined) return []; - if (keyNodeEntryRawOrPredicate === null) return []; + if (keyNodeEntryOrPredicate === undefined) return []; + if (keyNodeEntryOrPredicate === null) return []; startNode = this.ensureNode(startNode); if (!startNode) return []; let predicate: NodePredicate>; - const isRange = this.isRange(keyNodeEntryRawOrPredicate); + const isRange = this.isRange(keyNodeEntryOrPredicate); // Set predicate based on parameter type if (isRange) { - predicate = node => keyNodeEntryRawOrPredicate.isInRange(node.key, this._comparator); + predicate = node => keyNodeEntryOrPredicate.isInRange(node.key, this._comparator); } else { - predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate); + predicate = this._ensurePredicate(keyNodeEntryOrPredicate); } const isToLeftByRange = (cur: BSTNode) => { if (isRange) { - const range = keyNodeEntryRawOrPredicate; + 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); @@ -512,7 +527,7 @@ export class BST const isToRightByRange = (cur: BSTNode) => { if (isRange) { - const range = keyNodeEntryRawOrPredicate; + const range = keyNodeEntryOrPredicate; const rightS = this.isReverse ? range.low : range.high; const rightI = this.isReverse ? range.includeLow : range.includeLow; @@ -533,8 +548,8 @@ export class BST 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(keyNodeEntryRawOrPredicate)) { - const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate); + } else if (!this._isPredicate(keyNodeEntryOrPredicate)) { + const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate); if ( this.isRealNode(cur.left) && benchmarkKey !== null && @@ -567,8 +582,8 @@ export class BST 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(keyNodeEntryRawOrPredicate)) { - const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate); + } else if (!this._isPredicate(keyNodeEntryOrPredicate)) { + const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate); if ( this.isRealNode(cur.right) && benchmarkKey !== null && @@ -595,7 +610,7 @@ export class BST /** * Time Complexity: O(log n) - * Space Complexity: O(n) + * Space Complexity: O(k + log n) * * The `rangeSearch` function searches for nodes within a specified range in a binary search tree. * @param {Range | [K, K]} range - The `range` parameter in the `rangeSearch` function can be @@ -604,7 +619,7 @@ export class BST * 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 * data structure. - * @param {BTNRep> | R} startNode - The `startNode` parameter in the `rangeSearch` + * @param {BTNRep>} 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 @@ -617,7 +632,7 @@ export class BST rangeSearch>>( range: Range | [K, K], callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ) { const searchRange: Range = range instanceof Range ? range : new Range(range[0], range[1]); @@ -626,12 +641,12 @@ export class BST /** * Time Complexity: O(log n) - * Space Complexity: O(1) + * Space Complexity: O(log n) * - * This function retrieves a node based on a given keyNodeEntryRawOrPredicate within a binary search tree structure. - * @param {BTNRep> | R | NodePredicate>} keyNodeEntryRawOrPredicate - The `keyNodeEntryRawOrPredicate` + * 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 {R | BSTNOptKeyOrNode>} startNode - The `startNode` parameter in the `getNode` method + * @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 * node of the binary search tree. @@ -640,16 +655,16 @@ export class BST * `this.iterationType`, which means it will use the iteration type defined in the class instance if * no value is provided when calling the method. * @returns The `getNode` method is returning an optional binary search tree node (`OptNode>`). - * It is using the `getNodes` method to find the node based on the provided keyNodeEntryRawOrPredicate, beginning at + * It is using the `getNodes` method to find the node based on the provided keyNodeEntryOrPredicate, beginning at * the specified root node (`startNode`) and using the specified iteration type. The method then * returns the first node found or `undefined` if no node is found. */ override getNode( - keyNodeEntryRawOrPredicate: BTNRep> | R | NodePredicate>, - startNode: R | BSTNOptKeyOrNode> = this._root, + keyNodeEntryOrPredicate: BTNRep> | NodePredicate>, + startNode: BSTNOptKeyOrNode> = this._root, iterationType: IterationType = this.iterationType ): OptNode> { - return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined; + return this.getNodes(keyNodeEntryOrPredicate, true, startNode, iterationType)[0] ?? undefined; } /** @@ -664,7 +679,7 @@ export class BST * @param {DFSOrderPattern} [pattern=IN] - The "pattern" parameter in the code snippet refers to the * order in which the Depth-First Search (DFS) algorithm visits the nodes in a tree or graph. It can * take one of the following values: - * @param {BTNRep> | R} startNode - The `startNode` parameter is the starting + * @param {BTNRep>} 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 @@ -675,7 +690,7 @@ export class BST override dfs>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, pattern: DFSOrderPattern = 'IN', - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { return super.dfs(callback, pattern, startNode, iterationType); @@ -690,7 +705,7 @@ export class BST * @param {C} callback - The `callback` parameter is a function that will be called for each node * visited during the breadth-first search. It should take a single argument, which is the current * node being visited, and it can return a value of any type. - * @param {BTNRep> | R} startNode - The `startNode` parameter is the starting + * @param {BTNRep>} 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 @@ -700,7 +715,7 @@ export class BST */ override bfs>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { return super.bfs(callback, startNode, iterationType, false); @@ -715,7 +730,7 @@ export class BST * @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 * tree during the iteration process. - * @param {BTNRep> | R} startNode - The `startNode` parameter is the starting + * @param {BTNRep>} 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 @@ -726,7 +741,7 @@ export class BST */ override listLevels>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, - startNode: BTNRep> | R = this._root, + startNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[][] { return super.listLevels(callback, startNode, iterationType, false); @@ -744,7 +759,7 @@ export class BST * @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to * traverse nodes that are lesser, greater, or both than the `targetNode`. It accepts the values -1, * 0, or 1, where: - * @param {BTNRep> | R} targetNode - The `targetNode` parameter is the node in + * @param {BTNRep>} 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, @@ -756,7 +771,7 @@ export class BST lesserOrGreaterTraverse>>( callback: C = this._DEFAULT_NODE_CALLBACK as C, lesserOrGreater: CP = -1, - targetNode: BTNRep> | R = this._root, + targetNode: BTNRep> = this._root, iterationType: IterationType = this.iterationType ): ReturnType[] { const targetNodeEnsured = this.ensureNode(targetNode); @@ -900,6 +915,25 @@ export class BST return balanced; } + /** + * Time complexity: O(n) + * Space complexity: O(n) + * + * The `map` function in TypeScript overrides the default map behavior for a binary search tree by + * applying a callback function to each entry and creating a new tree with the results. + * @param callback - A function that will be called for each entry in the BST. It takes four + * arguments: the key, the value (which can be undefined), the index of the entry, and a reference to + * the BST itself. + * @param [options] - The `options` parameter in the `override map` method is of type `BSTOptions`. It is an optional parameter that allows you to specify additional options for the Binary + * Search Tree (BST) being created in the `map` method. These options could include configuration + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` method is used to specify + * the value of `this` that should be used when executing the `callback` function. It allows you to + * set the context or scope in which the callback function will be called. This can be useful when + * you want + * @returns The `map` method is returning a new Binary Search Tree (`BST`) instance with the entries + * transformed by the provided callback function. + */ override map( callback: EntryCallback, options?: BSTOptions, @@ -913,6 +947,14 @@ export class BST return newTree; } + /** + * Time complexity: O(n) + * Space complexity: O(n) + * + * The function `clone` overrides the default cloning behavior to create a deep copy of a tree + * structure. + * @returns The `cloned` object is being returned. + */ override clone() { const cloned = this.createTree(); this._clone(cloned); @@ -920,24 +962,30 @@ export class BST } /** + * Time Complexity: O(1) + * 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> | R} keyNodeEntryOrRaw - A variable that can be of + * @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 * element. * @param {V} [value] - The `value` parameter is an optional value of type `V`. It represents the * value associated with a key in a key-value pair. * @returns either a BSTNode object or undefined. */ - protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep> | R, + protected override _keyValueNodeOrEntryToNodeAndValue( + keyNodeOrEntry: BTNRep>, value?: V ): [OptNode>, V | undefined] { - const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + const [node, entryValue] = super._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (node === null) return [undefined, undefined]; return [node, value ?? entryValue]; } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function sets the root of a tree-like structure and updates the parent property of the new * root. * @param {OptNode>} v - v is a parameter of type BSTNode or undefined. @@ -949,6 +997,20 @@ export class BST this._root = v; } + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The _compare function compares two values using a specified comparator function and optionally + * reverses the result. + * @param {K} a - The parameter `a` is of type `K`, which is used as an input for comparison in the + * `_compare` method. + * @param {K} b - The parameter `b` in the `_compare` function is of type `K`. + * @returns The `_compare` method is returning the result of the ternary expression. If `_isReverse` + * is true, it returns the negation of the result of calling the `_comparator` function with + * arguments `a` and `b`. If `_isReverse` is false, it returns the result of calling the + * `_comparator` function with arguments `a` and `b`. + */ protected _compare(a: K, b: K) { return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b); } diff --git a/src/data-structures/binary-tree/index.ts b/src/data-structures/binary-tree/index.ts index 4afe9ea..3c172bd 100644 --- a/src/data-structures/binary-tree/index.ts +++ b/src/data-structures/binary-tree/index.ts @@ -6,3 +6,5 @@ export * from './avl-tree'; export * from './red-black-tree'; export * from './avl-tree-multi-map'; export * from './tree-multi-map'; +export * from './tree-counter'; +export * from './avl-tree-counter'; diff --git a/src/data-structures/binary-tree/red-black-tree.ts b/src/data-structures/binary-tree/red-black-tree.ts index a2fd0b3..ccd773e 100644 --- a/src/data-structures/binary-tree/red-black-tree.ts +++ b/src/data-structures/binary-tree/red-black-tree.ts @@ -13,15 +13,14 @@ import { IBinaryTree } from '../../interfaces'; export class RedBlackTreeNode extends BSTNode { /** - * The constructor function initializes a Red-Black Tree Node with a key, an optional value, and a - * color. - * @param {K} key - The key parameter is of type K and represents the key of the node in the - * Red-Black Tree. - * @param {V} [value] - The `value` parameter is an optional parameter that represents the value - * associated with the key in the Red-Black Tree Node. It is not required and can be omitted when - * creating a new instance of the Red-Black Tree Node. - * @param {RBTNColor} color - The `color` parameter is used to specify the color of the Red-Black - * Tree Node. It is an optional parameter with a default value of `'BLACK'`. + * 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 + * Red-Black Tree data structure. + * @param {V} [value] - The `value` parameter in the constructor is an optional parameter of type + * `V`. It represents the value associated with the key in the data structure being constructed. + * @param {RBTNColor} [color=BLACK] - The `color` parameter in the constructor is used to specify the + * color of the node in a Red-Black Tree. It has a default value of 'BLACK' if not provided + * explicitly. */ constructor(key: K, value?: V, color: RBTNColor = 'BLACK') { super(key, value); @@ -115,17 +114,18 @@ export class RedBlackTree { /** - * This is the constructor function for a Red-Black Tree data structure in TypeScript. - * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an - * iterable object that can contain either keys, nodes, entries, or raw elements. It is used to - * initialize the RBTree with the provided elements. - * @param [options] - The `options` parameter is an optional object that can be passed to the - * constructor. It is of type `RedBlackTreeOptions`. This object can contain various options for - * configuring the behavior of the Red-Black Tree. The specific properties and their meanings would - * depend on the implementation + * This TypeScript constructor initializes a Red-Black Tree with optional keys, nodes, entries, or + * raw data. + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an + * iterable that can contain either `BTNRep>` 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 + * RedBlackTree class. These options could include configuration settings, behavior customization, or + * any other parameters that are specific to */ constructor( - keysNodesEntriesOrRaws: Iterable>> = [], + keysNodesEntriesOrRaws: Iterable> | R> = [], options?: RedBlackTreeOptions ) { super([], options); @@ -139,15 +139,14 @@ export class RedBlackTree | undefined; - /** - * The function returns the root node of a tree or undefined if there is no root. - * @returns The root node of the tree structure, or undefined if there is no root node. - */ override get root(): RedBlackTreeNode | undefined { return this._root; } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function creates a new Red-Black Tree node with the specified key, value, and color. * @param {K} key - The key parameter represents the key value of the node being created. It is of * type K, which is a generic type that can be replaced with any specific type when using the @@ -162,10 +161,13 @@ export class RedBlackTree { - return new RedBlackTreeNode(key, this._isMapMode ? undefined : value, color) as RedBlackTreeNode; + return new RedBlackTreeNode(key, this._isMapMode ? undefined : value, color); } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function creates a new Red-Black Tree with the specified options. * @param [options] - The `options` parameter is an optional object that contains additional * configuration options for creating the Red-Black Tree. It has the following properties: @@ -186,15 +188,13 @@ export class RedBlackTree> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep>`. - * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is * an instance of the `RedBlackTreeNode` class. */ - override isNode( - keyNodeEntryOrRaw: BTNRep> | R - ): keyNodeEntryOrRaw is RedBlackTreeNode { - return keyNodeEntryOrRaw instanceof RedBlackTreeNode; + override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is RedBlackTreeNode { + return keyNodeOrEntry instanceof RedBlackTreeNode; } /** @@ -211,12 +211,12 @@ export class RedBlackTree> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep>`. + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can accept a value of type `R` or `BTNRep>`. * @param {V} [value] - The `value` parameter is an optional value that you want to associate with * the key in the data structure. It represents the value that you want to add or update in the data * structure. @@ -224,8 +224,8 @@ export class RedBlackTree> | R, value?: V): boolean { - const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + override add(keyNodeOrEntry: BTNRep>, value?: V): boolean { + const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value); if (!this.isRealNode(newNode)) return false; const insertStatus = this._insert(newNode); @@ -250,11 +250,11 @@ export class RedBlackTree> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw` + * @param {BTNRep>} 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,14 +263,14 @@ export class RedBlackTree> | R + keyNodeOrEntry: BTNRep> ): BinaryTreeDeleteResult>[] { - if (keyNodeEntryOrRaw === null) return []; + if (keyNodeOrEntry === null) return []; const results: BinaryTreeDeleteResult>[] = []; let nodeToDelete: OptNode>; - if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw); - else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw); + if (this._isPredicate(keyNodeOrEntry)) nodeToDelete = this.getNode(keyNodeOrEntry); + else nodeToDelete = this.isRealNode(keyNodeOrEntry) ? keyNodeOrEntry : this.getNode(keyNodeOrEntry); if (!nodeToDelete) { return results; @@ -361,6 +361,14 @@ export class RedBlackTree + * @license MIT License + */ +import type { + BinaryTreeDeleteResult, + BSTNOptKeyOrNode, + BTNRep, + EntryCallback, + IterationType, + OptNode, + OptNodeOrNull, + RBTNColor, + TreeCounterOptions +} from '../../types'; +import { IBinaryTree } from '../../interfaces'; +import { RedBlackTree, RedBlackTreeNode } from './red-black-tree'; + +export class TreeCounterNode extends RedBlackTreeNode { + /** + * The constructor function initializes a Red-Black Tree node with a key, value, count, and color. + * @param {K} key - The key parameter represents the key of the node in the Red-Black Tree. It is + * used to identify and locate the node within the tree. + * @param {V} [value] - The `value` parameter is an optional parameter that represents the value + * associated with the key in the Red-Black Tree node. It is not required and can be omitted when + * creating a new node. + * @param [count=1] - The `count` parameter represents the number of occurrences of a particular key + * in the Red-Black Tree. It is an optional parameter with a default value of 1. + * @param {RBTNColor} [color=BLACK] - The `color` parameter is used to specify the color of the node + * in a Red-Black Tree. It is optional and has a default value of `'BLACK'`. + */ + constructor(key: K, value?: V, count = 1, color: RBTNColor = 'BLACK') { + super(key, value, color); + this.count = count; + } + + override parent?: TreeCounterNode = undefined; + + override _left?: OptNodeOrNull> = undefined; + + override get left(): OptNodeOrNull> { + return this._left; + } + + override set left(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._left = v; + } + + override _right?: OptNodeOrNull> = undefined; + + override get right(): OptNodeOrNull> { + return this._right; + } + + override set right(v: OptNodeOrNull>) { + if (v) { + v.parent = this; + } + this._right = v; + } +} + +/** + * + */ +export class TreeCounter + extends RedBlackTree + implements IBinaryTree +{ + /** + * The constructor function initializes a TreeCounter object with optional initial data. + * @param keysNodesEntriesOrRaws - The parameter `keysNodesEntriesOrRaws` is an + * iterable that can contain keys, nodes, entries, or raw elements. It is used to initialize the + * TreeCounter with initial data. + * @param [options] - The `options` parameter is an optional object that can be used to customize the + * behavior of the `TreeCounter` constructor. It can include properties such as `compareKeys` and + * `compareValues`, which are functions used to compare keys and values respectively. + */ + constructor( + keysNodesEntriesOrRaws: Iterable> | R> = [], + options?: TreeCounterOptions + ) { + super([], options); + if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); + } + + protected _count = 0; + + // TODO the _count is not accurate after nodes count modified + /** + * The function calculates the sum of the count property of all nodes in a tree structure. + * @returns the sum of the count property of all nodes in the tree. + */ + get count(): number { + return this._count; + } + + /** + * Time Complexity: O(n) + * Space Complexity: O(1) + * + * The function calculates the sum of the count property of all nodes in a tree using depth-first + * search. + * @returns the sum of the count property of all nodes in the tree. + */ + getComputedCount(): number { + let sum = 0; + this.dfs(node => (sum += node.count)); + return sum; + } + + /** + * The function creates a new TreeCounterNode with the specified key, value, color, and count. + * @param {K} key - The key parameter represents the key of the node being created. It is of type K, + * which is a generic type representing the type of keys in the tree. + * @param {V} [value] - The `value` parameter is an optional parameter that represents the value + * associated with the key in the node. It is of type `V`, which can be any data type. + * @param {RBTNColor} [color=BLACK] - The color parameter is used to specify the color of the node in + * a Red-Black Tree. It can have two possible values: 'RED' or 'BLACK'. The default value is 'BLACK'. + * @param {number} [count] - The `count` parameter represents the number of occurrences of a key in + * the tree. It is an optional parameter and is used to keep track of the number of values associated + * with a key in the tree. + * @returns A new instance of the TreeCounterNode class, casted as TreeCounterNode. + */ + override createNode(key: K, value?: V, color: RBTNColor = 'BLACK', count?: number): TreeCounterNode { + return new TreeCounterNode(key, this._isMapMode ? undefined : value, count, color) as TreeCounterNode; + } + + /** + * The function creates a new instance of a TreeCounter with the specified options and returns it. + * @param [options] - The `options` parameter is an optional object that contains additional + * configuration options for creating the `TreeCounter`. It is of type `TreeCounterOptions`. + * @returns a new instance of the `TreeCounter` class, with the provided options merged with the + * existing `iterationType` property. The returned value is casted as `TREE`. + */ + override createTree(options?: TreeCounterOptions) { + return new TreeCounter([], { + iterationType: this.iterationType, + specifyComparable: this._specifyComparable, + isMapMode: this._isMapMode, + toEntryFn: this._toEntryFn, + ...options + }); + } + + /** + * The function checks if the input is an instance of the TreeCounterNode class. + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is + * an instance of the `TreeCounterNode` class. + */ + override isNode(keyNodeOrEntry: BTNRep>): keyNodeOrEntry is TreeCounterNode { + return keyNodeOrEntry instanceof TreeCounterNode; + } + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + * The function overrides the add method of a class and adds a new node to a data structure, updating + * the count and returning a boolean indicating success. + * @param {BTNRep>} 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. + * @param [count=1] - The `count` parameter represents the number of times the key-value pair should + * be added to the data structure. By default, it is set to 1, meaning that if no value is provided + * for `count`, the key-value pair will be added once. + * @returns The method is returning a boolean value. It returns true if the addition of the new node + * was successful, and false otherwise. + */ + override add(keyNodeOrEntry: BTNRep>, value?: V, count = 1): boolean { + const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value, count); + const orgCount = newNode?.count || 0; + const isSuccessAdded = super.add(newNode, newValue); + + if (isSuccessAdded) { + this._count += orgCount; + return true; + } else { + return false; + } + } + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + * The function `delete` in TypeScript overrides the deletion operation in a binary tree data + * structure, handling cases where nodes have children and maintaining balance in the tree. + * @param {BTNRep>} 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 + * boolean flag that determines whether to ignore the count of nodes when performing deletion. If + * `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If + * `ignoreCount` is `false + * @returns The `override delete` method returns an array of `BinaryTreeDeleteResult>` objects. + */ + override delete( + keyNodeOrEntry: BTNRep>, + ignoreCount = false + ): BinaryTreeDeleteResult>[] { + if (keyNodeOrEntry === null) return []; + + const results: BinaryTreeDeleteResult>[] = []; + + let nodeToDelete: OptNode>; + if (this._isPredicate(keyNodeOrEntry)) nodeToDelete = this.getNode(keyNodeOrEntry); + else nodeToDelete = this.isRealNode(keyNodeOrEntry) ? keyNodeOrEntry : this.getNode(keyNodeOrEntry); + + if (!nodeToDelete) { + return results; + } + + let originalColor = nodeToDelete.color; + let replacementNode: TreeCounterNode | undefined; + + if (!this.isRealNode(nodeToDelete.left)) { + if (nodeToDelete.right !== null) replacementNode = nodeToDelete.right; + if (ignoreCount || nodeToDelete.count <= 1) { + if (nodeToDelete.right !== null) { + this._transplant(nodeToDelete, nodeToDelete.right); + this._count -= nodeToDelete.count; + } + } else { + nodeToDelete.count--; + this._count--; + results.push({ deleted: nodeToDelete, needBalanced: undefined }); + return results; + } + } else if (!this.isRealNode(nodeToDelete.right)) { + replacementNode = nodeToDelete.left; + if (ignoreCount || nodeToDelete.count <= 1) { + this._transplant(nodeToDelete, nodeToDelete.left); + this._count -= nodeToDelete.count; + } else { + nodeToDelete.count--; + this._count--; + results.push({ deleted: nodeToDelete, needBalanced: undefined }); + return results; + } + } else { + const successor = this.getLeftMost(node => node, nodeToDelete.right); + if (successor) { + originalColor = successor.color; + if (successor.right !== null) replacementNode = successor.right; + + if (successor.parent === nodeToDelete) { + if (this.isRealNode(replacementNode)) { + replacementNode.parent = successor; + } + } else { + if (ignoreCount || nodeToDelete.count <= 1) { + if (successor.right !== null) { + this._transplant(successor, successor.right); + this._count -= nodeToDelete.count; + } + } else { + nodeToDelete.count--; + this._count--; + results.push({ deleted: nodeToDelete, needBalanced: undefined }); + return results; + } + successor.right = nodeToDelete.right; + if (this.isRealNode(successor.right)) { + successor.right.parent = successor; + } + } + if (ignoreCount || nodeToDelete.count <= 1) { + this._transplant(nodeToDelete, successor); + this._count -= nodeToDelete.count; + } else { + nodeToDelete.count--; + this._count--; + results.push({ deleted: nodeToDelete, needBalanced: undefined }); + return results; + } + successor.left = nodeToDelete.left; + if (this.isRealNode(successor.left)) { + successor.left.parent = successor; + } + successor.color = nodeToDelete.color; + } + } + this._size--; + + // If the original color was black, fix the tree + if (originalColor === 'BLACK') { + this._deleteFixup(replacementNode); + } + + results.push({ deleted: nodeToDelete, needBalanced: undefined }); + + return results; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The "clear" function overrides the parent class's "clear" function and also resets the count to + * zero. + */ + override clear() { + super.clear(); + this._count = 0; + } + + /** + * Time Complexity: O(n log n) + * Space Complexity: O(log n) + * + * The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search + * tree using either a recursive or iterative approach. + * @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that + * specifies the type of iteration to use when building the balanced binary search tree. It has a + * default value of `this.iterationType`, which means it will use the iteration type specified by the + * `iterationType` property of the current object. + * @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the + * balancing operation is successful, and `false` if there are no nodes to balance. + */ + override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean { + const sorted = this.dfs(node => node, 'IN'), + n = sorted.length; + if (sorted.length < 1) return false; + + this.clear(); + + if (iterationType === 'RECURSIVE') { + const buildBalanceBST = (l: number, r: number) => { + if (l > 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); + buildBalanceBST(l, m - 1); + buildBalanceBST(m + 1, r); + }; + + buildBalanceBST(0, n - 1); + return true; + } else { + const stack: [[number, number]] = [[0, n - 1]]; + while (stack.length > 0) { + const popped = stack.pop(); + if (popped) { + const [l, r] = popped; + if (l <= r) { + 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); + stack.push([m + 1, r]); + stack.push([l, m - 1]); + } + } + } + return true; + } + } + + /** + * Time complexity: O(n) + * Space complexity: O(n) + * + * The function overrides the clone method to create a deep copy of a tree object. + * @returns The `clone()` method is returning a cloned instance of the `TREE` object. + */ + override clone() { + const cloned = this.createTree(); + this.bfs(node => cloned.add(node.key, undefined, node.count)); + if (this._isMapMode) cloned._store = this._store; + return cloned; + } + + /** + * The `map` function in TypeScript overrides the default behavior to create a new TreeCounter with + * modified entries based on a provided callback. + * @param callback - The `callback` parameter is a function that will be called for each entry in the + * map. It takes four arguments: + * @param [options] - The `options` parameter in the `override map` function is of type + * `TreeCounterOptions`. This parameter allows you to provide additional configuration + * options when creating a new `TreeCounter` instance within the `map` function. These options could + * include things like + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) for the callback function when it is called within the `map` function. This + * @returns A new TreeCounter instance is being returned, which is populated with entries generated + * by the provided callback function. + */ + override map( + callback: EntryCallback, + options?: TreeCounterOptions, + thisArg?: any + ): TreeCounter { + const newTree = new TreeCounter([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + + /** + * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a + * node based on the input. + * @param {BTNRep>} keyNodeOrEntry - The parameter + * `keyNodeOrEntry` can be of type `R` or `BTNRep>`. + * @param {V} [value] - The `value` parameter is an optional value that represents the value + * associated with the key in the node. It is used when creating a new node or updating the value of + * an existing node. + * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of + * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. + * @returns either a TreeCounterNode object or undefined. + */ + protected override _keyValueNodeOrEntryToNodeAndValue( + keyNodeOrEntry: BTNRep>, + value?: V, + count = 1 + ): [TreeCounterNode | undefined, V | undefined] { + if (keyNodeOrEntry === undefined || keyNodeOrEntry === null) return [undefined, undefined]; + + if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value]; + + if (this.isEntry(keyNodeOrEntry)) { + const [key, entryValue] = keyNodeOrEntry; + if (key === undefined || key === null) return [undefined, undefined]; + const finalValue = value ?? entryValue; + return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; + } + + return [this.createNode(keyNodeOrEntry, value, 'BLACK', count), value]; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes + * in a binary search tree. + * @param {R | BSTNOptKeyOrNode>} srcNode - The `srcNode` parameter represents the source node + * that will be swapped with the `destNode`. It can be either an instance of the `R` class or an + * instance of the `BSTNOptKeyOrNode>` class. + * @param {R | BSTNOptKeyOrNode>} destNode - The `destNode` parameter represents the destination + * node where the properties will be swapped with the source node. + * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. + * If either `srcNode` or `destNode` is undefined, it returns undefined. + */ + protected override _swapProperties( + srcNode: BSTNOptKeyOrNode>, + destNode: BSTNOptKeyOrNode> + ): TreeCounterNode | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); + if (srcNode && destNode) { + const { key, value, count, color } = destNode; + const tempNode = this.createNode(key, value, color, count); + if (tempNode) { + tempNode.color = color; + + destNode.key = srcNode.key; + if (!this._isMapMode) destNode.value = srcNode.value; + destNode.count = srcNode.count; + destNode.color = srcNode.color; + + srcNode.key = tempNode.key; + if (!this._isMapMode) srcNode.value = tempNode.value; + srcNode.count = tempNode.count; + srcNode.color = tempNode.color; + } + + return destNode; + } + return undefined; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The function replaces an old node with a new node and updates the count property of the new node. + * @param {TreeCounterNode} oldNode - The `oldNode` parameter is the node that you want to replace in the data + * structure. + * @param {TreeCounterNode} newNode - The `newNode` parameter is an instance of the `TreeCounterNode` class. + * @returns The method is returning the result of calling the `_replaceNode` method from the + * superclass, which is of type `TreeCounterNode`. + */ + protected override _replaceNode( + oldNode: TreeCounterNode, + newNode: TreeCounterNode + ): TreeCounterNode { + newNode.count = oldNode.count + newNode.count; + return super._replaceNode(oldNode, newNode); + } +} diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index cd8ff05..a73cbef 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -5,36 +5,21 @@ * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ -import type { - BinaryTreeDeleteResult, - BSTNOptKeyOrNode, - BTNRep, - EntryCallback, - IterationType, - OptNode, - OptNodeOrNull, - RBTNColor, - TreeMultiMapOptions -} from '../../types'; -import { IBinaryTree } from '../../interfaces'; +import type { BTNOptKeyOrNull, BTNRep, OptNodeOrNull, TreeMultiMapOptions } from '../../types'; import { RedBlackTree, RedBlackTreeNode } from './red-black-tree'; -export class TreeMultiMapNode extends RedBlackTreeNode { +export class TreeMultiMapNode extends RedBlackTreeNode { /** - * The constructor function initializes a Red-Black Tree node with a key, value, count, and color. - * @param {K} key - The key parameter represents the key of the node in the Red-Black Tree. It is - * used to identify and locate the node within the tree. - * @param {V} [value] - The `value` parameter is an optional parameter that represents the value - * associated with the key in the Red-Black Tree node. It is not required and can be omitted when - * creating a new node. - * @param [count=1] - The `count` parameter represents the number of occurrences of a particular key - * in the Red-Black Tree. It is an optional parameter with a default value of 1. - * @param {RBTNColor} [color=BLACK] - The `color` parameter is used to specify the color of the node - * in a Red-Black Tree. It is optional and has a default value of `'BLACK'`. + * This TypeScript constructor initializes an object with a key of type K and an array of values of + * type V. + * @param {K} key - The `key` parameter is typically used to store a unique identifier or key for the + * data being stored in the data structure. It helps in quickly accessing or retrieving the + * associated value in the data structure. + * @param {V[]} value - The `value` parameter in the constructor represents an array of values of + * type `V`. */ - constructor(key: K, value?: V, count = 1, color: RBTNColor = 'BLACK') { - super(key, value, color); - this.count = count; + constructor(key: K, value: V[]) { + super(key, value); } override parent?: TreeMultiMapNode = undefined; @@ -66,446 +51,180 @@ export class TreeMultiMapNode extends RedBlackTreeNode { } } -export class TreeMultiMap - extends RedBlackTree - implements IBinaryTree -{ +/** + * + * @example + * // Find elements in a range + * const tmm = new TreeMultiMap([10, 5, 15, 3, 7, 12, 18]); + * console.log(tmm.search(new Range(5, 10))); // [5, 10, 7] + * console.log(tmm.search(new Range(4, 12))); // [5, 10, 12, 7] + * console.log(tmm.search(new Range(15, 20))); // [15, 18] + */ +export class TreeMultiMap extends RedBlackTree< + K, + V[], + R, + MK, + MV, + MR +> { /** - * The constructor function initializes a TreeMultiMap object with optional initial data. - * @param keysNodesEntriesOrRaws - The parameter `keysNodesEntriesOrRaws` is an - * iterable that can contain keys, nodes, entries, or raw elements. It is used to initialize the - * TreeMultiMap with initial data. - * @param [options] - The `options` parameter is an optional object that can be used to customize the - * behavior of the `TreeMultiMap` constructor. It can include properties such as `compareKeys` and - * `compareValues`, which are functions used to compare keys and values respectively. + * The constructor initializes an TreeMultiMap with the provided keys, nodes, entries, or raw data + * and options. + * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an + * iterable that can contain either key-value pairs represented as `BTNRep>` or raw data represented as `R`. This parameter is used to initialize + * the RedBlackTreeMulti + * @param [options] - The `options` parameter in the constructor is of type + * `TreeMultiMapOptions`. It is an optional parameter that allows you to specify + * additional options for configuring the TreeMultiMap instance. */ constructor( - keysNodesEntriesOrRaws: Iterable>> = [], - options?: TreeMultiMapOptions + keysNodesEntriesOrRaws: Iterable> | R> = [], + options?: TreeMultiMapOptions ) { - super([], options); - if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); - } - - protected _count = 0; - - // TODO the _count is not accurate after nodes count modified - /** - * The function calculates the sum of the count property of all nodes in a tree structure. - * @returns the sum of the count property of all nodes in the tree. - */ - get count(): number { - return this._count; + super([], { ...options, isMapMode: true }); + if (keysNodesEntriesOrRaws) { + this.addMany(keysNodesEntriesOrRaws); + } } /** - * Time Complexity: O(n) + * Time Complexity: O(1) * Space Complexity: O(1) * - * The function calculates the sum of the count property of all nodes in a tree using depth-first - * search. - * @returns the sum of the count property of all nodes in the tree. + * The `createTree` function in TypeScript overrides the default implementation to create a new + * TreeMultiMap with specified options. + * @param [options] - The `options` parameter in the `createTree` method is of type + * `TreeMultiMapOptions`. This parameter allows you to pass additional configuration + * options when creating a new `TreeMultiMap` instance. It includes properties such as + * `iterationType`, `specifyComparable + * @returns A new instance of `TreeMultiMap` is being returned, with an empty array as the initial + * data and the provided options merged with the existing properties of the current object. */ - getComputedCount(): number { - let sum = 0; - this.dfs(node => (sum += node.count)); - return sum; - } - - /** - * The function creates a new TreeMultiMapNode with the specified key, value, color, and count. - * @param {K} key - The key parameter represents the key of the node being created. It is of type K, - * which is a generic type representing the type of keys in the tree. - * @param {V} [value] - The `value` parameter is an optional parameter that represents the value - * associated with the key in the node. It is of type `V`, which can be any data type. - * @param {RBTNColor} [color=BLACK] - The color parameter is used to specify the color of the node in - * a Red-Black Tree. It can have two possible values: 'RED' or 'BLACK'. The default value is 'BLACK'. - * @param {number} [count] - The `count` parameter represents the number of occurrences of a key in - * the tree. It is an optional parameter and is used to keep track of the number of values associated - * with a key in the tree. - * @returns A new instance of the TreeMultiMapNode class, casted as TreeMultiMapNode. - */ - override createNode(key: K, value?: V, color: RBTNColor = 'BLACK', count?: number): TreeMultiMapNode { - return new TreeMultiMapNode(key, this._isMapMode ? undefined : value, count, color) as TreeMultiMapNode; - } - - /** - * The function creates a new instance of a TreeMultiMap with the specified options and returns it. - * @param [options] - The `options` parameter is an optional object that contains additional - * configuration options for creating the `TreeMultiMap`. It is of type `TreeMultiMapOptions`. - * @returns a new instance of the `TreeMultiMap` class, with the provided options merged with the - * existing `iterationType` property. The returned value is casted as `TREE`. - */ - override createTree(options?: TreeMultiMapOptions) { + override createTree(options?: TreeMultiMapOptions) { return new TreeMultiMap([], { iterationType: this.iterationType, - isMapMode: this._isMapMode, specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, + isReverse: this._isReverse, ...options }); } - /** - * The function checks if the input is an instance of the TreeMultiMapNode class. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep>`. - * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is - * an instance of the `TreeMultiMapNode` class. - */ - override isNode( - keyNodeEntryOrRaw: BTNRep> | R - ): keyNodeEntryOrRaw is TreeMultiMapNode { - return keyNodeEntryOrRaw instanceof TreeMultiMapNode; - } - - /** - * Time Complexity: O(log n) - * Space Complexity: O(1) - * - * The function overrides the add method of a class and adds a new node to a data structure, updating - * the count and returning a boolean indicating success. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The - * `keyNodeEntryOrRaw` parameter can accept one of the following types: - * @param {V} [value] - The `value` parameter represents the value associated with the key in the - * data structure. It is an optional parameter, so it can be omitted if not needed. - * @param [count=1] - The `count` parameter represents the number of times the key-value pair should - * be added to the data structure. By default, it is set to 1, meaning that if no value is provided - * for `count`, the key-value pair will be added once. - * @returns The method is returning a boolean value. It returns true if the addition of the new node - * was successful, and false otherwise. - */ - override add(keyNodeEntryOrRaw: BTNRep> | R, value?: V, count = 1): boolean { - const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count); - const orgCount = newNode?.count || 0; - const isSuccessAdded = super.add(newNode, newValue); - - if (isSuccessAdded) { - this._count += orgCount; - return true; - } else { - return false; - } - } - - /** - * Time Complexity: O(log n) - * Space Complexity: O(1) - * - * The function `delete` in TypeScript overrides the deletion operation in a binary tree data - * structure, handling cases where nodes have children and maintaining balance in the tree. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The `predicate` - * parameter in the `delete` method is used to specify the condition or key based on which a node - * should be deleted from the binary tree. It can be a key, a node, or an entry. - * @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a - * boolean flag that determines whether to ignore the count of nodes when performing deletion. If - * `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If - * `ignoreCount` is `false - * @returns The `override delete` method returns an array of `BinaryTreeDeleteResult>` objects. - */ - override delete( - keyNodeEntryOrRaw: BTNRep> | R, - ignoreCount = false - ): BinaryTreeDeleteResult>[] { - if (keyNodeEntryOrRaw === null) return []; - - const results: BinaryTreeDeleteResult>[] = []; - - let nodeToDelete: OptNode>; - if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw); - else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw); - - if (!nodeToDelete) { - return results; - } - - let originalColor = nodeToDelete.color; - let replacementNode: TreeMultiMapNode | undefined; - - if (!this.isRealNode(nodeToDelete.left)) { - if (nodeToDelete.right !== null) replacementNode = nodeToDelete.right; - if (ignoreCount || nodeToDelete.count <= 1) { - if (nodeToDelete.right !== null) { - this._transplant(nodeToDelete, nodeToDelete.right); - this._count -= nodeToDelete.count; - } - } else { - nodeToDelete.count--; - this._count--; - results.push({ deleted: nodeToDelete, needBalanced: undefined }); - return results; - } - } else if (!this.isRealNode(nodeToDelete.right)) { - replacementNode = nodeToDelete.left; - if (ignoreCount || nodeToDelete.count <= 1) { - this._transplant(nodeToDelete, nodeToDelete.left); - this._count -= nodeToDelete.count; - } else { - nodeToDelete.count--; - this._count--; - results.push({ deleted: nodeToDelete, needBalanced: undefined }); - return results; - } - } else { - const successor = this.getLeftMost(node => node, nodeToDelete.right); - if (successor) { - originalColor = successor.color; - if (successor.right !== null) replacementNode = successor.right; - - if (successor.parent === nodeToDelete) { - if (this.isRealNode(replacementNode)) { - replacementNode.parent = successor; - } - } else { - if (ignoreCount || nodeToDelete.count <= 1) { - if (successor.right !== null) { - this._transplant(successor, successor.right); - this._count -= nodeToDelete.count; - } - } else { - nodeToDelete.count--; - this._count--; - results.push({ deleted: nodeToDelete, needBalanced: undefined }); - return results; - } - successor.right = nodeToDelete.right; - if (this.isRealNode(successor.right)) { - successor.right.parent = successor; - } - } - if (ignoreCount || nodeToDelete.count <= 1) { - this._transplant(nodeToDelete, successor); - this._count -= nodeToDelete.count; - } else { - nodeToDelete.count--; - this._count--; - results.push({ deleted: nodeToDelete, needBalanced: undefined }); - return results; - } - successor.left = nodeToDelete.left; - if (this.isRealNode(successor.left)) { - successor.left.parent = successor; - } - successor.color = nodeToDelete.color; - } - } - this._size--; - - // If the original color was black, fix the tree - if (originalColor === 'BLACK') { - this._deleteFixup(replacementNode); - } - - results.push({ deleted: nodeToDelete, needBalanced: undefined }); - - return results; - } - /** * Time Complexity: O(1) * Space Complexity: O(1) * - * The "clear" function overrides the parent class's "clear" function and also resets the count to - * zero. + * The function `createNode` overrides the method to create a new `TreeMultiMapNode` with a specified + * key and an empty array of values. + * @param {K} key - The `key` parameter in the `createNode` method represents the key of the node + * that will be created in the TreeMultiMap data structure. + * @returns A new instance of `TreeMultiMapNode` is being returned, with the specified key and + * an empty array as its value. */ - override clear() { - super.clear(); - this._count = 0; + override createNode(key: K): TreeMultiMapNode { + return new TreeMultiMapNode(key, []); } + override add(node: BTNRep>): boolean; + + override add(key: K, value: V): boolean; + /** - * Time Complexity: O(n log n) + * Time Complexity: O(log n) * Space Complexity: O(log n) * - * The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search - * tree using either a recursive or iterative approach. - * @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that - * specifies the type of iteration to use when building the balanced binary search tree. It has a - * default value of `this.iterationType`, which means it will use the iteration type specified by the - * `iterationType` property of the current object. - * @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the - * balancing operation is successful, and `false` if there are no nodes to balance. + * The function `add` in TypeScript overrides the superclass method to add key-value pairs to a + * TreeMultiMapNode, handling different input types and scenarios. + * @param {BTNRep> | K} 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 + * you want to add to the TreeMultiMap. If the key is already present in the map, the new value will + * be added to the existing list of values associated with that key. If the key is not present, + * @returns The `add` method is returning a boolean value, which indicates whether the operation was + * successful or not. */ - override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean { - const sorted = this.dfs(node => node, 'IN'), - n = sorted.length; - if (sorted.length < 1) return false; + override add(keyNodeOrEntry: BTNRep> | K, value?: V): boolean { + if (this.isRealNode(keyNodeOrEntry)) return super.add(keyNodeOrEntry); - this.clear(); + const _commonAdd = (key?: BTNOptKeyOrNull, values?: V[]) => { + if (key === undefined || key === null) return false; - if (iterationType === 'RECURSIVE') { - const buildBalanceBST = (l: number, r: number) => { - if (l > 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); - buildBalanceBST(l, m - 1); - buildBalanceBST(m + 1, r); - }; - - buildBalanceBST(0, n - 1); - return true; - } else { - const stack: [[number, number]] = [[0, n - 1]]; - while (stack.length > 0) { - const popped = stack.pop(); - if (popped) { - const [l, r] = popped; - if (l <= r) { - 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); - stack.push([m + 1, r]); - stack.push([l, m - 1]); - } - } + const existingValues = this.get(key); + if (existingValues !== undefined && values !== undefined) { + for (const value of values) existingValues.push(value); + return true; } - return true; + + const existingNode = this.getNode(key); + if (this.isRealNode(existingNode)) { + if (existingValues === undefined) { + super.add(key, values); + return true; + } + if (values !== undefined) { + for (const value of values) existingValues.push(value); + return true; + } else { + return false; + } + } else { + return super.add(key, values); + } + }; + + if (this.isEntry(keyNodeOrEntry)) { + const [key, values] = keyNodeOrEntry; + return _commonAdd(key, value !== undefined ? [value] : values); } + + return _commonAdd(keyNodeOrEntry, value !== undefined ? [value] : undefined); } /** - * Time complexity: O(n) - * Space complexity: O(n) + * Time Complexity: O(log n) + * Space Complexity: O(log n) * - * The function overrides the clone method to create a deep copy of a tree object. - * @returns The `clone()` method is returning a cloned instance of the `TREE` object. + * The function `deleteValue` removes a specific value from a key in a TreeMultiMap data structure + * and deletes the entire node if no values are left for that key. + * @param {BTNRep> | K} 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 + * value that you want to remove from the multi-map data structure associated with a particular key. + * The function checks if the value exists in the array of values associated with the key, and if + * found, removes it from the array. + * @returns The `deleteValue` function returns a boolean value - `true` if the specified `value` was + * successfully deleted from the values associated with the `keyNodeOrEntry`, and `false` otherwise. + */ + deleteValue(keyNodeOrEntry: BTNRep> | K, value: V): boolean { + const values = this.get(keyNodeOrEntry); + if (Array.isArray(values)) { + const index = values.indexOf(value); + if (index === -1) return false; + values.splice(index, 1); + + // If no values left, remove the entire node + if (values.length === 0) this.delete(keyNodeOrEntry); + + return true; + } + return false; + } + + /** + * Time Complexity: O(n) + * Space Complexity: O(n) + * + * The function `clone` overrides the default cloning behavior to create a deep copy of a tree + * structure. + * @returns The `cloned` object is being returned. */ override clone() { const cloned = this.createTree(); - this.bfs(node => cloned.add(node.key, undefined, node.count)); - if (this._isMapMode) cloned._store = this._store; + this._clone(cloned); return cloned; } - - /** - * The `map` function in TypeScript overrides the default behavior to create a new TreeMultiMap with - * modified entries based on a provided callback. - * @param callback - The `callback` parameter is a function that will be called for each entry in the - * map. It takes four arguments: - * @param [options] - The `options` parameter in the `override map` function is of type - * `TreeMultiMapOptions`. This parameter allows you to provide additional configuration - * options when creating a new `TreeMultiMap` instance within the `map` function. These options could - * include things like - * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify - * the value of `this` when executing the `callback` function. It allows you to set the context - * (value of `this`) for the callback function when it is called within the `map` function. This - * @returns A new TreeMultiMap instance is being returned, which is populated with entries generated - * by the provided callback function. - */ - override map( - callback: EntryCallback, - options?: TreeMultiMapOptions, - thisArg?: any - ): TreeMultiMap { - const newTree = new TreeMultiMap([], options); - let index = 0; - for (const [key, value] of this) { - newTree.add(callback.call(thisArg, key, value, index++, this)); - } - return newTree; - } - - /** - * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a - * node based on the input. - * @param {BTNRep> | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep>`. - * @param {V} [value] - The `value` parameter is an optional value that represents the value - * associated with the key in the node. It is used when creating a new node or updating the value of - * an existing node. - * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of - * times the key-value pair should be added to the data structure. If not provided, it defaults to 1. - * @returns either a TreeMultiMapNode object or undefined. - */ - protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep> | R, - value?: V, - count = 1 - ): [TreeMultiMapNode | undefined, V | undefined] { - if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; - - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - if (this.isEntry(keyNodeEntryOrRaw)) { - const [key, entryValue] = keyNodeEntryOrRaw; - if (key === undefined || key === null) return [undefined, undefined]; - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; - } - - if (this.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value]; - - return [undefined, undefined]; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes - * in a binary search tree. - * @param {R | BSTNOptKeyOrNode>} srcNode - The `srcNode` parameter represents the source node - * that will be swapped with the `destNode`. It can be either an instance of the `R` class or an - * instance of the `BSTNOptKeyOrNode>` class. - * @param {R | BSTNOptKeyOrNode>} destNode - The `destNode` parameter represents the destination - * node where the properties will be swapped with the source node. - * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. - * If either `srcNode` or `destNode` is undefined, it returns undefined. - */ - protected override _swapProperties( - srcNode: R | BSTNOptKeyOrNode>, - destNode: R | BSTNOptKeyOrNode> - ): TreeMultiMapNode | undefined { - srcNode = this.ensureNode(srcNode); - destNode = this.ensureNode(destNode); - if (srcNode && destNode) { - const { key, value, count, color } = destNode; - const tempNode = this.createNode(key, value, color, count); - if (tempNode) { - tempNode.color = color; - - destNode.key = srcNode.key; - if (!this._isMapMode) destNode.value = srcNode.value; - destNode.count = srcNode.count; - destNode.color = srcNode.color; - - srcNode.key = tempNode.key; - if (!this._isMapMode) srcNode.value = tempNode.value; - srcNode.count = tempNode.count; - srcNode.color = tempNode.color; - } - - return destNode; - } - return undefined; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function replaces an old node with a new node and updates the count property of the new node. - * @param {TreeMultiMapNode} oldNode - The `oldNode` parameter is the node that you want to replace in the data - * structure. - * @param {TreeMultiMapNode} newNode - The `newNode` parameter is an instance of the `TreeMultiMapNode` class. - * @returns The method is returning the result of calling the `_replaceNode` method from the - * superclass, which is of type `TreeMultiMapNode`. - */ - protected override _replaceNode( - oldNode: TreeMultiMapNode, - newNode: TreeMultiMapNode - ): TreeMultiMapNode { - newNode.count = oldNode.count + newNode.count; - return super._replaceNode(oldNode, newNode); - } } diff --git a/src/data-structures/graph/directed-graph.ts b/src/data-structures/graph/directed-graph.ts index 8648b90..77b1d1b 100644 --- a/src/data-structures/graph/directed-graph.ts +++ b/src/data-structures/graph/directed-graph.ts @@ -45,6 +45,9 @@ export class DirectedEdge extends AbstractEdge { } } +/** + * + */ export class DirectedGraph< V = any, E = any, diff --git a/src/data-structures/graph/map-graph.ts b/src/data-structures/graph/map-graph.ts index 370198a..75c5b70 100644 --- a/src/data-structures/graph/map-graph.ts +++ b/src/data-structures/graph/map-graph.ts @@ -40,6 +40,9 @@ export class MapEdge extends DirectedEdge { } } +/** + * + */ export class MapGraph< V = any, E = any, diff --git a/src/data-structures/graph/undirected-graph.ts b/src/data-structures/graph/undirected-graph.ts index 56ac8ef..3f1df38 100644 --- a/src/data-structures/graph/undirected-graph.ts +++ b/src/data-structures/graph/undirected-graph.ts @@ -42,6 +42,9 @@ export class UndirectedEdge extends AbstractEdge { } } +/** + * + */ export class UndirectedGraph< V = any, E = any, diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index 6197f46..6fc4e84 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -59,6 +59,9 @@ export class SinglyLinkedListNode { } } +/** + * + */ export class SinglyLinkedList extends IterableElementBase> { constructor( elements: Iterable | Iterable | Iterable> = [], diff --git a/src/data-structures/linked-list/skip-linked-list.ts b/src/data-structures/linked-list/skip-linked-list.ts index 4b0ef57..470b4de 100644 --- a/src/data-structures/linked-list/skip-linked-list.ts +++ b/src/data-structures/linked-list/skip-linked-list.ts @@ -19,6 +19,9 @@ export class SkipListNode { } } +/** + * + */ export class SkipList { /** * The constructor function initializes a SkipLinkedList object with optional options and elements. diff --git a/src/data-structures/matrix/matrix.ts b/src/data-structures/matrix/matrix.ts index 73a3ca4..719b161 100644 --- a/src/data-structures/matrix/matrix.ts +++ b/src/data-structures/matrix/matrix.ts @@ -7,6 +7,9 @@ */ import type { MatrixOptions } from '../../types'; +/** + * + */ export class Matrix { /** * The constructor function initializes a matrix object with the provided data and options, or with diff --git a/src/data-structures/matrix/navigator.ts b/src/data-structures/matrix/navigator.ts index 3d1646f..246d9af 100644 --- a/src/data-structures/matrix/navigator.ts +++ b/src/data-structures/matrix/navigator.ts @@ -25,6 +25,9 @@ export class Character { } } +/** + * + */ export class Navigator { onMove: (cur: [number, number]) => void; protected readonly _matrix: T[][]; diff --git a/src/data-structures/priority-queue/max-priority-queue.ts b/src/data-structures/priority-queue/max-priority-queue.ts index 2464a1b..226a185 100644 --- a/src/data-structures/priority-queue/max-priority-queue.ts +++ b/src/data-structures/priority-queue/max-priority-queue.ts @@ -8,6 +8,9 @@ import type { Comparator, ElementCallback, PriorityQueueOptions } from '../../types'; import { PriorityQueue } from './priority-queue'; +/** + * + */ export class MaxPriorityQueue extends PriorityQueue { /** * The constructor initializes a PriorityQueue with optional elements and options, including a diff --git a/src/data-structures/priority-queue/min-priority-queue.ts b/src/data-structures/priority-queue/min-priority-queue.ts index a7f3cf0..7f2bbfe 100644 --- a/src/data-structures/priority-queue/min-priority-queue.ts +++ b/src/data-structures/priority-queue/min-priority-queue.ts @@ -8,6 +8,9 @@ import type { Comparator, ElementCallback, PriorityQueueOptions } from '../../types'; import { PriorityQueue } from './priority-queue'; +/** + * + */ export class MinPriorityQueue extends PriorityQueue { /** * The constructor initializes a PriorityQueue with optional elements and options, including a diff --git a/src/data-structures/trie/trie.ts b/src/data-structures/trie/trie.ts index f177764..73517b9 100644 --- a/src/data-structures/trie/trie.ts +++ b/src/data-structures/trie/trie.ts @@ -8,10 +8,6 @@ import type { ElementCallback, TrieOptions } from '../../types'; import { IterableElementBase } from '../base'; -/** - * TrieNode represents a node in the Trie data structure. It holds a character key, a map of children nodes, - * and a flag indicating whether it's the end of a word. - */ export class TrieNode { constructor(key: string) { this._key = key; diff --git a/src/types/data-structures/binary-tree/avl-tree-counter.ts b/src/types/data-structures/binary-tree/avl-tree-counter.ts new file mode 100644 index 0000000..84ab8a2 --- /dev/null +++ b/src/types/data-structures/binary-tree/avl-tree-counter.ts @@ -0,0 +1,3 @@ +import { AVLTreeOptions } from './avl-tree'; + +export type AVLTreeCounterOptions = AVLTreeOptions & {}; diff --git a/src/types/data-structures/binary-tree/avl-tree-multi-map.ts b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts index 267cb37..ad9dfac 100644 --- a/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts @@ -1,3 +1,3 @@ import type { AVLTreeOptions } from './avl-tree'; -export type AVLTreeMultiMapOptions = AVLTreeOptions & {} +export type AVLTreeMultiMapOptions = Omit, 'isMapMode'> & {} diff --git a/src/types/data-structures/binary-tree/index.ts b/src/types/data-structures/binary-tree/index.ts index 7d8dcb1..163d9cc 100644 --- a/src/types/data-structures/binary-tree/index.ts +++ b/src/types/data-structures/binary-tree/index.ts @@ -5,3 +5,5 @@ export * from './segment-tree'; export * from './avl-tree-multi-map'; export * from './rb-tree'; export * from './tree-multi-map'; +export * from './tree-counter'; +export * from './avl-tree-counter'; diff --git a/src/types/data-structures/binary-tree/tree-counter.ts b/src/types/data-structures/binary-tree/tree-counter.ts new file mode 100644 index 0000000..6a00f92 --- /dev/null +++ b/src/types/data-structures/binary-tree/tree-counter.ts @@ -0,0 +1,3 @@ +import type { RedBlackTreeOptions } from './rb-tree'; + +export type TreeCounterOptions = RedBlackTreeOptions & {}; diff --git a/src/types/data-structures/binary-tree/tree-multi-map.ts b/src/types/data-structures/binary-tree/tree-multi-map.ts index e03e50a..cd0b283 100644 --- a/src/types/data-structures/binary-tree/tree-multi-map.ts +++ b/src/types/data-structures/binary-tree/tree-multi-map.ts @@ -1,3 +1,3 @@ import type { RedBlackTreeOptions } from './rb-tree'; -export type TreeMultiMapOptions = RedBlackTreeOptions & {} +export type TreeMultiMapOptions = Omit, 'isMapMode'> & {} diff --git a/test/integration/compile.js b/test/integration/compile.js index bc7a6d5..8e940b6 100644 --- a/test/integration/compile.js +++ b/test/integration/compile.js @@ -1,6 +1,7 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var data_structure_typed_1 = require('data-structure-typed'); + var orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8]; var orgStrArr = ['trie', 'trial', 'trick', 'trip', 'tree', 'trend', 'triangle', 'track', 'trace', 'transmit']; var entries = [ diff --git a/test/performance/data-structures/binary-tree/avl-tree.test.ts b/test/performance/data-structures/binary-tree/avl-tree.test.ts index 3648f38..6a6624e 100644 --- a/test/performance/data-structures/binary-tree/avl-tree.test.ts +++ b/test/performance/data-structures/binary-tree/avl-tree.test.ts @@ -8,14 +8,14 @@ const { HUNDRED_THOUSAND } = magnitude; const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); suite - .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { - avlTree.clear(); - for (let i = 0; i < randomArray.length; i++) avlTree.add(i); - }) .add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => { avlTree.clear(); for (let i = 0; i < randomArray.length; i++) avlTree.add(randomArray[i]); }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { + avlTree.clear(); + for (let i = 0; i < randomArray.length; i++) avlTree.add(i); + }) .add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => { for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]); }) diff --git a/test/performance/data-structures/binary-tree/rb-tree.test.ts b/test/performance/data-structures/binary-tree/rb-tree.test.ts index f46021c..6432c78 100644 --- a/test/performance/data-structures/binary-tree/rb-tree.test.ts +++ b/test/performance/data-structures/binary-tree/rb-tree.test.ts @@ -7,19 +7,20 @@ import { isCompetitor } from '../../../config'; const suite = new Benchmark.Suite(); const rbTree = new RedBlackTree(); const rbTreeNodeMode = new RedBlackTree([], { isMapMode: false }); + const { HUNDRED_THOUSAND } = magnitude; const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true); const cOrderedMap = new OrderedMap(); suite - .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { - rbTree.clear(); - for (let i = 0; i < randomArray.length; i++) rbTree.add(i); - }) .add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => { rbTree.clear(); for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]); }) + .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => { + rbTree.clear(); + for (let i = 0; i < randomArray.length; i++) rbTree.add(i); + }) .add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => { for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]); }) diff --git a/test/performance/data-structures/comparison/comparison.test.ts b/test/performance/data-structures/comparison/comparison.test.ts index c4236dd..2de66fa 100644 --- a/test/performance/data-structures/comparison/comparison.test.ts +++ b/test/performance/data-structures/comparison/comparison.test.ts @@ -1,6 +1,6 @@ -import { PriorityQueue as MJSPriorityQueue } from '../../../../dist/cjs'; +import { PriorityQueue as MJSPriorityQueue } from '../../../../dist/esm'; import { PriorityQueue as SRCPriorityQueue } from '../../../../src'; -import { PriorityQueue as CJSPriorityQueue } from '../../../../dist/mjs'; +import { PriorityQueue as CJSPriorityQueue } from '../../../../dist/cjs'; import { Deque as CDeque, HashMap as CHashMap, diff --git a/test/performance/reportor.ts b/test/performance/reportor.ts index f67ceab..ebf63ff 100644 --- a/test/performance/reportor.ts +++ b/test/performance/reportor.ts @@ -49,7 +49,7 @@ if (args.length > 0) { testFiles = allFiles.filter(file => args.every(word => file.includes(word))); isIndividual = true; console.log( - `${testFiles.map(file => coloredLabeled('Matched', file)).join(` + `${testFiles.map(file => coloredLabeled('Found', file)).join(` `)}` ); } else { @@ -229,9 +229,11 @@ const sortedPerformanceTests = ( return 0; }); -console.log(`${GREEN} Found tests (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`); console.log( - `${GREEN} Running tests (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}` + `${GREEN} Matched Suites (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}` +); +console.log( + `${GREEN} Running Suites (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}` ); sortedPerformanceTests.forEach(item => { 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 new file mode 100644 index 0000000..2cab97f --- /dev/null +++ b/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts @@ -0,0 +1,877 @@ +import { AVLTreeCounter, AVLTreeCounterNode, AVLTreeNode, BinaryTreeNode, BSTNode } from '../../../../src'; +import { isDebugTest } from '../../../config'; + +const isDebug = isDebugTest; + +describe('AVLTreeCounter count', () => { + let avlCounter: AVLTreeCounter; + beforeEach(() => { + avlCounter = new AVLTreeCounter(); + }); + it('Should added isolated node count ', () => { + avlCounter.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new AVLTreeCounterNode(3, 33, 10); + avlCounter.add(newNode); + expect(avlCounter.count).toBe(15); + }); + + it('Should count', () => { + avlCounter.addMany([ + [1, 1], + [2, 2], + [3, 3] + ]); + avlCounter.add([2, 2], undefined, 10); + avlCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 1); + avlCounter.delete(2); + expect(avlCounter.count).toBe(12); + expect(avlCounter.getComputedCount()).toBe(16); + }); +}); + +describe('AVLTreeCounter operations test1', () => { + it('should perform various operations on a AVLTreeCounter with numeric values1', () => { + const avlCounter = new AVLTreeCounter(); + + expect(avlCounter instanceof AVLTreeCounter); + avlCounter.add([11, 11]); + avlCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + avlCounter.addMany(idAndValues); + expect(avlCounter.root instanceof AVLTreeCounterNode); + + if (avlCounter.root) expect(avlCounter.root.key == 11); + + expect(avlCounter.size).toBe(16); + expect(avlCounter.count).toBe(18); + + expect(avlCounter.has(6)); + + expect(avlCounter.getHeight(6)).toBe(4); + expect(avlCounter.getDepth(6)).toBe(0); + const nodeId10 = avlCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = avlCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = avlCounter.getNodes(node => node.count === 1); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = avlCounter.getNodes(node => node.count === 2); + expect(nodesByCount2.length).toBe(2); + const leftMost = avlCounter.getLeftMost(); + expect(leftMost).toBe(1); + + const node15 = avlCounter.getNode(15); + const minNodeBySpecificNode = node15 && avlCounter.getLeftMost(node => node, node15); + expect(minNodeBySpecificNode?.key).toBe(15); + + let subTreeSum = 0; + if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + expect(subTreeSum).toBe(31); + let lesserSum = 0; + avlCounter.lesserOrGreaterTraverse((node: AVLTreeCounterNode) => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); + + expect(node15 instanceof AVLTreeCounterNode); + if (node15 instanceof AVLTreeCounterNode) { + const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', 15); + expect(subTreeAdd); + } + const node11 = avlCounter.getNode(11); + expect(node11 instanceof AVLTreeCounterNode); + if (node11 instanceof AVLTreeCounterNode) { + const allGreaterNodesAdded = avlCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); + expect(allGreaterNodesAdded); + } + + const dfsInorderNodes = avlCounter.dfs(node => node, 'IN'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + expect(avlCounter.isPerfectlyBalanced()).toBe(false); + + avlCounter.perfectlyBalance(); + + expect(avlCounter.isPerfectlyBalanced()).toBe(true); + expect(avlCounter.isAVLBalanced()).toBe(true); + + const bfsNodesAfterBalanced = avlCounter.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = avlCounter.delete(11, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight(15)).toBe(1); + + const removed1 = avlCounter.delete(1, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight()).toBe(4); + + const removed4 = avlCounter.delete(4, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(4); + + const removed10 = avlCounter.delete(10, true); + expect(removed10 instanceof Array); + expect(removed10[0]); + expect(removed10[0].deleted); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight()).toBe(3); + + const removed15 = avlCounter.delete(15, true); + expect(removed15 instanceof Array); + expect(removed15[0]); + expect(removed15[0].deleted); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed5 = avlCounter.delete(5, true); + expect(removed5 instanceof Array); + expect(removed5[0]); + expect(removed5[0].deleted); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed13 = avlCounter.delete(13, true); + expect(removed13 instanceof Array); + expect(removed13[0]); + expect(removed13[0].deleted); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed3 = avlCounter.delete(3, true); + expect(removed3 instanceof Array); + expect(removed3[0]); + expect(removed3[0].deleted); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed8 = avlCounter.delete(8, true); + expect(removed8 instanceof Array); + expect(removed8[0]); + expect(removed8[0].deleted); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed6 = avlCounter.delete(6, true); + expect(removed6 instanceof Array); + expect(removed6[0]); + expect(removed6[0].deleted); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(avlCounter.delete(6, true).length).toBe(0); + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight()).toBe(2); + + const removed7 = avlCounter.delete(7, true); + expect(removed7 instanceof Array); + expect(removed7[0]); + expect(removed7[0].deleted); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(2); + + const removed9 = avlCounter.delete(9, true); + expect(removed9 instanceof Array); + expect(removed9[0]); + expect(removed9[0].deleted); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(2); + + const removed14 = avlCounter.delete(14, true); + expect(removed14 instanceof Array); + expect(removed14[0]); + expect(removed14[0].deleted); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(1); + + expect(avlCounter.isAVLBalanced()).toBe(true); + + const bfsIDs = avlCounter.bfs(node => node.key); + + expect(bfsIDs[0]).toBe(12); + expect(bfsIDs[1]).toBe(2); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes = avlCounter.bfs(node => node); + + expect(bfsNodes[0].key).toBe(12); + expect(bfsNodes[1].key).toBe(2); + expect(bfsNodes[2].key).toBe(16); + + expect(avlCounter.count).toBe(8); + }); + + it('should perform various operations on a AVLTreeCounter with object values', () => { + const objAvlCounter = new AVLTreeCounter(); + expect(objAvlCounter).toBeInstanceOf(AVLTreeCounter); + objAvlCounter.add([11, { key: 11, keyA: 11 }]); + objAvlCounter.add([3, { key: 3, keyA: 3 }]); + const values: [number, { key: number; keyA: number }][] = [ + [15, { key: 15, keyA: 15 }], + [1, { key: 1, keyA: 1 }], + [8, { key: 8, keyA: 8 }], + [13, { key: 13, keyA: 13 }], + [16, { key: 16, keyA: 16 }], + [2, { key: 2, keyA: 2 }], + [6, { key: 6, keyA: 6 }], + [9, { key: 9, keyA: 9 }], + [12, { key: 12, keyA: 12 }], + [14, { key: 14, keyA: 14 }], + [4, { key: 4, keyA: 4 }], + [7, { key: 7, keyA: 7 }], + [10, { key: 10, keyA: 10 }], + [5, { key: 5, keyA: 5 }] + ]; + + objAvlCounter.addMany(values); + + expect(objAvlCounter.root).toBeInstanceOf(AVLTreeCounterNode); + + if (objAvlCounter.root) expect(objAvlCounter.root.key).toBe(6); + + expect(objAvlCounter.count).toBe(16); + + expect(objAvlCounter.has(6)).toBe(true); + }); +}); + +describe('AVLTreeCounter operations test recursively1', () => { + it('should perform various operations on a AVLTreeCounter with numeric values1', () => { + const avlCounter = new AVLTreeCounter([], { + iterationType: 'RECURSIVE' + }); + + expect(avlCounter instanceof AVLTreeCounter); + avlCounter.add([11, 11]); + avlCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + avlCounter.addMany(idAndValues); + expect(avlCounter.root).toBeInstanceOf(AVLTreeCounterNode); + + if (avlCounter.root) expect(avlCounter.root.key).toBe(6); + + expect(avlCounter.size).toBe(16); + expect(avlCounter.count).toBe(18); + + expect(avlCounter.has(6)); + + expect(avlCounter.getHeight(6)).toBe(4); + expect(avlCounter.getDepth(6)).toBe(0); + const nodeId10 = avlCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = avlCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = avlCounter.getNodes(node => node.count === 1); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = avlCounter.getNodes(node => node.count === 2); + expect(nodesByCount2.length).toBe(2); + const leftMost = avlCounter.getLeftMost(); + expect(leftMost).toBe(1); + + const node15 = avlCounter.getNode(15); + const minNodeBySpecificNode = node15 && avlCounter.getLeftMost(node => node, node15); + expect(minNodeBySpecificNode?.key).toBe(15); + + let subTreeSum = 0; + if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + expect(subTreeSum).toBe(31); + let lesserSum = 0; + avlCounter.lesserOrGreaterTraverse((node: AVLTreeCounterNode) => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); + + expect(node15 instanceof AVLTreeCounterNode); + if (node15 instanceof AVLTreeCounterNode) { + const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', 15); + expect(subTreeAdd); + } + const node11 = avlCounter.getNode(11); + expect(node11 instanceof AVLTreeCounterNode); + if (node11 instanceof AVLTreeCounterNode) { + const allGreaterNodesAdded = avlCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); + expect(allGreaterNodesAdded); + } + + const dfsInorderNodes = avlCounter.dfs(node => node, 'IN'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + expect(avlCounter.isPerfectlyBalanced()).toBe(true); + + avlCounter.perfectlyBalance(); + + expect(avlCounter.isPerfectlyBalanced()).toBe(true); + expect(avlCounter.isAVLBalanced()).toBe(true); + + const bfsNodesAfterBalanced = avlCounter.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = avlCounter.delete(11, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight(15)).toBe(1); + + const removed1 = avlCounter.delete(1, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight()).toBe(4); + + const removed4 = avlCounter.delete(4, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(4); + + const removed10 = avlCounter.delete(10, true); + expect(removed10 instanceof Array); + expect(removed10[0]); + expect(removed10[0].deleted); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight()).toBe(3); + + const removed15 = avlCounter.delete(15, true); + expect(removed15 instanceof Array); + expect(removed15[0]); + expect(removed15[0].deleted); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed5 = avlCounter.delete(5, true); + expect(removed5 instanceof Array); + expect(removed5[0]); + expect(removed5[0].deleted); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed13 = avlCounter.delete(13, true); + expect(removed13 instanceof Array); + expect(removed13[0]); + expect(removed13[0].deleted); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed3 = avlCounter.delete(3, true); + expect(removed3 instanceof Array); + expect(removed3[0]); + expect(removed3[0].deleted); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed8 = avlCounter.delete(8, true); + expect(removed8 instanceof Array); + expect(removed8[0]); + expect(removed8[0].deleted); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(3); + + const removed6 = avlCounter.delete(6, true); + expect(removed6 instanceof Array); + expect(removed6[0]); + expect(removed6[0].deleted); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(avlCounter.delete(6, true).length).toBe(0); + expect(avlCounter.isAVLBalanced()).toBe(true); + + expect(avlCounter.getHeight()).toBe(2); + + const removed7 = avlCounter.delete(7, true); + expect(removed7 instanceof Array); + expect(removed7[0]); + expect(removed7[0].deleted); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(2); + + const removed9 = avlCounter.delete(9, true); + expect(removed9 instanceof Array); + expect(removed9[0]); + expect(removed9[0].deleted); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(2); + + const removed14 = avlCounter.delete(14, true); + expect(removed14 instanceof Array); + expect(removed14[0]); + expect(removed14[0].deleted); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(avlCounter.isAVLBalanced()).toBe(true); + expect(avlCounter.getHeight()).toBe(1); + + expect(avlCounter.isAVLBalanced()).toBe(true); + + const bfsIDs = avlCounter.bfs(node => node.key); + + expect(bfsIDs[0]).toBe(12); + expect(bfsIDs[1]).toBe(2); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes = avlCounter.bfs(node => node); + + expect(bfsNodes[0].key).toBe(12); + expect(bfsNodes[1].key).toBe(2); + expect(bfsNodes[2].key).toBe(16); + + expect(avlCounter.count).toBe(8); + }); + + it('should perform various operations on a AVLTreeCounter with object values', () => { + const objAvlCounter = new AVLTreeCounter(); + expect(objAvlCounter).toBeInstanceOf(AVLTreeCounter); + objAvlCounter.add([11, { key: 11, keyA: 11 }]); + objAvlCounter.add([3, { key: 3, keyA: 3 }]); + const values: [number, { key: number; keyA: number }][] = [ + [15, { key: 15, keyA: 15 }], + [1, { key: 1, keyA: 1 }], + [8, { key: 8, keyA: 8 }], + [13, { key: 13, keyA: 13 }], + [16, { key: 16, keyA: 16 }], + [2, { key: 2, keyA: 2 }], + [6, { key: 6, keyA: 6 }], + [9, { key: 9, keyA: 9 }], + [12, { key: 12, keyA: 12 }], + [14, { key: 14, keyA: 14 }], + [4, { key: 4, keyA: 4 }], + [7, { key: 7, keyA: 7 }], + [10, { key: 10, keyA: 10 }], + [5, { key: 5, keyA: 5 }] + ]; + + objAvlCounter.addMany(values); + + expect(objAvlCounter.root).toBeInstanceOf(AVLTreeCounterNode); + + if (objAvlCounter.root) expect(objAvlCounter.root.key).toBe(6); + + expect(objAvlCounter.count).toBe(16); + + expect(objAvlCounter.has(6)).toBe(true); + }); +}); + +describe('AVLTreeCounter Performance test', function () { + const avlCounter = new AVLTreeCounter(); + const inputSize = 100000; // Adjust input sizes as needed + + beforeEach(() => { + avlCounter.clear(); + }); + + it(`Observe the time consumption of AVLTreeCounter.dfs be good`, function () { + const startDFS = performance.now(); + const dfs = avlCounter.dfs(node => node); + if (isDebug) console.log('---bfs', performance.now() - startDFS, dfs.length); + }); + + it('Should the time consumption of lesserOrGreaterTraverse fitting O(n log n)', function () { + const start = performance.now(); + for (let i = 0; i < inputSize; i++) { + avlCounter.add(i); + } + if (isDebug) console.log('---add', performance.now() - start); + const startL = performance.now(); + avlCounter.lesserOrGreaterTraverse(node => (node.count += 1), -1, inputSize / 2); + if (isDebug) console.log('---lesserOrGreaterTraverse', performance.now() - startL); + }); + + it('should the clone method', () => { + function checkTreeStructure(avlCounter: AVLTreeCounter) { + expect(avlCounter.size).toBe(4); + expect(avlCounter.root?.key).toBe('2'); + expect(avlCounter.root?.left?.key).toBe('1'); + expect(avlCounter.root?.left?.left?.key).toBe(undefined); + expect(avlCounter.root?.left?.right?.key).toBe(undefined); + expect(avlCounter.root?.right?.key).toBe('4'); + expect(avlCounter.root?.right?.left?.key).toBe(undefined); + expect(avlCounter.root?.right?.right?.key).toBe('5'); + } + + const avlCounter = new AVLTreeCounter(); + avlCounter.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(avlCounter.size).toBe(5); + expect(avlCounter.root?.key).toBe('2'); + expect(avlCounter.root?.left?.key).toBe('1'); + expect(avlCounter.root?.left?.left?.key).toBe(undefined); + expect(avlCounter.root?.left?.right?.key).toBe(undefined); + expect(avlCounter.root?.right?.key).toBe('4'); + expect(avlCounter.root?.right?.left?.key).toBe('3'); + expect(avlCounter.root?.right?.right?.key).toBe('5'); + avlCounter.delete('3'); + checkTreeStructure(avlCounter); + const cloned = avlCounter.clone(); + checkTreeStructure(cloned); + cloned.delete('1'); + expect(avlCounter.size).toBe(4); + expect(cloned.size).toBe(3); + }); +}); + +describe('AVLTreeCounter iterative methods test', () => { + let avlCounter: AVLTreeCounter; + beforeEach(() => { + avlCounter = new AVLTreeCounter(); + avlCounter.add(1, 'a', 10); + avlCounter.add([2, 'b'], undefined, 10); + avlCounter.add([3, 'c'], undefined, 1); + }); + + it('The node obtained by get Node should match the node type', () => { + const node3 = avlCounter.getNode(3); + expect(node3).toBeInstanceOf(BinaryTreeNode); + expect(node3).toBeInstanceOf(BSTNode); + expect(node3).toBeInstanceOf(AVLTreeNode); + }); + + it('forEach should iterate over all elements', () => { + const mockCallback = jest.fn(); + avlCounter.forEach((key, value) => { + mockCallback(key, value); + }); + + expect(mockCallback.mock.calls.length).toBe(3); + expect(mockCallback.mock.calls[0]).toEqual([1, 'a']); + expect(mockCallback.mock.calls[1]).toEqual([2, 'b']); + expect(mockCallback.mock.calls[2]).toEqual([3, 'c']); + }); + + it('filter should return a new avlCounter with filtered elements', () => { + const filteredTree = avlCounter.filter(key => key > 1); + expect(filteredTree.size).toBe(2); + expect([...filteredTree]).toEqual([ + [2, 'b'], + [3, 'c'] + ]); + }); + + it('map should return a new avlCounter with modified elements', () => { + const avlCounterMapped = avlCounter.map((key, value) => [(key * 2).toString(), value]); + expect(avlCounterMapped.size).toBe(3); + expect([...avlCounterMapped]).toEqual([ + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] + ]); + }); + + it('reduce should accumulate values', () => { + const sum = avlCounter.reduce((acc, value, key) => acc + key, 0); + expect(sum).toBe(6); + }); + + it('[Symbol.iterator] should provide an iterator', () => { + const entries = []; + for (const entry of avlCounter) { + entries.push(entry); + } + + expect(entries.length).toBe(3); + expect(entries).toEqual([ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ]); + }); + + it('should clone work well', () => { + expect(avlCounter.count).toBe(21); + const cloned = avlCounter.clone(); + expect(cloned.root?.left?.key).toBe(1); + expect(cloned.get(cloned.root?.right)).toBe('c'); + }); + + it('should keys', () => { + const keys = avlCounter.keys(); + expect([...keys]).toEqual([1, 2, 3]); + }); + + it('should values', () => { + const values = avlCounter.values(); + expect([...values]).toEqual(['a', 'b', 'c']); + }); +}); + +describe('AVLTreeCounter toEntryFn', () => { + it('should toEntryFn 1', () => { + const avlCounter = new AVLTreeCounter([], { + toEntryFn: ele => [ele.obj.id, ele.obj.id] + }); + avlCounter.addMany([ + { obj: { id: 1 } }, + { obj: { id: 2 } }, + { obj: { id: 3 } }, + { obj: { id: 4 } }, + { obj: { id: 5 } } + ]); + + const expected = [1, 2, 3, 4, 5]; + + 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); + }); + + it('should toEntryFn 2', () => { + const avlCounter = new AVLTreeCounter( + [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }], + { + toEntryFn: ele => [ele.obj.id, ele.obj.id] + } + ); + + const expected = [1, 2, 3, 4, 5]; + + 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); + }); + + it('should toEntryFn throw error', () => { + expect( + () => + new AVLTreeCounter<{ obj: { id: number } }, number>([ + { obj: { id: 1 } }, + { obj: { id: 2 } }, + { obj: { id: 3 } }, + { obj: { id: 4 } }, + { obj: { id: 5 } } + ]) + ).toThrowError( + `When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.` + ); + }); + + it('should toEntryFn 3', () => { + const avlCounter = new AVLTreeCounter<{ obj: { id: number } }, number>( + [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }], + { + specifyComparable: key => key.obj.id + } + ); + + const expected = [ + { obj: { id: 1 } }, + { obj: { id: 2 } }, + { obj: { id: 3 } }, + { obj: { id: 4 } }, + { obj: { id: 5 } } + ]; + + 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); + }); +}); + +describe('AVLTreeCounter not map mode count', () => { + let avlCounter: AVLTreeCounter; + beforeEach(() => { + avlCounter = new AVLTreeCounter([], { isMapMode: false }); + }); + it('Should added isolated node count ', () => { + avlCounter.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new AVLTreeCounterNode(3, undefined, 10); + avlCounter.add(newNode, 33); + expect(avlCounter.count).toBe(15); + }); +}); + +describe('AVLTreeCounter not map mode operations test1', () => { + it('should perform various operations on a AVLTreeCounter with numeric values1', () => { + const avlCounter = new AVLTreeCounter([], { isMapMode: false }); + + expect(avlCounter instanceof AVLTreeCounter); + avlCounter.add([11, 11]); + avlCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + avlCounter.addMany(idAndValues); + expect(avlCounter.root instanceof AVLTreeCounterNode); + + if (avlCounter.root) expect(avlCounter.root.key == 11); + + expect(avlCounter.size).toBe(16); + expect(avlCounter.count).toBe(18); + + expect(avlCounter.has(6)); + + expect(avlCounter.getHeight(6)).toBe(4); + expect(avlCounter.getDepth(6)).toBe(0); + const nodeId10 = avlCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = avlCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); + +describe('AVLTreeCounter not map mode operations test recursively1', () => { + it('should perform various operations on a AVLTreeCounter with numeric values1', () => { + const avlCounter = new AVLTreeCounter([], { + iterationType: 'RECURSIVE', + isMapMode: false + }); + + expect(avlCounter instanceof AVLTreeCounter); + avlCounter.add([11, 11]); + avlCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + avlCounter.addMany(idAndValues); + expect(avlCounter.root).toBeInstanceOf(AVLTreeCounterNode); + + if (avlCounter.root) expect(avlCounter.root.key).toBe(6); + + expect(avlCounter.size).toBe(16); + expect(avlCounter.count).toBe(18); + + expect(avlCounter.has(6)); + + expect(avlCounter.getHeight(6)).toBe(4); + expect(avlCounter.getDepth(6)).toBe(0); + const nodeId10 = avlCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = avlCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); 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 8389c28..92a37a3 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 @@ -1,875 +1,573 @@ -import { AVLTreeMultiMap, AVLTreeMultiMapNode, AVLTreeNode, BinaryTreeNode, BSTNode } from '../../../../src'; -import { isDebugTest } from '../../../config'; +import { AVLTreeMultiMap, AVLTreeMultiMapNode } from '../../../../src'; +// import { isDebugTest } from '../../../config'; -const isDebug = isDebugTest; +// const isDebug = isDebugTest; -describe('AVLTreeMultiMap count', () => { - let tm: AVLTreeMultiMap; +describe('AVLTreeMultiMap', () => { + let avlTmm: AVLTreeMultiMap; beforeEach(() => { - tm = new AVLTreeMultiMap(); - }); - it('Should added isolated node count ', () => { - tm.addMany([ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5] - ]); - const newNode = new AVLTreeMultiMapNode(3, 33, 10); - tm.add(newNode); - expect(tm.count).toBe(15); + avlTmm = new AVLTreeMultiMap(); }); - it('Should count', () => { - tm.addMany([ - [1, 1], - [2, 2], - [3, 3] - ]); - tm.add([2, 2], undefined, 10); - tm.lesserOrGreaterTraverse(node => (node.count += 2), 1, 1); - tm.delete(2); - expect(tm.count).toBe(12); - expect(tm.getComputedCount()).toBe(16); + it('Should add and delete values', () => { + avlTmm.add(3, 3); + avlTmm.add(3, 33); + avlTmm.add(3, 333); + expect(avlTmm.get(3)).toEqual([3, 33, 333]); + avlTmm.deleteValue(3, 33); + expect(avlTmm.get(3)).toEqual([3, 333]); + avlTmm.deleteValue(3, 3); + expect(avlTmm.get(3)).toEqual([333]); + avlTmm.deleteValue(3, 333); + expect(avlTmm.get(3)).toBe(undefined); + avlTmm.add(3, 3); + avlTmm.add([3, [3333, 33333]]); + expect(avlTmm.get(3)).toEqual([3, 3333, 33333]); }); }); -describe('AVLTreeMultiMap operations test1', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const treeMultimap = new AVLTreeMultiMap(); +describe('AVLTreeMultiMap Test', () => { + it('should perform various operations on a AVLTreeMultiMap', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + const avlTmm = new AVLTreeMultiMap(); - expect(treeMultimap instanceof AVLTreeMultiMap); - treeMultimap.add([11, 11]); - treeMultimap.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - treeMultimap.addMany(idAndValues); - expect(treeMultimap.root instanceof AVLTreeMultiMapNode); + for (const i of arr) avlTmm.add([i, [i]]); - if (treeMultimap.root) expect(treeMultimap.root.key == 11); + avlTmm.add(null); + const node6 = avlTmm.getNode(6); - expect(treeMultimap.size).toBe(16); - expect(treeMultimap.count).toBe(18); + expect(node6 && avlTmm.getHeight(node6)).toBe(3); + expect(node6 && avlTmm.getDepth(node6)).toBe(1); - expect(treeMultimap.has(6)); + const getNodeById = avlTmm.getNode(10); + expect(getNodeById?.key).toBe(10); - expect(treeMultimap.getHeight(6)).toBe(4); - expect(treeMultimap.getDepth(6)).toBe(0); - const nodeId10 = treeMultimap.getNode(10); - expect(nodeId10?.key).toBe(10); + const getMinNodeByRoot = avlTmm.getLeftMost(); + expect(getMinNodeByRoot).toBe(1); - const nodeVal9 = treeMultimap.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); - - const nodesByCount1 = treeMultimap.getNodes(node => node.count === 1); - expect(nodesByCount1.length).toBe(14); - - const nodesByCount2 = treeMultimap.getNodes(node => node.count === 2); - expect(nodesByCount2.length).toBe(2); - const leftMost = treeMultimap.getLeftMost(); - expect(leftMost).toBe(1); - - const node15 = treeMultimap.getNode(15); - const minNodeBySpecificNode = node15 && treeMultimap.getLeftMost(node => node, node15); - expect(minNodeBySpecificNode?.key).toBe(15); + const node15 = avlTmm.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTmm.getLeftMost(node => node, node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) treeMultimap.dfs(node => (subTreeSum += node.key), 'PRE', 15); - expect(subTreeSum).toBe(31); + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + expect(subTreeSum).toBe(70); + let lesserSum = 0; - treeMultimap.lesserOrGreaterTraverse((node: AVLTreeMultiMapNode) => (lesserSum += node.key), -1, 10); + avlTmm.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); expect(lesserSum).toBe(45); - expect(node15 instanceof AVLTreeMultiMapNode); - if (node15 instanceof AVLTreeMultiMapNode) { - const subTreeAdd = treeMultimap.dfs(node => (node.count += 1), 'PRE', 15); - expect(subTreeAdd); - } - const node11 = treeMultimap.getNode(11); - expect(node11 instanceof AVLTreeMultiMapNode); - if (node11 instanceof AVLTreeMultiMapNode) { - const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); - expect(allGreaterNodesAdded); - } + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(avlTmm.get(15)).toEqual([15]); + const dfs = avlTmm.dfs(node => node, 'IN'); + expect(dfs[0].key).toBe(1); + expect(dfs[dfs.length - 1].key).toBe(16); + avlTmm.perfectlyBalance(); + const bfs = avlTmm.bfs(node => node); + expect(avlTmm.isPerfectlyBalanced()).toBe(true); + expect(bfs[0].key).toBe(8); + expect(bfs[bfs.length - 1].key).toBe(16); - const dfsInorderNodes = treeMultimap.dfs(node => node, 'IN'); - expect(dfsInorderNodes[0].key).toBe(1); - expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); - expect(treeMultimap.isPerfectlyBalanced()).toBe(false); + expect(avlTmm.delete(avlTmm.getNode(11))[0].deleted?.key).toBe(11); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(node15 && avlTmm.getHeight(node15)).toBe(2); - treeMultimap.perfectlyBalance(); + expect(avlTmm.delete(1)[0].deleted?.key).toBe(1); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(4); - expect(treeMultimap.isPerfectlyBalanced()).toBe(true); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(4)[0].deleted?.key).toBe(4); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(4); - const bfsNodesAfterBalanced = treeMultimap.bfs(node => node); - expect(bfsNodesAfterBalanced[0].key).toBe(8); - expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + expect(avlTmm.delete(10)[0].deleted?.key).toBe(10); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - const removed11 = treeMultimap.delete(11, true); - expect(removed11 instanceof Array); - expect(removed11[0]); - expect(removed11[0].deleted); + expect(avlTmm.delete(15)[0].deleted?.key).toBe(15); + expect(avlTmm.isAVLBalanced()).toBe(true); - if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(5)[0].deleted?.key).toBe(5); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.getHeight(15)).toBe(1); + expect(avlTmm.delete(13)[0].deleted?.key).toBe(13); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - const removed1 = treeMultimap.delete(1, true); - expect(removed1 instanceof Array); - expect(removed1[0]); - expect(removed1[0].deleted); - if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + expect(avlTmm.delete(3)[0].deleted?.key).toBe(3); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(8)[0].deleted?.key).toBe(8); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.getHeight()).toBe(4); + expect(avlTmm.delete(6)[0].deleted?.key).toBe(6); + expect(avlTmm.delete(6).length).toBe(0); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(2); - const removed4 = treeMultimap.delete(4, true); - expect(removed4 instanceof Array); - expect(removed4[0]); - expect(removed4[0].deleted); - if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + expect(avlTmm.delete(7)[0].deleted?.key).toBe(7); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(2); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(4); + expect(avlTmm.delete(9)[0].deleted?.key).toBe(9); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(2); + expect(avlTmm.delete(14)[0].deleted?.key).toBe(14); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(1); - const removed10 = treeMultimap.delete(10, true); - expect(removed10 instanceof Array); - expect(removed10[0]); - expect(removed10[0].deleted); - if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.isAVLBalanced()).toBe(true); + const lastBFSIds = avlTmm.bfs(); + expect(lastBFSIds[0]).toBe(12); + expect(lastBFSIds[1]).toBe(2); + expect(lastBFSIds[2]).toBe(16); - expect(treeMultimap.getHeight()).toBe(3); - - const removed15 = treeMultimap.delete(15, true); - expect(removed15 instanceof Array); - expect(removed15[0]); - expect(removed15[0].deleted); - if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); - - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed5 = treeMultimap.delete(5, true); - expect(removed5 instanceof Array); - expect(removed5[0]); - expect(removed5[0].deleted); - if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); - - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed13 = treeMultimap.delete(13, true); - expect(removed13 instanceof Array); - expect(removed13[0]); - expect(removed13[0].deleted); - if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed3 = treeMultimap.delete(3, true); - expect(removed3 instanceof Array); - expect(removed3[0]); - expect(removed3[0].deleted); - if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed8 = treeMultimap.delete(8, true); - expect(removed8 instanceof Array); - expect(removed8[0]); - expect(removed8[0].deleted); - if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed6 = treeMultimap.delete(6, true); - expect(removed6 instanceof Array); - expect(removed6[0]); - expect(removed6[0].deleted); - if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); - expect(treeMultimap.delete(6, true).length).toBe(0); - expect(treeMultimap.isAVLBalanced()).toBe(true); - - expect(treeMultimap.getHeight()).toBe(2); - - const removed7 = treeMultimap.delete(7, true); - expect(removed7 instanceof Array); - expect(removed7[0]); - expect(removed7[0].deleted); - if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(2); - - const removed9 = treeMultimap.delete(9, true); - expect(removed9 instanceof Array); - expect(removed9[0]); - expect(removed9[0].deleted); - if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(2); - - const removed14 = treeMultimap.delete(14, true); - expect(removed14 instanceof Array); - expect(removed14[0]); - expect(removed14[0].deleted); - if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(1); - - expect(treeMultimap.isAVLBalanced()).toBe(true); - - const bfsIDs = treeMultimap.bfs(node => node.key); - - expect(bfsIDs[0]).toBe(12); - expect(bfsIDs[1]).toBe(2); - expect(bfsIDs[2]).toBe(16); - - const bfsNodes = treeMultimap.bfs(node => node); - - expect(bfsNodes[0].key).toBe(12); - expect(bfsNodes[1].key).toBe(2); - expect(bfsNodes[2].key).toBe(16); - - expect(treeMultimap.count).toBe(8); + const lastBFSNodes = avlTmm.bfs(node => node); + expect(lastBFSNodes[0].key).toBe(12); + expect(lastBFSNodes[1].key).toBe(2); + expect(lastBFSNodes[2].key).toBe(16); }); - it('should perform various operations on a Binary Search Tree with object values', () => { - const objAVLTreeMultiMap = new AVLTreeMultiMap(); - expect(objAVLTreeMultiMap).toBeInstanceOf(AVLTreeMultiMap); - objAVLTreeMultiMap.add([11, { key: 11, keyA: 11 }]); - objAVLTreeMultiMap.add([3, { key: 3, keyA: 3 }]); - const values: [number, { key: number; keyA: number }][] = [ - [15, { key: 15, keyA: 15 }], - [1, { key: 1, keyA: 1 }], - [8, { key: 8, keyA: 8 }], - [13, { key: 13, keyA: 13 }], - [16, { key: 16, keyA: 16 }], - [2, { key: 2, keyA: 2 }], - [6, { key: 6, keyA: 6 }], - [9, { key: 9, keyA: 9 }], - [12, { key: 12, keyA: 12 }], - [14, { key: 14, keyA: 14 }], - [4, { key: 4, keyA: 4 }], - [7, { key: 7, keyA: 7 }], - [10, { key: 10, keyA: 10 }], - [5, { key: 5, keyA: 5 }] - ]; - - objAVLTreeMultiMap.addMany(values); - - expect(objAVLTreeMultiMap.root).toBeInstanceOf(AVLTreeMultiMapNode); - - if (objAVLTreeMultiMap.root) expect(objAVLTreeMultiMap.root.key).toBe(6); - - expect(objAVLTreeMultiMap.count).toBe(16); - - expect(objAVLTreeMultiMap.has(6)).toBe(true); + it('should add value', () => { + const avlTmm = new AVLTreeMultiMap([4, 5, [1, ['1']], 2, 3]); + expect(avlTmm.get(1)).toEqual(['1']); + expect(avlTmm.getNode(1)?.value).toEqual([]); + avlTmm.add(1, 'a'); + expect(avlTmm.get(1)).toEqual(['1', 'a']); + avlTmm.add([1, ['b']]); + expect(avlTmm.getNode(1)?.value).toEqual([]); + expect(avlTmm.get(1)).toEqual(['1', 'a', 'b']); + const treeMap = new AVLTreeMultiMap([4, 5, [1, ['1']], 2, 3]); + expect(treeMap.get(1)).toEqual(['1']); + expect(treeMap.getNode(1)?.value).toEqual([]); + treeMap.add(1, 'a'); + expect(treeMap.get(1)).toEqual(['1', 'a']); + treeMap.add([1, ['b']]); + expect(treeMap.getNode(1)?.value).toEqual([]); + expect(treeMap.get(1)).toEqual(['1', 'a', 'b']); }); }); -describe('AVLTreeMultiMap operations test recursively1', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const treeMultimap = new AVLTreeMultiMap([], { - iterationType: 'RECURSIVE' - }); +describe('AVLTreeMultiMap Test recursively', () => { + it('should perform various operations on a AVLTreeMultiMap', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + const avlTmm = new AVLTreeMultiMap([], { iterationType: 'RECURSIVE' }); - expect(treeMultimap instanceof AVLTreeMultiMap); - treeMultimap.add([11, 11]); - treeMultimap.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - treeMultimap.addMany(idAndValues); - expect(treeMultimap.root).toBeInstanceOf(AVLTreeMultiMapNode); + for (const i of arr) avlTmm.add([i, [i]]); - if (treeMultimap.root) expect(treeMultimap.root.key).toBe(6); + const node6 = avlTmm.getNode(6); - expect(treeMultimap.size).toBe(16); - expect(treeMultimap.count).toBe(18); + expect(node6 && avlTmm.getHeight(node6)).toBe(3); + expect(node6 && avlTmm.getDepth(node6)).toBe(1); - expect(treeMultimap.has(6)); + const getNodeById = avlTmm.getNode(10); + expect(getNodeById?.key).toBe(10); - expect(treeMultimap.getHeight(6)).toBe(4); - expect(treeMultimap.getDepth(6)).toBe(0); - const nodeId10 = treeMultimap.getNode(10); - expect(nodeId10?.key).toBe(10); + const getMinNodeByRoot = avlTmm.getLeftMost(); + expect(getMinNodeByRoot).toBe(1); - const nodeVal9 = treeMultimap.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); - - const nodesByCount1 = treeMultimap.getNodes(node => node.count === 1); - expect(nodesByCount1.length).toBe(14); - - const nodesByCount2 = treeMultimap.getNodes(node => node.count === 2); - expect(nodesByCount2.length).toBe(2); - const leftMost = treeMultimap.getLeftMost(); - expect(leftMost).toBe(1); - - const node15 = treeMultimap.getNode(15); - const minNodeBySpecificNode = node15 && treeMultimap.getLeftMost(node => node, node15); - expect(minNodeBySpecificNode?.key).toBe(15); + const node15 = avlTmm.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTmm.getLeftMost(node => node, node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) treeMultimap.dfs(node => (subTreeSum += node.key), 'PRE', 15); - expect(subTreeSum).toBe(31); + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + expect(subTreeSum).toBe(70); + let lesserSum = 0; - treeMultimap.lesserOrGreaterTraverse((node: AVLTreeMultiMapNode) => (lesserSum += node.key), -1, 10); + avlTmm.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); expect(lesserSum).toBe(45); - expect(node15 instanceof AVLTreeMultiMapNode); - if (node15 instanceof AVLTreeMultiMapNode) { - const subTreeAdd = treeMultimap.dfs(node => (node.count += 1), 'PRE', 15); - expect(subTreeAdd); - } - const node11 = treeMultimap.getNode(11); - expect(node11 instanceof AVLTreeMultiMapNode); - if (node11 instanceof AVLTreeMultiMapNode) { - const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); - expect(allGreaterNodesAdded); - } + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(avlTmm.get(15)).toEqual([15]); - const dfsInorderNodes = treeMultimap.dfs(node => node, 'IN'); - expect(dfsInorderNodes[0].key).toBe(1); - expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); - expect(treeMultimap.isPerfectlyBalanced()).toBe(true); + const dfs = avlTmm.dfs(node => node, 'IN'); + expect(dfs[0].key).toBe(1); + expect(dfs[dfs.length - 1].key).toBe(16); - treeMultimap.perfectlyBalance(); + avlTmm.perfectlyBalance(); + const bfs = avlTmm.bfs(node => node); + expect(avlTmm.isPerfectlyBalanced()).toBe(true); + expect(bfs[0].key).toBe(8); + expect(bfs[bfs.length - 1].key).toBe(16); - expect(treeMultimap.isPerfectlyBalanced()).toBe(true); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(11)[0].deleted?.key).toBe(11); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(node15 && avlTmm.getHeight(node15)).toBe(2); - const bfsNodesAfterBalanced = treeMultimap.bfs(node => node); - expect(bfsNodesAfterBalanced[0].key).toBe(8); - expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + expect(avlTmm.delete(1)[0].deleted?.key).toBe(1); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(4); - const removed11 = treeMultimap.delete(11, true); - expect(removed11 instanceof Array); - expect(removed11[0]); - expect(removed11[0].deleted); + expect(avlTmm.delete(4)[0].deleted?.key).toBe(4); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(4); - if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + expect(avlTmm.delete(10)[0].deleted?.key).toBe(10); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(15)[0].deleted?.key).toBe(15); + expect(avlTmm.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight(15)).toBe(1); + expect(avlTmm.getHeight()).toBe(3); - const removed1 = treeMultimap.delete(1, true); - expect(removed1 instanceof Array); - expect(removed1[0]); - expect(removed1[0].deleted); - if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + expect(avlTmm.delete(5)[0].deleted?.key).toBe(5); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(13)[0].deleted?.key).toBe(13); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.getHeight()).toBe(4); + expect(avlTmm.delete(3)[0].deleted?.key).toBe(3); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - const removed4 = treeMultimap.delete(4, true); - expect(removed4 instanceof Array); - expect(removed4[0]); - expect(removed4[0].deleted); - if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + expect(avlTmm.delete(8)[0].deleted?.key).toBe(8); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(4); + expect(avlTmm.delete(6)[0].deleted?.key).toBe(6); + expect(avlTmm.delete(6).length).toBe(0); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(2); - const removed10 = treeMultimap.delete(10, true); - expect(removed10 instanceof Array); - expect(removed10[0]); - expect(removed10[0].deleted); - if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); - expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(avlTmm.delete(7)[0].deleted?.key).toBe(7); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(2); - expect(treeMultimap.getHeight()).toBe(3); + expect(avlTmm.delete(9)[0].deleted?.key).toBe(9); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(2); + expect(avlTmm.delete(14)[0].deleted?.key).toBe(14); + expect(avlTmm.isAVLBalanced()).toBe(true); + expect(avlTmm.getHeight()).toBe(1); - const removed15 = treeMultimap.delete(15, true); - expect(removed15 instanceof Array); - expect(removed15[0]); - expect(removed15[0].deleted); - if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + expect(avlTmm.isAVLBalanced()).toBe(true); + const lastBFSIds = avlTmm.bfs(); + expect(lastBFSIds[0]).toBe(12); + expect(lastBFSIds[1]).toBe(2); + expect(lastBFSIds[2]).toBe(16); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed5 = treeMultimap.delete(5, true); - expect(removed5 instanceof Array); - expect(removed5[0]); - expect(removed5[0].deleted); - if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); - - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed13 = treeMultimap.delete(13, true); - expect(removed13 instanceof Array); - expect(removed13[0]); - expect(removed13[0].deleted); - if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed3 = treeMultimap.delete(3, true); - expect(removed3 instanceof Array); - expect(removed3[0]); - expect(removed3[0].deleted); - if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed8 = treeMultimap.delete(8, true); - expect(removed8 instanceof Array); - expect(removed8[0]); - expect(removed8[0].deleted); - if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(3); - - const removed6 = treeMultimap.delete(6, true); - expect(removed6 instanceof Array); - expect(removed6[0]); - expect(removed6[0].deleted); - if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); - expect(treeMultimap.delete(6, true).length).toBe(0); - expect(treeMultimap.isAVLBalanced()).toBe(true); - - expect(treeMultimap.getHeight()).toBe(2); - - const removed7 = treeMultimap.delete(7, true); - expect(removed7 instanceof Array); - expect(removed7[0]); - expect(removed7[0].deleted); - if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(2); - - const removed9 = treeMultimap.delete(9, true); - expect(removed9 instanceof Array); - expect(removed9[0]); - expect(removed9[0].deleted); - if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(2); - - const removed14 = treeMultimap.delete(14, true); - expect(removed14 instanceof Array); - expect(removed14[0]); - expect(removed14[0].deleted); - if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); - expect(treeMultimap.isAVLBalanced()).toBe(true); - expect(treeMultimap.getHeight()).toBe(1); - - expect(treeMultimap.isAVLBalanced()).toBe(true); - - const bfsIDs = treeMultimap.bfs(node => node.key); - - expect(bfsIDs[0]).toBe(12); - expect(bfsIDs[1]).toBe(2); - expect(bfsIDs[2]).toBe(16); - - const bfsNodes = treeMultimap.bfs(node => node); - - expect(bfsNodes[0].key).toBe(12); - expect(bfsNodes[1].key).toBe(2); - expect(bfsNodes[2].key).toBe(16); - - expect(treeMultimap.count).toBe(8); - }); - - it('should perform various operations on a Binary Search Tree with object values', () => { - const objAVLTreeMultiMap = new AVLTreeMultiMap(); - expect(objAVLTreeMultiMap).toBeInstanceOf(AVLTreeMultiMap); - objAVLTreeMultiMap.add([11, { key: 11, keyA: 11 }]); - objAVLTreeMultiMap.add([3, { key: 3, keyA: 3 }]); - const values: [number, { key: number; keyA: number }][] = [ - [15, { key: 15, keyA: 15 }], - [1, { key: 1, keyA: 1 }], - [8, { key: 8, keyA: 8 }], - [13, { key: 13, keyA: 13 }], - [16, { key: 16, keyA: 16 }], - [2, { key: 2, keyA: 2 }], - [6, { key: 6, keyA: 6 }], - [9, { key: 9, keyA: 9 }], - [12, { key: 12, keyA: 12 }], - [14, { key: 14, keyA: 14 }], - [4, { key: 4, keyA: 4 }], - [7, { key: 7, keyA: 7 }], - [10, { key: 10, keyA: 10 }], - [5, { key: 5, keyA: 5 }] - ]; - - objAVLTreeMultiMap.addMany(values); - - expect(objAVLTreeMultiMap.root).toBeInstanceOf(AVLTreeMultiMapNode); - - if (objAVLTreeMultiMap.root) expect(objAVLTreeMultiMap.root.key).toBe(6); - - expect(objAVLTreeMultiMap.count).toBe(16); - - expect(objAVLTreeMultiMap.has(6)).toBe(true); + const lastBFSNodes = avlTmm.bfs(node => node); + expect(lastBFSNodes[0].key).toBe(12); + expect(lastBFSNodes[1].key).toBe(2); + expect(lastBFSNodes[2].key).toBe(16); }); }); -describe('AVLTreeMultiMap Performance test', function () { - const treeMS = new AVLTreeMultiMap(); - const inputSize = 100000; // Adjust input sizes as needed - +describe('AVLTreeMultiMap APIs test', () => { + const avlTmm = new AVLTreeMultiMap(); beforeEach(() => { - treeMS.clear(); + avlTmm.clear(); }); - it(`Observe the time consumption of AVLTreeMultiMap.dfs be good`, function () { - const startDFS = performance.now(); - const dfs = treeMS.dfs(node => node); - if (isDebug) console.log('---bfs', performance.now() - startDFS, dfs.length); - }); + it('add', () => { + avlTmm.add(1); + const node2 = new AVLTreeMultiMapNode(2, []); + avlTmm.add(node2); + const node3 = new AVLTreeMultiMapNode(3, [ + { + id: 3, + text: 'text3' + } + ]); + avlTmm.add(node3); + avlTmm.add([3, [{ id: 3, text: 'text33' }]]); - it('Should the time consumption of lesserOrGreaterTraverse fitting O(n log n)', function () { - const start = performance.now(); - for (let i = 0; i < inputSize; i++) { - treeMS.add(i); - } - if (isDebug) console.log('---add', performance.now() - start); - const startL = performance.now(); - treeMS.lesserOrGreaterTraverse(node => (node.count += 1), -1, inputSize / 2); - if (isDebug) console.log('---lesserOrGreaterTraverse', performance.now() - startL); + const bfsRes = avlTmm.bfs(node => node.key); + expect(bfsRes[0]).toBe(2); }); it('should the clone method', () => { - function checkTreeStructure(treeMultimap: AVLTreeMultiMap) { - expect(treeMultimap.size).toBe(4); - expect(treeMultimap.root?.key).toBe('2'); - expect(treeMultimap.root?.left?.key).toBe('1'); - expect(treeMultimap.root?.left?.left?.key).toBe(undefined); - expect(treeMultimap.root?.left?.right?.key).toBe(undefined); - expect(treeMultimap.root?.right?.key).toBe('4'); - expect(treeMultimap.root?.right?.left?.key).toBe(undefined); - expect(treeMultimap.root?.right?.right?.key).toBe('5'); + function checkTreeStructure(avlTmm: AVLTreeMultiMap) { + expect(avlTmm.size).toBe(4); + expect(avlTmm.root?.key).toBe('2'); + expect(avlTmm.root?.left?.key).toBe('1'); + expect(avlTmm.root?.left?.left?.key).toBe(undefined); + expect(avlTmm.root?.left?.right?.key).toBe(undefined); + expect(avlTmm.root?.right?.key).toBe('4'); + expect(avlTmm.root?.right?.left?.key).toBe(undefined); + expect(avlTmm.root?.right?.right?.key).toBe('5'); } - const treeMultimap = new AVLTreeMultiMap(); - treeMultimap.addMany([ + const avlTmm = new AVLTreeMultiMap(); + avlTmm.addMany([ ['2', 2], ['4', 4], ['5', 5], ['3', 3], ['1', 1] ]); - expect(treeMultimap.size).toBe(5); - expect(treeMultimap.root?.key).toBe('2'); - expect(treeMultimap.root?.left?.key).toBe('1'); - expect(treeMultimap.root?.left?.left?.key).toBe(undefined); - expect(treeMultimap.root?.left?.right?.key).toBe(undefined); - expect(treeMultimap.root?.right?.key).toBe('4'); - expect(treeMultimap.root?.right?.left?.key).toBe('3'); - expect(treeMultimap.root?.right?.right?.key).toBe('5'); - treeMultimap.delete('3'); - checkTreeStructure(treeMultimap); - const cloned = treeMultimap.clone(); + expect(avlTmm.size).toBe(5); + expect(avlTmm.root?.key).toBe('2'); + expect(avlTmm.root?.left?.key).toBe('1'); + expect(avlTmm.root?.left?.left?.key).toBe(undefined); + expect(avlTmm.root?.left?.right?.key).toBe(undefined); + expect(avlTmm.root?.right?.key).toBe('4'); + expect(avlTmm.root?.right?.left?.key).toBe('3'); + expect(avlTmm.root?.right?.right?.key).toBe('5'); + avlTmm.delete('3'); + checkTreeStructure(avlTmm); + const cloned = avlTmm.clone(); checkTreeStructure(cloned); cloned.delete('1'); - expect(treeMultimap.size).toBe(4); + expect(avlTmm.size).toBe(4); expect(cloned.size).toBe(3); }); }); +describe('AVLTreeMultiMap', () => { + it('should balance the avlTmm using _balanceLR when nodes are added', () => { + const avlTmm = new AVLTreeMultiMap(); + avlTmm.add([10, 'A']); + avlTmm.add([5, 'B']); + avlTmm.add([15, 'C']); + avlTmm.add([3, 'D']); + avlTmm.add([7, 'E']); + + // Adding nodes to trigger _balanceLR + avlTmm.add([12, 'F']); + + // You can add more specific assertions to check the avlTmm's balance and structure. + }); + + it('should addMany undefined and null', () => { + const avlTmm = new AVLTreeMultiMap(); + const addManyWithUndefined = avlTmm.addMany([1, undefined, 3]); + expect(addManyWithUndefined).toEqual([true, false, true]); + expect(avlTmm.get(undefined)).toBe(undefined); + const addManyWithNull = avlTmm.addMany([1, null, 3, 4]); + expect(addManyWithNull).toEqual([true, false, true, true]); + const addManyEntriesWithNull = avlTmm.addMany([ + [1, '1'], + [null, 'null'], + [3, '3'], + [4, '4'] + ]); + expect(addManyEntriesWithNull).toEqual([true, false, true, true]); + expect(avlTmm.get(null)).toBe(undefined); + const node0 = avlTmm.add(0, '0'); + expect(node0).toBe(true); + expect(avlTmm.get(0)).toEqual(['0']); + }); + + it('should balance the avlTmm using _balanceLR when nodes are deleted', () => { + const avlTmm = new AVLTreeMultiMap(); + avlTmm.add([10, 'A']); + avlTmm.add([5, 'B']); + avlTmm.add([15, 'C']); + avlTmm.add([3, 'D']); + avlTmm.add([7, 'E']); + avlTmm.add([12, 'F']); + + // Deleting nodes to trigger _balanceLR + avlTmm.delete(3); + + // You can add more specific assertions to check the avlTmm's balance and structure. + }); + + describe('BinaryTree APIs test', () => { + const avlTmm = new AVLTreeMultiMap(); + beforeEach(() => { + avlTmm.clear(); + }); + + it('add', () => { + avlTmm.add(1); + const node2 = new AVLTreeMultiMapNode(2, []); + avlTmm.add(node2); + const node3 = new AVLTreeMultiMapNode(3, [ + { + id: 3, + text: 'text3' + } + ]); + avlTmm.add(node3); + avlTmm.add([3, [{ id: 3, text: 'text33' }]]); + + const bfsRes = avlTmm.bfs(node => node); + expect(bfsRes[0]?.key).toBe(2); + }); + }); +}); + describe('AVLTreeMultiMap iterative methods test', () => { - let treeMM: AVLTreeMultiMap; + let avlTmm: AVLTreeMultiMap; beforeEach(() => { - treeMM = new AVLTreeMultiMap(); - treeMM.add(1, 'a', 10); - treeMM.add([2, 'b'], undefined, 10); - treeMM.add([3, 'c'], undefined, 1); + avlTmm = new AVLTreeMultiMap(); + avlTmm.add([1, ['a']]); + avlTmm.add([2, ['b']]); + avlTmm.add([3, ['c']]); }); it('The node obtained by get Node should match the node type', () => { - const node3 = treeMM.getNode(3); - expect(node3).toBeInstanceOf(BinaryTreeNode); - expect(node3).toBeInstanceOf(BSTNode); - expect(node3).toBeInstanceOf(AVLTreeNode); + const node3 = avlTmm.getNode(3); + + expect(node3).toBeInstanceOf(AVLTreeMultiMapNode); }); it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); - treeMM.forEach((key, value) => { + avlTmm.forEach((key, value) => { mockCallback(key, value); }); expect(mockCallback.mock.calls.length).toBe(3); - expect(mockCallback.mock.calls[0]).toEqual([1, 'a']); - expect(mockCallback.mock.calls[1]).toEqual([2, 'b']); - expect(mockCallback.mock.calls[2]).toEqual([3, 'c']); + expect(mockCallback.mock.calls[0]).toEqual([1, ['a']]); + expect(mockCallback.mock.calls[1]).toEqual([2, ['b']]); + expect(mockCallback.mock.calls[2]).toEqual([3, ['c']]); }); - it('filter should return a new tree with filtered elements', () => { - const filteredTree = treeMM.filter(key => key > 1); + it('filter should return a new avlTmm with filtered elements', () => { + const filteredTree = avlTmm.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ - [2, 'b'], - [3, 'c'] + [2, ['b']], + [3, ['c']] ]); }); - it('map should return a new tree with modified elements', () => { - const mappedTree = treeMM.map((key, value) => [(key * 2).toString(), value]); - expect(mappedTree.size).toBe(3); - expect([...mappedTree]).toEqual([ - ['2', 'a'], - ['4', 'b'], - ['6', 'c'] + it('map should return a new avlTmm with modified elements', () => { + const avlTmmMapped = avlTmm.map((key, value) => [(key * 2).toString(), value]); + expect(avlTmmMapped.size).toBe(3); + expect([...avlTmmMapped]).toEqual([ + ['2', ['a']], + ['4', ['b']], + ['6', ['c']] ]); }); it('reduce should accumulate values', () => { - const sum = treeMM.reduce((acc, value, key) => acc + key, 0); + const sum = avlTmm.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); it('[Symbol.iterator] should provide an iterator', () => { const entries = []; - for (const entry of treeMM) { + for (const entry of avlTmm) { entries.push(entry); } expect(entries.length).toBe(3); expect(entries).toEqual([ - [1, 'a'], - [2, 'b'], - [3, 'c'] + [1, ['a']], + [2, ['b']], + [3, ['c']] ]); }); it('should clone work well', () => { - expect(treeMM.count).toBe(21); - const cloned = treeMM.clone(); + const cloned = avlTmm.clone(); expect(cloned.root?.left?.key).toBe(1); - expect(cloned.get(cloned.root?.right)).toBe('c'); + expect(cloned.root?.right?.value).toEqual([]); }); it('should keys', () => { - const keys = treeMM.keys(); + const keys = avlTmm.keys(); expect([...keys]).toEqual([1, 2, 3]); }); it('should values', () => { - const values = treeMM.values(); - expect([...values]).toEqual(['a', 'b', 'c']); + const values = avlTmm.values(); + expect([...values]).toEqual([['a'], ['b'], ['c']]); + }); + + it('should leaves', () => { + const leaves = avlTmm.leaves(); + expect(leaves).toEqual([1, 3]); }); }); -describe('AVLTree toEntryFn', () => { - it('should toEntryFn 1', () => { - const tree = new AVLTreeMultiMap([], { - toEntryFn: ele => [ele.obj.id, ele.obj.id] - }); - tree.add({ obj: { id: 1 } }); - tree.add({ obj: { id: 2 } }); - tree.add({ obj: { id: 3 } }); - tree.add({ obj: { id: 4 } }); - tree.add({ obj: { id: 5 } }); +describe('AVLTreeMultiMap not map mode', () => { + it('should perform various operations on a AVLTreeMultiMap', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + const avlTmm = new AVLTreeMultiMap([]); - const expected = [1, 2, 3, 4, 5]; + for (const i of arr) avlTmm.add([i, [i]]); - expect(tree.morris(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN', tree.root, 'RECURSIVE')).toEqual(expected); - }); + avlTmm.add(null); + const node6 = avlTmm.getNode(6); - it('should toEntryFn 2', () => { - const tree = new AVLTreeMultiMap( - [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }], - { - toEntryFn: ele => [ele.obj.id, ele.obj.id] - } - ); + expect(node6 && avlTmm.getHeight(node6)).toBe(3); + expect(node6 && avlTmm.getDepth(node6)).toBe(1); - const expected = [1, 2, 3, 4, 5]; + const getNodeById = avlTmm.getNode(10); + expect(getNodeById?.key).toBe(10); - expect(tree.morris(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN', tree.root, 'RECURSIVE')).toEqual(expected); - }); + const getMinNodeByRoot = avlTmm.getLeftMost(); + expect(getMinNodeByRoot).toBe(1); - it('should toEntryFn throw error', () => { - expect( - () => - new AVLTreeMultiMap<{ obj: { id: number } }, number>([ - { obj: { id: 1 } }, - { obj: { id: 2 } }, - { obj: { id: 3 } }, - { obj: { id: 4 } }, - { obj: { id: 5 } } - ]) - ).toThrowError( - `When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.` - ); - }); + const node15 = avlTmm.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTmm.getLeftMost(node => node, node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); - it('should toEntryFn 3', () => { - const tree = new AVLTreeMultiMap<{ obj: { id: number } }, number>( - [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }], - { - specifyComparable: key => key.obj.id - } - ); + let subTreeSum = 0; + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + expect(subTreeSum).toBe(70); - const expected = [ - { obj: { id: 1 } }, - { obj: { id: 2 } }, - { obj: { id: 3 } }, - { obj: { id: 4 } }, - { obj: { id: 5 } } - ]; + let lesserSum = 0; + avlTmm.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); - expect(tree.morris(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN', tree.root, 'RECURSIVE')).toEqual(expected); + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(avlTmm.get(node15)).toEqual([15]); }); }); -describe('AVLTreeMultiMap not map mode count', () => { - let tm: AVLTreeMultiMap; +describe('AVLTreeMultiMap not map mode test recursively', () => { + it('should perform various operations on a AVLTreeMultiMap', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + 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); + expect(node6 && avlTmm.getDepth(node6)).toBe(1); + + const getNodeById = avlTmm.getNode(10); + expect(getNodeById?.key).toBe(10); + + const getMinNodeByRoot = avlTmm.getLeftMost(); + expect(getMinNodeByRoot).toBe(1); + + const node15 = avlTmm.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTmm.getLeftMost(node => node, node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + if (node15) avlTmm.dfs(node => (subTreeSum += node.key), 'PRE', node15); + expect(subTreeSum).toBe(70); + + let lesserSum = 0; + avlTmm.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); + + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(avlTmm.get(node15)).toEqual([15]); + }); +}); + +describe('AVLTreeMultiMap iterative methods not map mode', () => { + let avlTmm: AVLTreeMultiMap; beforeEach(() => { - tm = new AVLTreeMultiMap([], { isMapMode: false }); + avlTmm = new AVLTreeMultiMap([]); + avlTmm.add([1, ['a']]); + avlTmm.add([2, ['b']]); + avlTmm.add([3, ['c']]); }); - it('Should added isolated node count ', () => { - tm.addMany([ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5] - ]); - const newNode = new AVLTreeMultiMapNode(3, undefined, 10); - tm.add(newNode, 33); - expect(tm.count).toBe(15); - }); -}); - -describe('AVLTreeMultiMap not map mode operations test1', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const treeMultimap = new AVLTreeMultiMap([], { isMapMode: false }); - - expect(treeMultimap instanceof AVLTreeMultiMap); - treeMultimap.add([11, 11]); - treeMultimap.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - treeMultimap.addMany(idAndValues); - expect(treeMultimap.root instanceof AVLTreeMultiMapNode); - - if (treeMultimap.root) expect(treeMultimap.root.key == 11); - - expect(treeMultimap.size).toBe(16); - expect(treeMultimap.count).toBe(18); - - expect(treeMultimap.has(6)); - - expect(treeMultimap.getHeight(6)).toBe(4); - expect(treeMultimap.getDepth(6)).toBe(0); - const nodeId10 = treeMultimap.getNode(10); - expect(nodeId10?.key).toBe(10); - - const nodeVal9 = treeMultimap.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); - }); -}); - -describe('AVLTreeMultiMap not map mode operations test recursively1', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const treeMultimap = new AVLTreeMultiMap([], { - iterationType: 'RECURSIVE', - isMapMode: false - }); - - expect(treeMultimap instanceof AVLTreeMultiMap); - treeMultimap.add([11, 11]); - treeMultimap.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - treeMultimap.addMany(idAndValues); - expect(treeMultimap.root).toBeInstanceOf(AVLTreeMultiMapNode); - - if (treeMultimap.root) expect(treeMultimap.root.key).toBe(6); - - expect(treeMultimap.size).toBe(16); - expect(treeMultimap.count).toBe(18); - - expect(treeMultimap.has(6)); - - expect(treeMultimap.getHeight(6)).toBe(4); - expect(treeMultimap.getDepth(6)).toBe(0); - const nodeId10 = treeMultimap.getNode(10); - expect(nodeId10?.key).toBe(10); - - const nodeVal9 = treeMultimap.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); + + it('should clone work well', () => { + const cloned = avlTmm.clone(); + expect(cloned.root?.left?.key).toBe(1); + expect(cloned.get(cloned.root?.right?.key)).toEqual(['c']); }); }); 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 edb1120..57dc9ce 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -3,119 +3,119 @@ import { AVLTree, AVLTreeNode, BinaryTreeNode, BSTNode } from '../../../../src'; describe('AVL Tree Test', () => { it('should perform various operations on a AVL Tree', () => { const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - const tree = new AVLTree(); + const avlTree = new AVLTree(); - for (const i of arr) tree.add([i, i]); + for (const i of arr) avlTree.add([i, i]); - tree.add(null); - const node6 = tree.getNode(6); + avlTree.add(null); + const node6 = avlTree.getNode(6); - expect(node6 && tree.getHeight(node6)).toBe(3); - expect(node6 && tree.getDepth(node6)).toBe(1); + expect(node6 && avlTree.getHeight(node6)).toBe(3); + expect(node6 && avlTree.getDepth(node6)).toBe(1); - const getNodeById = tree.getNode(10); + const getNodeById = avlTree.getNode(10); expect(getNodeById?.key).toBe(10); - const getMinNodeByRoot = tree.getLeftMost(); + const getMinNodeByRoot = avlTree.getLeftMost(); expect(getMinNodeByRoot).toBe(1); - const node15 = tree.getNode(15); - const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15); + const node15 = avlTree.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15); expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); expect(subTreeSum).toBe(70); let lesserSum = 0; - tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); expect(lesserSum).toBe(45); // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. expect(node15?.value).toBe(undefined); - const dfs = tree.dfs(node => node, 'IN'); + const dfs = avlTree.dfs(node => node, 'IN'); expect(dfs[0].key).toBe(1); expect(dfs[dfs.length - 1].key).toBe(16); - tree.perfectlyBalance(); - const bfs = tree.bfs(node => node); - expect(tree.isPerfectlyBalanced()).toBe(true); + avlTree.perfectlyBalance(); + const bfs = avlTree.bfs(node => node); + expect(avlTree.isPerfectlyBalanced()).toBe(true); expect(bfs[0].key).toBe(8); expect(bfs[bfs.length - 1].key).toBe(16); - expect(tree.delete(tree.getNode(11))[0].deleted?.key).toBe(11); - expect(tree.isAVLBalanced()).toBe(true); - expect(node15 && tree.getHeight(node15)).toBe(2); + expect(avlTree.delete(avlTree.getNode(11))[0].deleted?.key).toBe(11); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(node15 && avlTree.getHeight(node15)).toBe(2); - expect(tree.delete(1)[0].deleted?.key).toBe(1); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(4); + expect(avlTree.delete(1)[0].deleted?.key).toBe(1); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(4); - expect(tree.delete(4)[0].deleted?.key).toBe(4); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(4); + expect(avlTree.delete(4)[0].deleted?.key).toBe(4); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(4); - expect(tree.delete(10)[0].deleted?.key).toBe(10); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(10)[0].deleted?.key).toBe(10); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(15)[0].deleted?.key).toBe(15); - expect(tree.isAVLBalanced()).toBe(true); + expect(avlTree.delete(15)[0].deleted?.key).toBe(15); + expect(avlTree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(5)[0].deleted?.key).toBe(5); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(5)[0].deleted?.key).toBe(5); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(13)[0].deleted?.key).toBe(13); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(13)[0].deleted?.key).toBe(13); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(3)[0].deleted?.key).toBe(3); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(3)[0].deleted?.key).toBe(3); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(8)[0].deleted?.key).toBe(8); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(8)[0].deleted?.key).toBe(8); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(6)[0].deleted?.key).toBe(6); - expect(tree.delete(6).length).toBe(0); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(2); + expect(avlTree.delete(6)[0].deleted?.key).toBe(6); + expect(avlTree.delete(6).length).toBe(0); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(2); - expect(tree.delete(7)[0].deleted?.key).toBe(7); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(2); + expect(avlTree.delete(7)[0].deleted?.key).toBe(7); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(2); - expect(tree.delete(9)[0].deleted?.key).toBe(9); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(2); - expect(tree.delete(14)[0].deleted?.key).toBe(14); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(1); + expect(avlTree.delete(9)[0].deleted?.key).toBe(9); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(2); + expect(avlTree.delete(14)[0].deleted?.key).toBe(14); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(1); - expect(tree.isAVLBalanced()).toBe(true); - const lastBFSIds = tree.bfs(); + expect(avlTree.isAVLBalanced()).toBe(true); + const lastBFSIds = avlTree.bfs(); expect(lastBFSIds[0]).toBe(12); expect(lastBFSIds[1]).toBe(2); expect(lastBFSIds[2]).toBe(16); - const lastBFSNodes = tree.bfs(node => node); + const lastBFSNodes = avlTree.bfs(node => node); expect(lastBFSNodes[0].key).toBe(12); expect(lastBFSNodes[1].key).toBe(2); expect(lastBFSNodes[2].key).toBe(16); }); it('should replace value', () => { - const tree = new AVLTree([4, 5, [1, '1'], 2, 3], { isMapMode: false }); - expect(tree.get(1)).toBe('1'); - expect(tree.getNode(1)?.value).toBe('1'); - tree.add(1, 'a'); - expect(tree.get(1)).toBe('a'); - tree.add([1, 'b']); - expect(tree.getNode(1)?.value).toBe('b'); - expect(tree.get(1)).toBe('b'); + const avlTree = new AVLTree([4, 5, [1, '1'], 2, 3], { isMapMode: false }); + expect(avlTree.get(1)).toBe('1'); + expect(avlTree.getNode(1)?.value).toBe('1'); + avlTree.add(1, 'a'); + expect(avlTree.get(1)).toBe('a'); + avlTree.add([1, 'b']); + expect(avlTree.getNode(1)?.value).toBe('b'); + expect(avlTree.get(1)).toBe('b'); const treeMap = new AVLTree([4, 5, [1, '1'], 2, 3]); expect(treeMap.get(1)).toBe('1'); expect(treeMap.getNode(1)?.value).toBe(undefined); @@ -130,106 +130,106 @@ describe('AVL Tree Test', () => { describe('AVL Tree Test recursively', () => { it('should perform various operations on a AVL Tree', () => { const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - const tree = new AVLTree([], { iterationType: 'RECURSIVE' }); + const avlTree = new AVLTree([], { iterationType: 'RECURSIVE' }); - for (const i of arr) tree.add([i, i]); + for (const i of arr) avlTree.add([i, i]); - const node6 = tree.getNode(6); + const node6 = avlTree.getNode(6); - expect(node6 && tree.getHeight(node6)).toBe(3); - expect(node6 && tree.getDepth(node6)).toBe(1); + expect(node6 && avlTree.getHeight(node6)).toBe(3); + expect(node6 && avlTree.getDepth(node6)).toBe(1); - const getNodeById = tree.getNode(10); + const getNodeById = avlTree.getNode(10); expect(getNodeById?.key).toBe(10); - const getMinNodeByRoot = tree.getLeftMost(); + const getMinNodeByRoot = avlTree.getLeftMost(); expect(getMinNodeByRoot).toBe(1); - const node15 = tree.getNode(15); - const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15); + const node15 = avlTree.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15); expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); expect(subTreeSum).toBe(70); let lesserSum = 0; - tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); expect(lesserSum).toBe(45); // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. expect(node15?.value).toBe(undefined); - const dfs = tree.dfs(node => node, 'IN'); + const dfs = avlTree.dfs(node => node, 'IN'); expect(dfs[0].key).toBe(1); expect(dfs[dfs.length - 1].key).toBe(16); - tree.perfectlyBalance(); - const bfs = tree.bfs(node => node); - expect(tree.isPerfectlyBalanced()).toBe(true); + avlTree.perfectlyBalance(); + const bfs = avlTree.bfs(node => node); + expect(avlTree.isPerfectlyBalanced()).toBe(true); expect(bfs[0].key).toBe(8); expect(bfs[bfs.length - 1].key).toBe(16); - expect(tree.delete(11)[0].deleted?.key).toBe(11); - expect(tree.isAVLBalanced()).toBe(true); - expect(node15 && tree.getHeight(node15)).toBe(2); + expect(avlTree.delete(11)[0].deleted?.key).toBe(11); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(node15 && avlTree.getHeight(node15)).toBe(2); - expect(tree.delete(1)[0].deleted?.key).toBe(1); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(4); + expect(avlTree.delete(1)[0].deleted?.key).toBe(1); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(4); - expect(tree.delete(4)[0].deleted?.key).toBe(4); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(4); + expect(avlTree.delete(4)[0].deleted?.key).toBe(4); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(4); - expect(tree.delete(10)[0].deleted?.key).toBe(10); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(10)[0].deleted?.key).toBe(10); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(15)[0].deleted?.key).toBe(15); - expect(tree.isAVLBalanced()).toBe(true); + expect(avlTree.delete(15)[0].deleted?.key).toBe(15); + expect(avlTree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(5)[0].deleted?.key).toBe(5); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(5)[0].deleted?.key).toBe(5); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(13)[0].deleted?.key).toBe(13); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(13)[0].deleted?.key).toBe(13); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(3)[0].deleted?.key).toBe(3); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(3)[0].deleted?.key).toBe(3); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(8)[0].deleted?.key).toBe(8); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(avlTree.delete(8)[0].deleted?.key).toBe(8); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(3); - expect(tree.delete(6)[0].deleted?.key).toBe(6); - expect(tree.delete(6).length).toBe(0); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(2); + expect(avlTree.delete(6)[0].deleted?.key).toBe(6); + expect(avlTree.delete(6).length).toBe(0); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(2); - expect(tree.delete(7)[0].deleted?.key).toBe(7); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(2); + expect(avlTree.delete(7)[0].deleted?.key).toBe(7); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(2); - expect(tree.delete(9)[0].deleted?.key).toBe(9); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(2); - expect(tree.delete(14)[0].deleted?.key).toBe(14); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(1); + expect(avlTree.delete(9)[0].deleted?.key).toBe(9); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(2); + expect(avlTree.delete(14)[0].deleted?.key).toBe(14); + expect(avlTree.isAVLBalanced()).toBe(true); + expect(avlTree.getHeight()).toBe(1); - expect(tree.isAVLBalanced()).toBe(true); - const lastBFSIds = tree.bfs(); + expect(avlTree.isAVLBalanced()).toBe(true); + const lastBFSIds = avlTree.bfs(); expect(lastBFSIds[0]).toBe(12); expect(lastBFSIds[1]).toBe(2); expect(lastBFSIds[2]).toBe(16); - const lastBFSNodes = tree.bfs(node => node); + const lastBFSNodes = avlTree.bfs(node => node); expect(lastBFSNodes[0].key).toBe(12); expect(lastBFSNodes[1].key).toBe(2); expect(lastBFSNodes[2].key).toBe(16); @@ -237,66 +237,66 @@ describe('AVL Tree Test recursively', () => { }); describe('AVLTree APIs test', () => { - const avl = new AVLTree(); + const avlTree = new AVLTree(); beforeEach(() => { - avl.clear(); + avlTree.clear(); }); it('add', () => { - avl.add(1); + avlTree.add(1); const node2 = new AVLTreeNode(2); - avl.add(node2); + avlTree.add(node2); const node3 = new AVLTreeNode(3, { id: 3, text: 'text3' }); - avl.add(node3); - avl.add([3, { id: 3, text: 'text33' }]); + avlTree.add(node3); + avlTree.add([3, { id: 3, text: 'text33' }]); - const bfsRes = avl.bfs(node => node.key); + const bfsRes = avlTree.bfs(node => node.key); expect(bfsRes[0]).toBe(2); }); it('should the clone method', () => { - function checkTreeStructure(avl: AVLTree) { - expect(avl.size).toBe(4); - expect(avl.root?.key).toBe('2'); - expect(avl.root?.left?.key).toBe('1'); - expect(avl.root?.left?.left?.key).toBe(undefined); - expect(avl.root?.left?.right?.key).toBe(undefined); - expect(avl.root?.right?.key).toBe('4'); - expect(avl.root?.right?.left?.key).toBe(undefined); - expect(avl.root?.right?.right?.key).toBe('5'); + function checkTreeStructure(avlTree: AVLTree) { + expect(avlTree.size).toBe(4); + expect(avlTree.root?.key).toBe('2'); + expect(avlTree.root?.left?.key).toBe('1'); + expect(avlTree.root?.left?.left?.key).toBe(undefined); + expect(avlTree.root?.left?.right?.key).toBe(undefined); + expect(avlTree.root?.right?.key).toBe('4'); + expect(avlTree.root?.right?.left?.key).toBe(undefined); + expect(avlTree.root?.right?.right?.key).toBe('5'); } - const avl = new AVLTree(); - avl.addMany([ + const avlTree = new AVLTree(); + avlTree.addMany([ ['2', 2], ['4', 4], ['5', 5], ['3', 3], ['1', 1] ]); - expect(avl.size).toBe(5); - expect(avl.root?.key).toBe('2'); - expect(avl.root?.left?.key).toBe('1'); - expect(avl.root?.left?.left?.key).toBe(undefined); - expect(avl.root?.left?.right?.key).toBe(undefined); - expect(avl.root?.right?.key).toBe('4'); - expect(avl.root?.right?.left?.key).toBe('3'); - expect(avl.root?.right?.right?.key).toBe('5'); - avl.delete('3'); - checkTreeStructure(avl); - const cloned = avl.clone(); + expect(avlTree.size).toBe(5); + expect(avlTree.root?.key).toBe('2'); + expect(avlTree.root?.left?.key).toBe('1'); + expect(avlTree.root?.left?.left?.key).toBe(undefined); + expect(avlTree.root?.left?.right?.key).toBe(undefined); + expect(avlTree.root?.right?.key).toBe('4'); + expect(avlTree.root?.right?.left?.key).toBe('3'); + expect(avlTree.root?.right?.right?.key).toBe('5'); + avlTree.delete('3'); + checkTreeStructure(avlTree); + const cloned = avlTree.clone(); checkTreeStructure(cloned); cloned.delete('1'); - expect(avl.size).toBe(4); + expect(avlTree.size).toBe(4); expect(cloned.size).toBe(3); }); }); describe('AVLTree', () => { - it('should balance the tree using _balanceLR when nodes are added', () => { + it('should balance the avlTree using _balanceLR when nodes are added', () => { const avlTree = new AVLTree(); avlTree.add([10, 'A']); avlTree.add([5, 'B']); @@ -307,30 +307,30 @@ describe('AVLTree', () => { // Adding nodes to trigger _balanceLR avlTree.add([12, 'F']); - // You can add more specific assertions to check the tree's balance and structure. + // You can add more specific assertions to check the avlTree's balance and structure. }); it('should addMany undefined and null', () => { - const avl = new AVLTree(); - const addManyWithUndefined = avl.addMany([1, undefined, 3]); + const avlTree = new AVLTree(); + const addManyWithUndefined = avlTree.addMany([1, undefined, 3]); expect(addManyWithUndefined).toEqual([true, false, true]); - expect(avl.get(undefined)).toBe(undefined); - const addManyWithNull = avl.addMany([1, null, 3, 4]); + expect(avlTree.get(undefined)).toBe(undefined); + const addManyWithNull = avlTree.addMany([1, null, 3, 4]); expect(addManyWithNull).toEqual([true, false, true, true]); - const addManyEntriesWithNull = avl.addMany([ + const addManyEntriesWithNull = avlTree.addMany([ [1, '1'], [null, 'null'], [3, '3'], [4, '4'] ]); expect(addManyEntriesWithNull).toEqual([true, false, true, true]); - expect(avl.get(null)).toBe(undefined); - const node0 = avl.add(0, '0'); + expect(avlTree.get(null)).toBe(undefined); + const node0 = avlTree.add(0, '0'); expect(node0).toBe(true); - expect(avl.get(0)).toBe('0'); + expect(avlTree.get(0)).toBe('0'); }); - it('should balance the tree using _balanceLR when nodes are deleted', () => { + it('should balance the avlTree using _balanceLR when nodes are deleted', () => { const avlTree = new AVLTree(); avlTree.add([10, 'A']); avlTree.add([5, 'B']); @@ -342,43 +342,43 @@ describe('AVLTree', () => { // Deleting nodes to trigger _balanceLR avlTree.delete(3); - // You can add more specific assertions to check the tree's balance and structure. + // You can add more specific assertions to check the avlTree's balance and structure. }); - describe('BinaryTree APIs test', () => { - const avl = new AVLTree(); + describe('AVLTree APIs test', () => { + const avlTree = new AVLTree(); beforeEach(() => { - avl.clear(); + avlTree.clear(); }); it('add', () => { - avl.add(1); + avlTree.add(1); const node2 = new AVLTreeNode(2); - avl.add(node2); + avlTree.add(node2); const node3 = new AVLTreeNode(3, { id: 3, text: 'text3' }); - avl.add(node3); - avl.add([3, { id: 3, text: 'text33' }]); + avlTree.add(node3); + avlTree.add([3, { id: 3, text: 'text33' }]); - const bfsRes = avl.bfs(node => node); + const bfsRes = avlTree.bfs(node => node); expect(bfsRes[0]?.key).toBe(2); }); }); }); describe('AVLTree iterative methods test', () => { - let avl: AVLTree; + let avlTree: AVLTree; beforeEach(() => { - avl = new AVLTree(); - avl.add([1, 'a']); - avl.add([2, 'b']); - avl.add([3, 'c']); + avlTree = new AVLTree(); + avlTree.add([1, 'a']); + avlTree.add([2, 'b']); + avlTree.add([3, 'c']); }); it('The node obtained by get Node should match the node type', () => { - const node3 = avl.getNode(3); + const node3 = avlTree.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); expect(node3).toBeInstanceOf(BSTNode); expect(node3).toBeInstanceOf(AVLTreeNode); @@ -386,7 +386,7 @@ describe('AVLTree iterative methods test', () => { it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); - avl.forEach((key, value) => { + avlTree.forEach((key, value) => { mockCallback(key, value); }); @@ -396,8 +396,8 @@ describe('AVLTree iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual([3, 'c']); }); - it('filter should return a new tree with filtered elements', () => { - const filteredTree = avl.filter(key => key > 1); + it('filter should return a new avlTree with filtered elements', () => { + const filteredTree = avlTree.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], @@ -405,8 +405,8 @@ describe('AVLTree iterative methods test', () => { ]); }); - it('map should return a new tree with modified elements', () => { - const mappedTree = avl.map((key, value) => [(key * 2).toString(), value]); + it('map should return a new avlTree with modified elements', () => { + const mappedTree = avlTree.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ ['2', 'a'], @@ -416,13 +416,13 @@ describe('AVLTree iterative methods test', () => { }); it('reduce should accumulate values', () => { - const sum = avl.reduce((acc, value, key) => acc + key, 0); + const sum = avlTree.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); it('[Symbol.iterator] should provide an iterator', () => { const entries = []; - for (const entry of avl) { + for (const entry of avlTree) { entries.push(entry); } @@ -435,23 +435,23 @@ describe('AVLTree iterative methods test', () => { }); it('should clone work well', () => { - const cloned = avl.clone(); + const cloned = avlTree.clone(); expect(cloned.root?.left?.key).toBe(1); expect(cloned.root?.right?.value).toBe(undefined); }); it('should keys', () => { - const keys = avl.keys(); + const keys = avlTree.keys(); expect([...keys]).toEqual([1, 2, 3]); }); it('should values', () => { - const values = avl.values(); + const values = avlTree.values(); expect([...values]).toEqual(['a', 'b', 'c']); }); it('should leaves', () => { - const leaves = avl.leaves(); + const leaves = avlTree.leaves(); expect(leaves).toEqual([1, 3]); }); }); @@ -459,85 +459,85 @@ describe('AVLTree iterative methods test', () => { describe('AVL Tree not map mode', () => { it('should perform various operations on a AVL Tree', () => { const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - const tree = new AVLTree([], { isMapMode: false }); + const avlTree = new AVLTree([], { isMapMode: false }); - for (const i of arr) tree.add([i, i]); + for (const i of arr) avlTree.add([i, i]); - tree.add(null); - const node6 = tree.getNode(6); + avlTree.add(null); + const node6 = avlTree.getNode(6); - expect(node6 && tree.getHeight(node6)).toBe(3); - expect(node6 && tree.getDepth(node6)).toBe(1); + expect(node6 && avlTree.getHeight(node6)).toBe(3); + expect(node6 && avlTree.getDepth(node6)).toBe(1); - const getNodeById = tree.getNode(10); + const getNodeById = avlTree.getNode(10); expect(getNodeById?.key).toBe(10); - const getMinNodeByRoot = tree.getLeftMost(); + const getMinNodeByRoot = avlTree.getLeftMost(); expect(getMinNodeByRoot).toBe(1); - const node15 = tree.getNode(15); - const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15); + const node15 = avlTree.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15); expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); expect(subTreeSum).toBe(70); let lesserSum = 0; - tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); expect(lesserSum).toBe(45); // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. - expect(tree.get(node15)).toBe(15); + expect(avlTree.get(node15)).toBe(15); }); }); describe('AVL Tree not map mode test recursively', () => { it('should perform various operations on a AVL Tree', () => { const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - const tree = new AVLTree([], { iterationType: 'RECURSIVE', isMapMode: false }); + const avlTree = new AVLTree([], { iterationType: 'RECURSIVE', isMapMode: false }); - for (const i of arr) tree.add([i, i]); + for (const i of arr) avlTree.add([i, i]); - const node6 = tree.getNode(6); + const node6 = avlTree.getNode(6); - expect(node6 && tree.getHeight(node6)).toBe(3); - expect(node6 && tree.getDepth(node6)).toBe(1); + expect(node6 && avlTree.getHeight(node6)).toBe(3); + expect(node6 && avlTree.getDepth(node6)).toBe(1); - const getNodeById = tree.getNode(10); + const getNodeById = avlTree.getNode(10); expect(getNodeById?.key).toBe(10); - const getMinNodeByRoot = tree.getLeftMost(); + const getMinNodeByRoot = avlTree.getLeftMost(); expect(getMinNodeByRoot).toBe(1); - const node15 = tree.getNode(15); - const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15); + const node15 = avlTree.getNode(15); + const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15); expect(getMinNodeBySpecificNode?.key).toBe(12); let subTreeSum = 0; - if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15); + if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15); expect(subTreeSum).toBe(70); let lesserSum = 0; - tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); expect(lesserSum).toBe(45); // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. - expect(tree.get(node15)).toBe(15); + expect(avlTree.get(node15)).toBe(15); }); }); describe('AVLTree iterative methods not map mode', () => { - let avl: AVLTree; + let avlTree: AVLTree; beforeEach(() => { - avl = new AVLTree([], { isMapMode: false }); - avl.add([1, 'a']); - avl.add([2, 'b']); - avl.add([3, 'c']); + avlTree = new AVLTree([], { isMapMode: false }); + avlTree.add([1, 'a']); + avlTree.add([2, 'b']); + avlTree.add([3, 'c']); }); it('should clone work well', () => { - const cloned = avl.clone(); + const cloned = avlTree.clone(); expect(cloned.root?.left?.key).toBe(1); expect(cloned.get(cloned.root?.right?.key)).toBe('c'); }); diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index 6b7e784..382ea30 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -90,10 +90,10 @@ describe('BinaryTreeNode', () => { describe('BinaryTree addMany', () => { it('should addMany', () => { - const tree = new BinaryTree([], { + const binTree = new BinaryTree([], { toEntryFn: ({ id, name }) => [id, name] }); - tree.addMany( + binTree.addMany( [ { id: 1, name: 1 }, { id: 2, name: 2 }, @@ -102,10 +102,10 @@ describe('BinaryTree addMany', () => { ], [undefined, 22, 44, 33] ); - expect(tree.get(2)).toBe(22); - expect(tree.get(tree.getNode(3))).toBe(33); - expect(tree.get(tree.getNode(4))).toBe(44); - expect(tree.get(tree.getNode(1))).toBe(1); + expect(binTree.get(2)).toBe(22); + expect(binTree.get(binTree.getNode(3))).toBe(33); + expect(binTree.get(binTree.getNode(4))).toBe(44); + expect(binTree.get(binTree.getNode(1))).toBe(1); }); it('should addMany undefined and null', () => { @@ -123,7 +123,9 @@ describe('BinaryTree addMany', () => { ]); expect(addManyEntriesWithNull).toEqual([true, true, true, true]); expect(binaryTree.get(null)).toBe(undefined); - expect(binaryTree.getNode(null)).toBe(null); + expect(binaryTree.getNode(null)).toBe(undefined); + // // TODO should be null instead of undefined + // expect(binaryTree.getNode(null)).toBe(null); const node0 = binaryTree.add(0, '0'); expect(node0).toBe(true); expect(binaryTree.get(0)).toBe('0'); @@ -131,83 +133,83 @@ describe('BinaryTree addMany', () => { }); describe('BinaryTree', () => { - let tree: BinaryTree; + let binTree: BinaryTree; beforeEach(() => { - tree = new BinaryTree(); + binTree = new BinaryTree(); }); afterEach(() => { - tree.clear(); + binTree.clear(); }); it('should add a node', () => { - const node = tree.add(1); + const node = binTree.add(1); expect(node).not.toBeNull(); - expect(tree.size).toBe(1); + expect(binTree.size).toBe(1); }); it('should delete nodes', () => { - expect(tree.getHeight(tree.root, 'ITERATIVE')).toBe(-1); - expect(tree.getMinHeight()).toBe(-1); - const node1 = tree.createNode(1); - tree.add(node1); - expect(tree.size).toBe(1); + expect(binTree.getHeight(binTree.root, 'ITERATIVE')).toBe(-1); + expect(binTree.getMinHeight()).toBe(-1); + const node1 = binTree.createNode(1); + binTree.add(node1); + expect(binTree.size).toBe(1); const leftChild = new BinaryTreeNode(2); const rightChild = new BinaryTreeNode(3); - tree.add(leftChild); - tree.add(rightChild); - const root = tree.root; + binTree.add(leftChild); + binTree.add(rightChild); + const root = binTree.root; expect(leftChild.familyPosition).toBe('LEFT'); - tree.add(null); - tree.add(new BinaryTreeNode(4)); + binTree.add(null); + binTree.add(new BinaryTreeNode(4)); expect(rightChild.familyPosition).toBe('RIGHT'); expect(root?.familyPosition).toBe('ROOT'); expect(leftChild.familyPosition).toBe('ROOT_LEFT'); - tree.add(new BinaryTreeNode(5)); + binTree.add(new BinaryTreeNode(5)); expect(rightChild.familyPosition).toBe('ROOT_RIGHT'); - tree.delete(new BinaryTreeNode(200)); - tree.delete(rightChild); + binTree.delete(new BinaryTreeNode(200)); + binTree.delete(rightChild); if (node1) { - const result = tree.delete(node1); + const result = binTree.delete(node1); expect(result).toHaveLength(1); - expect(tree.size).toBe(4); - expect(tree.getMinHeight(tree.root, 'RECURSIVE')).toBe(1); + expect(binTree.size).toBe(4); + expect(binTree.getMinHeight(binTree.root, 'RECURSIVE')).toBe(1); } }); it('should add and find nodes', () => { - tree.add([1, 1]); - tree.add(undefined); - tree.add([2, 2]); - tree.add([3, 3]); + binTree.add([1, 1]); + binTree.add(undefined); + binTree.add([2, 2]); + binTree.add([3, 3]); - expect(tree.has(1)).toBe(true); - expect(tree.has(2)).toBe(true); - expect(tree.has(3)).toBe(true); - expect(tree.has(4)).toBe(false); - const node4 = tree.getNode(4); - expect(tree.has(node4)).toBe(false); - expect(tree.has(node => node === node4)).toBe(false); - expect(tree.has(node => node.key?.toString() === '3')).toBe(true); + expect(binTree.has(1)).toBe(true); + expect(binTree.has(2)).toBe(true); + expect(binTree.has(3)).toBe(true); + expect(binTree.has(4)).toBe(false); + const node4 = binTree.getNode(4); + expect(binTree.has(node4)).toBe(false); + expect(binTree.has(node => node === node4)).toBe(false); + expect(binTree.has(node => node.key?.toString() === '3')).toBe(true); }); it('should the clone method work fine', () => { - expect(tree.isEmpty()).toBe(true); - tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); - expect(tree.root?.key).toBe(4); - expect(tree.root?.left?.key).toBe(2); - expect(tree.root?.left?.left).toBe(null); - expect(tree.root?.left?.right?.key).toBe(1); - expect(tree.root?.right?.key).toBe(6); - expect(tree.root?.right?.left?.key).toBe(3); - expect(tree.root?.right?.right).toBe(null); + expect(binTree.isEmpty()).toBe(true); + binTree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); + expect(binTree.root?.key).toBe(4); + expect(binTree.root?.left?.key).toBe(2); + expect(binTree.root?.left?.left).toBe(null); + expect(binTree.root?.left?.right?.key).toBe(1); + expect(binTree.root?.right?.key).toBe(6); + expect(binTree.root?.right?.left?.key).toBe(3); + expect(binTree.root?.right?.right).toBe(null); - const cloned = tree.clone(); + const cloned = binTree.clone(); expect(cloned.root?.key).toBe(4); expect(cloned.root?.left?.key).toBe(2); expect(cloned.root?.left?.left).toBe(null); @@ -244,66 +246,66 @@ describe('BinaryTree', () => { // cloned.delete(null); // cloned.delete(null); // cloned.delete(null); - expect(tree.size).toBe(10); + expect(binTree.size).toBe(10); expect(cloned.size).toBe(3); // expect(cloned.size).toBe(0); // expect(cloned.isEmpty()).toBe(true); }); - it('should be a balance tree after malicious manipulation', () => { - tree.add(3); - tree.add(12); - tree.addMany(getRandomIntArray(100, 1, 100)); - tree.add(10); + it('should be a balance binTree after malicious manipulation', () => { + binTree.add(3); + binTree.add(12); + binTree.addMany(getRandomIntArray(100, 1, 100)); + binTree.add(10); - expect(tree.isPerfectlyBalanced()).toBe(true); - const node3 = tree.getNode(3); + expect(binTree.isPerfectlyBalanced()).toBe(true); + const node3 = binTree.getNode(3); - if (node3) node3.right = tree.createNode(1); - expect(tree.isPerfectlyBalanced()).toBe(false); + if (node3) node3.right = binTree.createNode(1); + expect(binTree.isPerfectlyBalanced()).toBe(false); - tree.clear(); - tree.addMany([1, null, 2, null, 3, null, 4, null, 5, null, 6, null]); - expect(tree.isPerfectlyBalanced()).toBe(false); + binTree.clear(); + binTree.addMany([1, null, 2, null, 3, null, 4, null, 5, null, 6, null]); + expect(binTree.isPerfectlyBalanced()).toBe(false); }); it('should getDepth return correct depth', () => { - tree.add(1); - expect(tree.getDepth(1)).toBe(0); - tree.add(2); - expect(tree.getDepth(2)).toBe(1); - tree.add(3); - expect(tree.getDepth(3, 1)).toBe(1); - tree.add(4); - expect(tree.getDepth(4, 1)).toBe(2); - expect(tree.getDepth(4)).toBe(2); - expect(tree.getDepth(4, 2)).toBe(1); + binTree.add(1); + expect(binTree.getDepth(1)).toBe(0); + binTree.add(2); + expect(binTree.getDepth(2)).toBe(1); + binTree.add(3); + expect(binTree.getDepth(3, 1)).toBe(1); + binTree.add(4); + expect(binTree.getDepth(4, 1)).toBe(2); + expect(binTree.getDepth(4)).toBe(2); + expect(binTree.getDepth(4, 2)).toBe(1); }); it('should traverse in-order', () => { - tree.add(null); - tree.delete(1); - expect(tree.getHeight()).toBe(-1); - tree.add(4); - tree.add(2); - expect(tree.getHeight()).toBe(1); - tree.iterationType = 'RECURSIVE'; - expect(tree.getHeight()).toBe(1); - tree.iterationType = 'ITERATIVE'; + binTree.add(null); + binTree.delete(1); + expect(binTree.getHeight()).toBe(-1); + binTree.add(4); + binTree.add(2); + expect(binTree.getHeight()).toBe(1); + binTree.iterationType = 'RECURSIVE'; + expect(binTree.getHeight()).toBe(1); + binTree.iterationType = 'ITERATIVE'; - tree.add(6); - tree.add(1); - tree.add(new BinaryTreeNode(3)); - tree.add(5); - tree.add(7); + binTree.add(6); + binTree.add(1); + binTree.add(new BinaryTreeNode(3)); + binTree.add(5); + binTree.add(7); - const inOrder = tree.dfs(node => node.key); + const inOrder = binTree.dfs(node => node.key); expect(inOrder).toEqual([1, 2, 3, 4, 5, 6, 7]); }); it('should isSubtreeBST', () => { - tree.addMany([ + binTree.addMany([ new BinaryTreeNode(4, 4), new BinaryTreeNode(2, 2), new BinaryTreeNode(6, 6), @@ -314,14 +316,14 @@ describe('BinaryTree', () => { new BinaryTreeNode(4, 4) ]); - expect(tree.isBST(tree.getNode(4), 'RECURSIVE')).toBe(true); - expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true); + expect(binTree.isBST(binTree.getNode(4), 'RECURSIVE')).toBe(true); + expect(binTree.isBST(binTree.getNode(4), 'ITERATIVE')).toBe(true); }); it('should isSubtreeBST', () => { - expect(tree.toVisual()).toBe(''); - tree.addMany([4, 2, 6, 1, 3, 5, 7, 4]); - expect(tree.toVisual()).toBe( + expect(binTree.toVisual()).toBe(''); + binTree.addMany([4, 2, 6, 1, 3, 5, 7, 4]); + expect(binTree.toVisual()).toBe( 'N for null\n' + ' ___4___ \n' + ' / \\ \n' + @@ -330,7 +332,11 @@ describe('BinaryTree', () => { ' 1 3 5 7 \n' + ' \n' ); - const visualized = tree.toVisual(undefined, { isShowUndefined: true, isShowNull: true, isShowRedBlackNIL: true }); + const visualized = binTree.toVisual(undefined, { + isShowUndefined: true, + isShowNull: true, + isShowRedBlackNIL: true + }); expect(visualized).toBe( 'U for undefined\n' + 'N for null\n' + @@ -345,97 +351,97 @@ describe('BinaryTree', () => { ' \n' ); - expect(tree.isBST(tree.getNode(4), 'RECURSIVE')).toBe(true); - expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true); - expect(tree.getNodes(2, false, null)).toEqual([]); - expect(tree.getNodes(undefined)).toEqual([]); - expect(tree.getNodes(tree.getNode(2), false, tree.root)).toEqual([tree.getNode(2)]); + expect(binTree.isBST(binTree.getNode(4), 'RECURSIVE')).toBe(true); + expect(binTree.isBST(binTree.getNode(4), 'ITERATIVE')).toBe(true); + expect(binTree.getNodes(2, false, null)).toEqual([]); + expect(binTree.getNodes(undefined)).toEqual([]); + expect(binTree.getNodes(binTree.getNode(2), false, binTree.root)).toEqual([binTree.getNode(2)]); }); - describe('should isKey', () => { + describe('should isValidKey', () => { describe('primitive types', () => { it('numbers should be a key', () => { - expect(tree.isKey(42)).toBe(true); - expect(tree.isKey(0)).toBe(true); - expect(tree.isKey(-1)).toBe(true); - expect(tree.isKey(Infinity)).toBe(true); - expect(tree.isKey(-Infinity)).toBe(true); + expect(binTree.isValidKey(42)).toBe(true); + expect(binTree.isValidKey(0)).toBe(true); + expect(binTree.isValidKey(-1)).toBe(true); + expect(binTree.isValidKey(Infinity)).toBe(true); + expect(binTree.isValidKey(-Infinity)).toBe(true); }); // it('NaN should not be a key', () => { - // expect(tree.isKey(NaN)).toBe(false); + // expect(binTree.isValidKey(NaN)).toBe(false); // }); it('strings should be a key', () => { - expect(tree.isKey('hello')).toBe(true); - expect(tree.isKey('')).toBe(true); - expect(tree.isKey('123')).toBe(true); + expect(binTree.isValidKey('hello')).toBe(true); + expect(binTree.isValidKey('')).toBe(true); + expect(binTree.isValidKey('123')).toBe(true); }); it('BigInt should be a key', () => { - expect(tree.isKey(BigInt(42))).toBe(true); - expect(tree.isKey(BigInt(0))).toBe(true); - expect(tree.isKey(BigInt(-1))).toBe(true); + expect(binTree.isValidKey(BigInt(42))).toBe(true); + expect(binTree.isValidKey(BigInt(0))).toBe(true); + expect(binTree.isValidKey(BigInt(-1))).toBe(true); }); it('boolean should not be a key', () => { - expect(tree.isKey(true)).toBe(true); - expect(tree.isKey(false)).toBe(true); + expect(binTree.isValidKey(true)).toBe(true); + expect(binTree.isValidKey(false)).toBe(true); }); it('null and undefined should not be a key', () => { - expect(tree.isKey(null)).toBe(true); - expect(tree.isKey(undefined)).toBe(false); + expect(binTree.isValidKey(null)).toBe(true); + expect(binTree.isValidKey(undefined)).toBe(false); }); it('symbols should not be a key', () => { - expect(tree.isKey(Symbol('test'))).toBe(false); - expect(tree.isKey(Symbol.for('test'))).toBe(false); + expect(binTree.isValidKey(Symbol('test'))).toBe(false); + expect(binTree.isValidKey(Symbol.for('test'))).toBe(false); }); }); describe('Date objects', () => { it('valid Date objects should be a key', () => { - expect(tree.isKey(new Date())).toBe(true); - expect(tree.isKey(new Date('2024-01-01'))).toBe(true); + expect(binTree.isValidKey(new Date())).toBe(true); + expect(binTree.isValidKey(new Date('2024-01-01'))).toBe(true); }); // it('invalid Date objects should not be a key', () => { - // expect(tree.isKey(new Date('invalid'))).toBe(false); + // expect(binTree.isValidKey(new Date('invalid'))).toBe(false); // }); }); describe('arrays', () => { it('arrays should be a key as they convert to string', () => { - expect(tree.isKey([])).toBe(true); - expect(tree.isKey([1, 2, 3])).toBe(true); - expect(tree.isKey(['a', 'b', 'c'])).toBe(true); + expect(binTree.isValidKey([])).toBe(true); + expect(binTree.isValidKey([1, 2, 3])).toBe(true); + expect(binTree.isValidKey(['a', 'b', 'c'])).toBe(true); }); }); describe('plain objects', () => { it('plain objects should not be a key', () => { - expect(tree.isKey({})).toBe(false); - expect(tree.isKey({ a: 1 })).toBe(false); + expect(binTree.isValidKey({})).toBe(false); + expect(binTree.isValidKey({ a: 1 })).toBe(false); }); }); describe('custom objects', () => { it('objects with numeric valueOf should be a key', () => { - expect(tree.isKey({ valueOf: () => 42 })).toBe(true); + expect(binTree.isValidKey({ valueOf: () => 42 })).toBe(true); }); it('objects with string valueOf should be a key', () => { - expect(tree.isKey({ valueOf: () => 'test' })).toBe(true); + expect(binTree.isValidKey({ valueOf: () => 'test' })).toBe(true); }); it('objects with boolean valueOf should not be a key', () => { - expect(tree.isKey({ valueOf: () => true })).toBe(true); + expect(binTree.isValidKey({ valueOf: () => true })).toBe(true); }); it('objects with nested valueOf/toString should be a key', () => { expect( - tree.isKey({ + binTree.isValidKey({ valueOf: () => ({ toString: () => '42' }) }) ).toBe(true); @@ -449,7 +455,7 @@ describe('BinaryTree', () => { valueOf: () => 42 }) }; - expect(tree.isKey(deeplyNested)).toBe(true); + expect(binTree.isValidKey(deeplyNested)).toBe(true); }); it('objects with very deeply nested conversion should be a key', () => { @@ -460,14 +466,14 @@ describe('BinaryTree', () => { }) }) }; - expect(tree.isKey(veryDeeplyNested)).toBe(true); + expect(binTree.isValidKey(veryDeeplyNested)).toBe(true); }); it('objects with circular references should not be a key', () => { const circular: any = { valueOf: () => circular }; - expect(tree.isKey(circular)).toBe(false); + expect(binTree.isValidKey(circular)).toBe(false); }); }); @@ -480,7 +486,7 @@ describe('BinaryTree', () => { }) }) }; - expect(tree.isKey(complexObject)).toBe(false); + expect(binTree.isValidKey(complexObject)).toBe(false); }); it('objects returning primitive values should be handled correctly', () => { @@ -497,40 +503,40 @@ describe('BinaryTree', () => { }) }) }; - expect(tree.isKey(complexObject)).toBe(true); + expect(binTree.isValidKey(complexObject)).toBe(true); }); }); describe('type checking', () => { it('should work with type guard in array methods', () => { const values: unknown[] = [42, 'test', true, null, undefined, new Date()]; - const comparableValues = values.filter(item => tree.isKey(item)); + const comparableValues = values.filter(item => binTree.isValidKey(item)); expect(comparableValues.length).toBe(5); }); }); }); it('should isLeaf', () => { - expect(tree.getLeftMost()).toBe(undefined); - expect(tree.getRightMost()).toBe(undefined); - tree.addMany([4, 2, 6, 1, 3, 5, 7, 4]); - const leftMost = tree.getLeftMost(); - expect(tree.isLeaf(leftMost)).toBe(true); - expect(tree.isLeaf(null)).toBe(true); - const rightMost = tree.getRightMost(); - expect(tree.isLeaf(rightMost)).toBe(true); - expect(tree.isLeaf(null)).toBe(true); + expect(binTree.getLeftMost()).toBe(undefined); + expect(binTree.getRightMost()).toBe(undefined); + binTree.addMany([4, 2, 6, 1, 3, 5, 7, 4]); + const leftMost = binTree.getLeftMost(); + expect(binTree.isLeaf(leftMost)).toBe(true); + expect(binTree.isLeaf(null)).toBe(true); + const rightMost = binTree.getRightMost(); + expect(binTree.isLeaf(rightMost)).toBe(true); + expect(binTree.isLeaf(null)).toBe(true); }); - it('should tree traverse', () => { - expect(tree.dfs()).toEqual([]); - expect([...tree.values()]).toEqual([]); - tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); - expect(tree.dfs(node => node.key, 'PRE', undefined, 'ITERATIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', false)).toEqual([ + it('should binTree traverse', () => { + 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(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'ITERATIVE', true)).toEqual([ 4, 2, null, @@ -543,11 +549,11 @@ describe('BinaryTree', () => { null ]); - expect(tree.dfs(node => node.key, 'PRE', undefined, 'RECURSIVE')).toEqual([4, 2, 1, 5, 6, 3, 7]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', false)).toEqual([ + 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(tree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'PRE', undefined, 'RECURSIVE', true)).toEqual([ 4, 2, null, @@ -560,11 +566,11 @@ describe('BinaryTree', () => { null ]); - expect(tree.dfs(node => node.key, 'IN', undefined, 'ITERATIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', false)).toEqual([ + 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([ 2, 5, 1, 4, 7, 3, 6 ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'ITERATIVE', true)).toEqual([ null, 2, 5, @@ -577,11 +583,11 @@ describe('BinaryTree', () => { null ]); - expect(tree.dfs(node => node.key, 'IN', undefined, 'RECURSIVE')).toEqual([2, 5, 1, 4, 7, 3, 6]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', false)).toEqual([ + 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([ 2, 5, 1, 4, 7, 3, 6 ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'IN', undefined, 'RECURSIVE', true)).toEqual([ null, 2, 5, @@ -594,11 +600,11 @@ describe('BinaryTree', () => { null ]); - expect(tree.dfs(node => node.key, 'POST', undefined, 'ITERATIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', false)).toEqual([ + 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(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'ITERATIVE', true)).toEqual([ null, 5, null, @@ -611,11 +617,11 @@ describe('BinaryTree', () => { 4 ]); - expect(tree.dfs(node => node.key, 'POST', undefined, 'RECURSIVE')).toEqual([5, 1, 2, 7, 3, 6, 4]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', false)).toEqual([ + 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(tree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', true)).toEqual([ + expect(binTree.dfs(node => (node !== null ? node.key : null), 'POST', undefined, 'RECURSIVE', true)).toEqual([ null, 5, null, @@ -629,93 +635,75 @@ describe('BinaryTree', () => { ]); }); - it('should sub tree traverse', () => { - tree.addMany([4, 2, 6, null, 1, 3, null, 5, null, 7]); - expect(tree.dfs(node => node.key, 'PRE', tree.getNode(6), 'ITERATIVE')).toEqual([6, 3, 7]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'ITERATIVE', false)).toEqual([ - 6, 3, 7 - ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'ITERATIVE', true)).toEqual([ - 6, - 3, - 7, - null - ]); + 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 !== null ? node.key : null), 'PRE', binTree.getNode(6), 'ITERATIVE', false) + ).toEqual([6, 3, 7]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'PRE', binTree.getNode(6), 'ITERATIVE', true) + ).toEqual([6, 3, 7, null]); - expect(tree.dfs(node => node.key, 'PRE', tree.getNode(6), 'RECURSIVE')).toEqual([6, 3, 7]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'RECURSIVE', false)).toEqual([ - 6, 3, 7 - ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'PRE', tree.getNode(6), 'RECURSIVE', 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 !== null ? node.key : null), 'PRE', binTree.getNode(6), 'RECURSIVE', false) + ).toEqual([6, 3, 7]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'PRE', binTree.getNode(6), 'RECURSIVE', true) + ).toEqual([6, 3, 7, null]); - expect(tree.dfs(node => node.key, 'IN', tree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'ITERATIVE', false)).toEqual([ - 7, 3, 6 - ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'ITERATIVE', true)).toEqual([ - 7, - 3, - 6, - null - ]); + expect(binTree.dfs(node => node.key, 'IN', binTree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'IN', 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(tree.dfs(node => node.key, 'IN', tree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'RECURSIVE', false)).toEqual([ - 7, 3, 6 - ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'IN', tree.getNode(6), 'RECURSIVE', 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 !== null ? node.key : null), 'IN', 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(tree.dfs(node => node.key, 'POST', tree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'ITERATIVE', false)).toEqual([ - 7, 3, 6 - ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'ITERATIVE', true)).toEqual([ - 7, - 3, - null, - 6 - ]); + expect(binTree.dfs(node => node.key, 'POST', binTree.getNode(6), 'ITERATIVE')).toEqual([7, 3, 6]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'ITERATIVE', false) + ).toEqual([7, 3, 6]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'ITERATIVE', true) + ).toEqual([7, 3, null, 6]); - expect(tree.dfs(node => node.key, 'POST', tree.getNode(6), 'RECURSIVE')).toEqual([7, 3, 6]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'RECURSIVE', false)).toEqual([ - 7, 3, 6 - ]); - expect(tree.dfs(node => (node !== null ? node.key : null), 'POST', tree.getNode(6), 'RECURSIVE', 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 !== null ? node.key : null), 'POST', binTree.getNode(6), 'RECURSIVE', false) + ).toEqual([7, 3, 6]); + expect( + binTree.dfs(node => (node !== null ? node.key : null), 'POST', binTree.getNode(6), 'RECURSIVE', true) + ).toEqual([7, 3, null, 6]); }); - it('should clear the tree', () => { - tree.add(1); - tree.add(2); + it('should clear the binTree', () => { + binTree.add(1); + binTree.add(2); - expect(tree.size).toBe(2); + expect(binTree.size).toBe(2); - tree.clear(); + binTree.clear(); - expect(tree.size).toBe(0); - expect(tree.root).toBeUndefined(); + expect(binTree.size).toBe(0); + expect(binTree.root).toBeUndefined(); }); it('should duplicated nodes just replace the node exists', function () { - tree.clear(); - expect(tree.bfs()).toEqual([]); - tree.addMany([-10, -10, -10, 9, 9, 20, null, null, 15, 7, 8, null, 2, null, 6, null, null, 8, 8, 8]); + binTree.clear(); + expect(binTree.bfs()).toEqual([]); + binTree.addMany([-10, -10, -10, 9, 9, 20, null, null, 15, 7, 8, null, 2, null, 6, null, null, 8, 8, 8]); - expect(tree.bfs(node => (node ? node.key : null), undefined, undefined, true)).toEqual([ + expect(binTree.bfs(node => (node ? node.key : null), undefined, undefined, true)).toEqual([ -10, 9, 20, @@ -734,8 +722,8 @@ describe('BinaryTree', () => { }); // it('should keyValueNodeEntryRawToNodeAndValue', () => { - // const tree = new BinaryTree(); - // const node0 = tree.keyValueNodeEntryRawToNodeAndValue(0); + // const binTree = new BinaryTree(); + // const node0 = binTree.keyValueNodeEntryRawToNodeAndValue(0); // expect(node0).toEqual([ // { // _left: undefined, @@ -747,18 +735,18 @@ describe('BinaryTree', () => { // undefined // ]); // - // const nodeUndefined = tree.keyValueNodeEntryRawToNodeAndValue(undefined); + // const nodeUndefined = binTree.keyValueNodeEntryRawToNodeAndValue(undefined); // expect(nodeUndefined).toEqual([undefined, undefined]); // - // const nodeNull = tree.keyValueNodeEntryRawToNodeAndValue(null); + // const nodeNull = binTree.keyValueNodeEntryRawToNodeAndValue(null); // expect(nodeNull).toEqual([null, undefined]); // - // const [, nodeWithSeparateValue] = tree.keyValueNodeEntryRawToNodeAndValue(7, 77); + // const [, nodeWithSeparateValue] = binTree.keyValueNodeEntryRawToNodeAndValue(7, 77); // expect(nodeWithSeparateValue).toBe(77); // - // expect(tree.keyValueNodeEntryRawToNodeAndValue([undefined, 2])).toEqual([undefined, undefined]); + // expect(binTree.keyValueNodeEntryRawToNodeAndValue([undefined, 2])).toEqual([undefined, undefined]); // - // expect(tree.keyValueNodeEntryRawToNodeAndValue(Symbol('test') as unknown as number)).toEqual([ + // expect(binTree.keyValueNodeEntryRawToNodeAndValue(Symbol('test') as unknown as number)).toEqual([ // undefined, // undefined // ]); @@ -770,14 +758,14 @@ describe('BinaryTree', () => { // }); it('should replace value', () => { - const tree = new BinaryTree([4, 5, [1, '1'], 2, 3], { isMapMode: false }); - expect(tree.get(1)).toBe('1'); - expect(tree.getNode(1)?.value).toBe('1'); - tree.add(1, 'a'); - expect(tree.get(1)).toBe('a'); - tree.add([1, 'b']); - expect(tree.getNode(1)?.value).toBe('b'); - expect(tree.get(1)).toBe('b'); + const binTree = new BinaryTree([4, 5, [1, '1'], 2, 3], { isMapMode: false }); + expect(binTree.get(1)).toBe('1'); + expect(binTree.getNode(1)?.value).toBe('1'); + binTree.add(1, 'a'); + expect(binTree.get(1)).toBe('a'); + binTree.add([1, 'b']); + expect(binTree.getNode(1)?.value).toBe('b'); + expect(binTree.get(1)).toBe('b'); const treeMap = new BinaryTree([4, 5, [1, '1'], 2, 3]); expect(treeMap.get(1)).toBe('1'); expect(treeMap.getNode(1)?.value).toBe(undefined); @@ -791,7 +779,7 @@ describe('BinaryTree', () => { describe('BinaryTree ensureNode', () => { it('should ensureNode with toEntryFn', () => { - const tree = new BinaryTree< + const binTree = new BinaryTree< number, string, { @@ -799,62 +787,62 @@ describe('BinaryTree ensureNode', () => { name: string; } >([], { toEntryFn: rawElement => [rawElement.id, rawElement.name] }); - tree.add({ id: 1, name: 'Pablo' }); - const node = tree.getNode(1); - expect(tree.ensureNode({ id: 1, name: 'Pablo' })).toBe(node); - expect(tree.ensureNode([1, 'Pablo'])).toBe(node); - expect(tree.ensureNode([null, 'Pablo'])).toBe(null); - expect(tree.ensureNode([undefined, 'Pablo'])).toBe(undefined); - expect(tree.ensureNode(Symbol('test') as unknown as number)).toBe(undefined); + binTree.add([1, 'Pablo']); + const node = binTree.getNode(1); + // expect(binTree.ensureNode({ id: 1, name: 'Pablo' })).toBe(node); + expect(binTree.ensureNode([1, 'Pablo'])).toBe(node); + expect(binTree.ensureNode([null, 'Pablo'])).toBe(null); + expect(binTree.ensureNode([undefined, 'Pablo'])).toBe(undefined); + expect(binTree.ensureNode(Symbol('test') as unknown as number)).toBe(undefined); }); }); describe('BinaryTree Morris Traversal', () => { - // Create a binary tree - const tree = new BinaryTree(); - tree.add(1); - tree.add(2); - tree.add(3); - tree.add(4); - tree.add(5); + // Create a binary binTree + const binTree = new BinaryTree(); + binTree.add(1); + binTree.add(2); + binTree.add(3); + binTree.add(4); + binTree.add(5); it('should perform in-order Morris traversal correctly as dfs traversal', () => { // Perform in-order Morris traversal - const result = tree.morris(node => node.key, 'IN'); + const result = binTree.morris(node => node.key, 'IN'); // Expected in-order traversal result const expected = [4, 2, 5, 1, 3]; expect(result).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN')).toEqual(expected); - expect(tree.dfs(node => node.key, 'IN', tree.root, 'RECURSIVE')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'IN')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'IN', binTree.root, 'RECURSIVE')).toEqual(expected); }); it('should perform pre-order Morris traversal correctly as dfs traversal', () => { // Perform pre-order Morris traversal - const result = tree.morris(node => node.key, 'PRE'); + const result = binTree.morris(node => node.key, 'PRE'); // Expected pre-order traversal result const expected = [1, 2, 4, 5, 3]; expect(result).toEqual(expected); - expect(tree.dfs(node => node.key, 'PRE')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'PRE')).toEqual(expected); }); it('should perform post-order Morris traversal correctly as dfs traversal', () => { // Perform post-order Morris traversal - const result = tree.morris(node => node.key, 'POST'); + const result = binTree.morris(node => node.key, 'POST'); // Expected post-order traversal result const expected = [4, 5, 2, 3, 1]; expect(result).toEqual([4, 5, 2, 3, 1]); - expect(tree.dfs(node => node.key, 'POST')).toEqual(expected); + expect(binTree.dfs(node => node.key, 'POST')).toEqual(expected); }); - it('after morris traversals should the structure of the tree be correct', () => { - const node1 = tree.getNode(1); - const node2 = tree.getNode(2); - const node3 = tree.getNode(3); + it('after morris traversals should the structure of the binTree be correct', () => { + const node1 = binTree.getNode(1); + const node2 = binTree.getNode(2); + const node3 = binTree.getNode(3); expect(node1?.left).toBe(node2); expect(node1?.right).toBe(node3); }); @@ -875,11 +863,13 @@ describe('BinaryTree toEntryFn', () => { const binTree = new BinaryTree([], { toEntryFn: ele => [ele.obj.id, ele.obj.id] }); - binTree.add({ obj: { id: 1 } }); - binTree.add({ obj: { id: 2 } }); - binTree.add({ obj: { id: 3 } }); - binTree.add({ obj: { id: 4 } }); - binTree.add({ obj: { id: 5 } }); + binTree.addMany([ + { obj: { id: 1 } }, + { obj: { id: 2 } }, + { obj: { id: 3 } }, + { obj: { id: 4 } }, + { obj: { id: 5 } } + ]); const expected = [4, 2, 5, 1, 3]; @@ -911,21 +901,21 @@ describe('BinaryTree toEntryFn', () => { { obj: { id: 1 }, valueOf: () => 1 }, { obj: { id: 3 }, valueOf: () => 3 } ]; - const tree = new BinaryTree<{ obj: { id: number }; valueOf: () => number }, number>(data); + const binTree = new BinaryTree<{ obj: { id: number }; valueOf: () => number }, number>(data); - expect(tree.morris(node => node.key, 'IN')).toEqual(data.sort((a, b) => a.obj.id - b.obj.id)); - expect(tree.dfs(node => node.key, 'IN')).toEqual(data); - expect(tree.dfs(node => node.key, 'IN', tree.root, 'RECURSIVE')).toEqual(data); + 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); }); }); describe('BinaryTree traversals', () => { it('traversals', () => { - const tree = new BinaryTree(); + const binTree = new BinaryTree(); const arr = [35, 20, 40, 15, 29, null, 50, null, 16, 28, 30, 45, 55]; - tree.refill(arr); - expect(tree.bfs(node => node, tree.root, 'ITERATIVE', true).map(node => (node ? node.key : null))).toEqual([ + binTree.refill(arr); + expect(binTree.bfs(node => node, binTree.root, 'ITERATIVE', true).map(node => (node ? node.key : null))).toEqual([ 35, 20, 40, @@ -940,7 +930,7 @@ describe('BinaryTree traversals', () => { 45, 55 ]); - expect(tree.bfs(node => node, tree.root, 'RECURSIVE', true).map(node => (node ? node.key : null))).toEqual([ + expect(binTree.bfs(node => node, binTree.root, 'RECURSIVE', true).map(node => (node ? node.key : null))).toEqual([ 35, 20, 40, @@ -955,241 +945,233 @@ describe('BinaryTree traversals', () => { 45, 55 ]); - expect(tree.bfs(node => node, tree.root, 'ITERATIVE').map(node => (node === null ? null : node.key))).toEqual([ - 35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55 - ]); - expect(tree.bfs(node => node, tree.root, 'RECURSIVE').map(node => (node === null ? null : node.key))).toEqual([ - 35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55 - ]); + expect(binTree.bfs(node => node, binTree.root, 'ITERATIVE').map(node => (node === null ? null : node.key))).toEqual( + [35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55] + ); + expect(binTree.bfs(node => node, binTree.root, 'RECURSIVE').map(node => (node === null ? null : node.key))).toEqual( + [35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55] + ); - expect(tree.dfs(node => node.key, 'PRE')).toEqual([35, 20, 15, 16, 29, 28, 30, 40, 50, 45, 55]); - expect(tree.dfs(node => node.key, 'PRE', tree.root, 'RECURSIVE')).toEqual([ + 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([ 35, 20, 15, 16, 29, 28, 30, 40, 50, 45, 55 ]); expect( - tree.dfs(node => node, 'PRE', tree.root, 'ITERATIVE', true).map(node => (node === null ? null : node.key)) + binTree.dfs(node => node, 'PRE', 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)) ).toEqual([35, 20, 15, null, 16, 29, 28, 30, 40, null, 50, 45, 55]); - expect(tree.dfs(node => node, 'PRE', tree.root, 'RECURSIVE', true).map(node => (node ? node.key : null))).toEqual([ - 35, - 20, - 15, - null, - 16, - 29, - 28, - 30, - 40, - null, - 50, - 45, - 55 - ]); - expect(tree.dfs(node => node.key, 'IN')).toEqual([15, 16, 20, 28, 29, 30, 35, 40, 45, 50, 55]); - expect(tree.dfs(node => node.key, 'POST')).toEqual([16, 15, 28, 30, 29, 20, 45, 55, 50, 40, 35]); - expect(tree.dfs(node => node.key, 'POST', tree.root, 'RECURSIVE')).toEqual([ + 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([ 16, 15, 28, 30, 29, 20, 45, 55, 50, 40, 35 ]); - expect(tree.bfs(node => node.key, tree.root, 'RECURSIVE')).toEqual([35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55]); - expect(tree.bfs(node => node.key, tree.root, 'ITERATIVE')).toEqual([35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55]); + expect(binTree.bfs(node => node.key, binTree.root, 'RECURSIVE')).toEqual([ + 35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55 + ]); + expect(binTree.bfs(node => node.key, binTree.root, 'ITERATIVE')).toEqual([ + 35, 20, 40, 15, 29, 50, 16, 28, 30, 45, 55 + ]); - expect(tree.listLevels(node => node.key)).toEqual([[35], [20, 40], [15, 29, 50], [16, 28, 30, 45, 55]]); + expect(binTree.listLevels(node => node.key)).toEqual([[35], [20, 40], [15, 29, 50], [16, 28, 30, 45, 55]]); - expect(tree.listLevels(node => node.key, tree.root, 'RECURSIVE')).toEqual([ + expect(binTree.listLevels(node => node.key, binTree.root, 'RECURSIVE')).toEqual([ [35], [20, 40], [15, 29, 50], [16, 28, 30, 45, 55] ]); - expect(tree.listLevels(node => (node ? node.key : null), tree.root, 'ITERATIVE', true)).toEqual([ + expect(binTree.listLevels(node => (node ? node.key : null), binTree.root, 'ITERATIVE', true)).toEqual([ [35], [20, 40], [15, 29, null, 50], [null, 16, 28, 30, 45, 55] ]); - expect(tree.listLevels(node => (node ? node.key : null), tree.root, 'RECURSIVE', true)).toEqual([ + expect(binTree.listLevels(node => (node ? node.key : null), binTree.root, 'RECURSIVE', true)).toEqual([ [35], [20, 40], [15, 29, null, 50], [null, 16, 28, 30, 45, 55] ]); - tree.clear(); - expect(tree.listLevels()).toEqual([]); + binTree.clear(); + expect(binTree.listLevels()).toEqual([]); }); }); describe('BinaryTree', () => { - let tree: BinaryTree; + let binTree: BinaryTree; beforeEach(() => { - tree = new BinaryTree([], { + binTree = new BinaryTree([], { iterationType: 'RECURSIVE' }); }); afterEach(() => { - tree.clear(); + binTree.clear(); }); it('should create an empty BinaryTree', () => { - expect(tree.size).toBe(0); - expect(tree.isEmpty()).toBe(true); - expect(tree.root).toBe(undefined); + expect(binTree.size).toBe(0); + expect(binTree.isEmpty()).toBe(true); + expect(binTree.root).toBe(undefined); }); - it('should add nodes to the tree', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + it('should add nodes to the binTree', () => { + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - expect(tree.size).toBe(3); - expect(tree.isEmpty()).toBe(false); - expect(tree.root?.key).toBe(5); + expect(binTree.size).toBe(3); + expect(binTree.isEmpty()).toBe(false); + expect(binTree.root?.key).toBe(5); }); it('should clear the BinaryTree', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - tree.clear(); + binTree.clear(); - expect(tree.size).toBe(0); - expect(tree.isEmpty()).toBe(true); - expect(tree.root).toBe(undefined); + expect(binTree.size).toBe(0); + expect(binTree.isEmpty()).toBe(true); + expect(binTree.root).toBe(undefined); }); it('should get nodes by key', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - const nodeA = tree.getNode(5); - const nodeB = tree.getNode(3); + const nodeA = binTree.getNode(5); + const nodeB = binTree.getNode(3); expect(nodeA?.key).toBe(5); expect(nodeA?.value).toBe(undefined); expect(nodeB?.key).toBe(3); - expect(tree.get(nodeB)).toBe('B'); + expect(binTree.get(nodeB)).toBe('B'); }); - it('should return null when getting a non-existent node', () => { - tree.add([5, 'A']); + it('should return undefined when getting a non-existent node', () => { + binTree.add([5, 'A']); - const node = tree.getNode(3); + const node = binTree.getNode(3); - expect(node).toBe(null); + expect(node).toBe(undefined); }); it('should get the depth of a node', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - expect(tree.getDepth(7)).toBe(1); - expect(tree.getDepth(3)).toBe(1); + expect(binTree.getDepth(7)).toBe(1); + expect(binTree.getDepth(3)).toBe(1); }); - it('should get the height of the tree', () => { - expect(tree.getMinHeight()).toBe(-1); - tree.add([5, 'A']); - tree.add(3, 'B'); - tree.add([7, 'C']); + it('should get the height of the binTree', () => { + expect(binTree.getMinHeight()).toBe(-1); + binTree.add([5, 'A']); + binTree.add(3, 'B'); + binTree.add([7, 'C']); - expect(tree.getHeight()).toBe(1); - expect(tree.getHeight(undefined, 'RECURSIVE')).toBe(1); - expect(tree.getMinHeight(undefined, 'RECURSIVE')).toBe(1); + expect(binTree.getHeight()).toBe(1); + expect(binTree.getHeight(undefined, 'RECURSIVE')).toBe(1); + expect(binTree.getMinHeight(undefined, 'RECURSIVE')).toBe(1); }); - it('should check if the tree is a binary search tree', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + it('should check if the binTree is a binary search binTree', () => { + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - expect(tree.isBST()).toBe(true); + expect(binTree.isBST()).toBe(true); }); it('should perform a depth-first traversal', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - const result = tree.dfs(); + const result = binTree.dfs(); expect(result).toEqual([3, 5, 7]); // Add assertions for the result of depth-first traversal }); it('should perform a breadth-first traversal', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - const result = tree.bfs(node => node.key); + const result = binTree.bfs(node => node.key); expect(result).toEqual([5, 3, 7]); // Add assertions for the result of breadth-first traversal }); - it('should list levels of the tree', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + it('should list levels of the binTree', () => { + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - const levels = tree.listLevels(); + const levels = binTree.listLevels(); expect(levels).toEqual([[5], [3, 7]]); - // Add assertions for the levels of the tree + // Add assertions for the levels of the binTree }); - it('should delete nodes from the tree', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + it('should delete nodes from the binTree', () => { + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - tree.delete(3); + binTree.delete(3); - expect(tree.size).toBe(2); - expect(tree.getNode(3)).toBe(null); + expect(binTree.size).toBe(2); + expect(binTree.getNode(3)).toBe(undefined); }); it('should getPathToRoot', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - expect(tree.getPathToRoot(7)).toEqual([7, 5]); - expect(tree.getPathToRoot(1)).toEqual([]); + expect(binTree.getPathToRoot(7)).toEqual([7, 5]); + expect(binTree.getPathToRoot(1)).toEqual([]); }); - it('should check if the tree is perfectly balanced', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + it('should check if the binTree is perfectly balanced', () => { + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - expect(tree.isPerfectlyBalanced()).toBe(true); + expect(binTree.isPerfectlyBalanced()).toBe(true); }); it('should get nodes by a custom callback', () => { - tree.add([5, 'E']); - tree.add([4, 'D']); - tree.add([3, 'C']); - tree.add([7, 'G']); - tree.add([null, 'null']); - tree.add([1, 'A']); - tree.add([6, 'F']); - tree.add([null, 'null']); - tree.add([2, 'B']); - tree.add([null, 'null']); + binTree.add([5, 'E']); + binTree.add([4, 'D']); + binTree.add([3, 'C']); + binTree.add([7, 'G']); + binTree.add([null, 'null']); + binTree.add([1, 'A']); + binTree.add([6, 'F']); + binTree.add([null, 'null']); + binTree.add([2, 'B']); + binTree.add([null, 'null']); - const nodes = tree.getNodes(node => node.key === 2); + const nodes = binTree.getNodes(node => node.key === 2); expect(nodes.length).toBe(1); expect(nodes[0].key).toBe(2); - const nodesRec = tree.getNodes(node => node.key === 2, false, tree.root, 'RECURSIVE'); + const nodesRec = binTree.getNodes(node => node.key === 2, false, binTree.root, 'RECURSIVE'); expect(nodesRec.length).toBe(1); expect(nodesRec[0].key).toBe(2); - const nodesItr = tree.getNodes(node => node.key === 2, false, tree.root, 'ITERATIVE'); + const nodesItr = binTree.getNodes(node => node.key === 2, false, binTree.root, 'ITERATIVE'); expect(nodesItr.length).toBe(1); expect(nodesItr[0].key).toBe(2); @@ -1198,75 +1180,75 @@ describe('BinaryTree', () => { }); it('should perform Morris traversal', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - tree.iterationType = 'ITERATIVE'; - expect([...tree]).toEqual([ + binTree.iterationType = 'ITERATIVE'; + expect([...binTree]).toEqual([ [3, 'B'], [5, 'A'], [7, 'C'] ]); - tree.iterationType = 'RECURSIVE'; - expect([...tree]).toEqual([ + binTree.iterationType = 'RECURSIVE'; + expect([...binTree]).toEqual([ [3, 'B'], [5, 'A'], [7, 'C'] ]); - tree.iterationType = 'ITERATIVE'; + binTree.iterationType = 'ITERATIVE'; - const result = tree.morris(); + const result = binTree.morris(); expect(result).toEqual([3, 5, 7]); - tree.clear(); - expect(tree.morris()).toEqual([]); + binTree.clear(); + expect(binTree.morris()).toEqual([]); }); it('should perform delete all', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - tree.delete(5); - tree.delete(7); - tree.delete(3); - expect(tree.root).toBe(undefined); - expect(tree.getHeight()).toBe(-1); + binTree.delete(5); + binTree.delete(7); + binTree.delete(3); + expect(binTree.root).toBe(undefined); + expect(binTree.getHeight()).toBe(-1); }); }); describe('BinaryTree not map mode', () => { - let tree: BinaryTree; + let binTree: BinaryTree; beforeEach(() => { - tree = new BinaryTree([], { + binTree = new BinaryTree([], { iterationType: 'RECURSIVE', isMapMode: false }); }); afterEach(() => { - tree.clear(); + binTree.clear(); }); it('should add and find nodes', () => { - tree.add([1, 1]); - tree.add(undefined); - tree.add([2, 2]); - tree.add([3, 3]); + binTree.add([1, '1']); + binTree.add(undefined); + binTree.add([2, '2']); + binTree.add([3, '3']); - expect(tree.has(1)).toBe(true); - expect(tree.has(2)).toBe(true); - expect(tree.has(3)).toBe(true); - expect(tree.has(4)).toBe(false); - const node4 = tree.getNode(4); - expect(tree.has(node4)).toBe(false); - expect(tree.has(node => node === node4)).toBe(false); - expect(tree.has(node => node.value?.toString() === '3')).toBe(true); + expect(binTree.has(1)).toBe(true); + expect(binTree.has(2)).toBe(true); + expect(binTree.has(3)).toBe(true); + expect(binTree.has(4)).toBe(false); + const node4 = binTree.getNode(4); + expect(binTree.has(node4)).toBe(false); + expect(binTree.has(node => node === node4)).toBe(false); + expect(binTree.has(node => node.value?.toString() === '3')).toBe(true); }); it('should isSubtreeBST', () => { - tree.addMany([ + binTree.addMany([ new BinaryTreeNode(4), new BinaryTreeNode(2), new BinaryTreeNode(6), @@ -1277,47 +1259,47 @@ describe('BinaryTree not map mode', () => { new BinaryTreeNode(4) ]); - expect(tree.isBST(tree.getNode(4), 'RECURSIVE')).toBe(true); - expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true); + expect(binTree.isBST(binTree.getNode(4), 'RECURSIVE')).toBe(true); + expect(binTree.isBST(binTree.getNode(4), 'ITERATIVE')).toBe(true); }); it('should get nodes by key', () => { - tree.add([5, 'A']); - tree.add([3, 'B']); - tree.add([7, 'C']); + binTree.add([5, 'A']); + binTree.add([3, 'B']); + binTree.add([7, 'C']); - const nodeA = tree.getNode(5); - const nodeB = tree.getNode(3); + const nodeA = binTree.getNode(5); + const nodeB = binTree.getNode(3); expect(nodeA?.key).toBe(5); - expect(tree.get(nodeA)).toBe('A'); + expect(binTree.get(nodeA)).toBe('A'); expect(nodeB?.key).toBe(3); - expect(tree.get(nodeB)).toBe('B'); + expect(binTree.get(nodeB)).toBe('B'); }); it('should get nodes by a custom callback', () => { - tree.add([5, 'E']); - tree.add([4, 'D']); - tree.add([3, 'C']); - tree.add([7, 'G']); - tree.add([null, 'null']); - tree.add([1, 'A']); - tree.add([6, 'F']); - tree.add([null, 'null']); - tree.add([2, 'B']); - tree.add([null, 'null']); + binTree.add([5, 'E']); + binTree.add([4, 'D']); + binTree.add([3, 'C']); + binTree.add([7, 'G']); + binTree.add([null, 'null']); + binTree.add([1, 'A']); + binTree.add([6, 'F']); + binTree.add([null, 'null']); + binTree.add([2, 'B']); + binTree.add([null, 'null']); - const nodes = tree.getNodes(node => node.key === 2); + const nodes = binTree.getNodes(node => node.key === 2); expect(nodes.length).toBe(1); expect(nodes[0].key).toBe(2); - const nodesRec = tree.getNodes(node => node.key === 2, false, tree.root, 'RECURSIVE'); + const nodesRec = binTree.getNodes(node => node.key === 2, false, binTree.root, 'RECURSIVE'); expect(nodesRec.length).toBe(1); expect(nodesRec[0].key).toBe(2); - const nodesItr = tree.getNodes(node => node.key === 2, false, tree.root, 'ITERATIVE'); + const nodesItr = binTree.getNodes(node => node.key === 2, false, binTree.root, 'ITERATIVE'); expect(nodesItr.length).toBe(1); expect(nodesItr[0].key).toBe(2); @@ -1352,8 +1334,8 @@ describe('BinaryTree iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual([3, 'c']); }); - it('filter should return a new tree with filtered elements', () => { - const filteredTree = binaryTree.filter((key, value) => key > 1); + it('filter should return a new binTree with filtered elements', () => { + const filteredTree = binaryTree.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [3, 'c'], @@ -1361,7 +1343,7 @@ describe('BinaryTree iterative methods test', () => { ]); }); - it('map should return a new tree with modified elements', () => { + it('map should return a new binTree with modified elements', () => { const mappedTree = binaryTree.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -1415,9 +1397,9 @@ describe('BinaryTree iterative methods test', () => { }); it('should iterative method return undefined when the node is null', () => { - const tree = new BinaryTree(); - tree.addMany([-10, -10, -10, 9, 9, 20, null, null, 15, 7, 8, null, 2, null, 6, null, null, 8, 8, 8]); - const bfsResult = tree.bfs(undefined, undefined, undefined, true); + const binTree = new BinaryTree(); + binTree.addMany([-10, -10, -10, 9, 9, 20, null, null, 15, 7, 8, null, 2, null, 6, null, null, 8, 8, 8]); + const bfsResult = binTree.bfs(undefined, undefined, undefined, true); expect(bfsResult).toEqual([ -10, 9, diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index eb9e537..5a27122 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -1121,7 +1121,7 @@ describe('BST iterative methods test', () => { }); it('filter should return a new tree with filtered elements', () => { - const filteredTree = bst.filter((key, value) => key > 1); + const filteredTree = bst.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index d3f4014..3ab69f8 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -150,7 +150,6 @@ describe('Overall BinaryTree Test', () => { tmm.add(2); tmm.add(5); tmm.add(4); - expect(tmm.count).toBe(10); expect(tmm.root?.key).toBe(3); expect(tmm.root?.left?.key).toBe(1); expect(tmm.root?.left?.left?.key).toBe(NaN); @@ -163,7 +162,6 @@ describe('Overall BinaryTree Test', () => { expect(tmm.delete(7)[0].deleted?.key).toBe(7); expect(tmm.has(7)).toBe(false); expect(tmm.size).toBe(7); - expect(tmm.count).toBe(9); expect(tmm.root?.key).toBe(3); expect(tmm.root?.left?.key).toBe(1); expect(tmm.root?.right?.key).toBe(9); @@ -176,7 +174,6 @@ describe('Overall BinaryTree Test', () => { // expect(tmm.bfs()).toEqual([6, 1, 9, 3, 2, 5, 4]); const clonedTMM = tmm.clone(); expect(clonedTMM.size).toBe(7); - expect(clonedTMM.count).toBe(9); expect(clonedTMM.root?.key).toBe(3); expect(clonedTMM.root?.left?.key).toBe(1); expect(clonedTMM.root?.right?.key).toBe(5); 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 e923205..9bc5e22 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 @@ -185,14 +185,14 @@ describe('RedBlackTree 1', () => { }); it('should replace value', () => { - const tree = new RedBlackTree([4, 5, [1, '1'], 2, 3], { isMapMode: false }); - expect(tree.get(1)).toBe('1'); - expect(tree.getNode(1)?.value).toBe('1'); - tree.add(1, 'a'); - expect(tree.get(1)).toBe('a'); - tree.add([1, 'b']); - expect(tree.getNode(1)?.value).toBe('b'); - expect(tree.get(1)).toBe('b'); + const rbTree = new RedBlackTree([4, 5, [1, '1'], 2, 3], { isMapMode: false }); + expect(rbTree.get(1)).toBe('1'); + expect(rbTree.getNode(1)?.value).toBe('1'); + rbTree.add(1, 'a'); + expect(rbTree.get(1)).toBe('a'); + rbTree.add([1, 'b']); + expect(rbTree.getNode(1)?.value).toBe('b'); + expect(rbTree.get(1)).toBe('b'); const treeMap = new RedBlackTree([4, 5, [1, '1'], 2, 3]); expect(treeMap.get(1)).toBe('1'); expect(treeMap.getNode(1)?.value).toBe(undefined); @@ -668,9 +668,9 @@ describe('RedBlackTree 2', () => { }); it('map should return a new rbTree with modified elements', () => { - const mappedTree = rbTree.map((key, value) => [(key * 2).toString(), value]); - expect(mappedTree.size).toBe(3); - expect([...mappedTree]).toEqual([ + const rbTreeMapped = rbTree.map((key, value) => [(key * 2).toString(), value]); + expect(rbTreeMapped.size).toBe(3); + expect([...rbTreeMapped]).toEqual([ ['2', 'a'], ['4', 'b'], ['6', 'c'] @@ -699,119 +699,116 @@ describe('RedBlackTree 2', () => { }); describe('RedBlackTree - _deleteFixup', () => { - let tree: RedBlackTree; + let rbTree: RedBlackTree; beforeEach(() => { - tree = new RedBlackTree(); + rbTree = new RedBlackTree(); }); it('should handle deleting a red leaf node', () => { - tree.add(10, 10); - tree.add(5, 5); // Red leaf - tree.add(20, 20); + rbTree.add(10, 10); + rbTree.add(5, 5); // Red leaf + rbTree.add(20, 20); - expect(tree.delete(5)).toHaveLength(1); // Delete red leaf - expect(tree.root?.left).toBe(tree.NIL); // Left child should be NIL + expect(rbTree.delete(5)).toHaveLength(1); // Delete red leaf + expect(rbTree.root?.left).toBe(rbTree.NIL); // Left child should be NIL }); it('should handle deleting a black leaf node', () => { - tree.add(10, 10); - tree.add(5, 5); // Black node - tree.add(20, 20); - tree.add(1, 1); // Black leaf node + rbTree.add(10, 10); + rbTree.add(5, 5); // Black node + rbTree.add(20, 20); + rbTree.add(1, 1); // Black leaf node - expect(tree.delete(1)).toHaveLength(1); // Delete black leaf - expect(tree.root?.left?.left).toBe(tree.NIL); + expect(rbTree.delete(1)).toHaveLength(1); // Delete black leaf + expect(rbTree.root?.left?.left).toBe(rbTree.NIL); }); it('should handle deleting black node with red sibling', () => { - tree.add(10, 10); - tree.add(5, 5); // Black node - tree.add(20, 20); // Red sibling - tree.add(25, 25); // Force the sibling to be red + rbTree.add(10, 10); + rbTree.add(5, 5); // Black node + rbTree.add(20, 20); // Red sibling + rbTree.add(25, 25); // Force the sibling to be red - expect(tree.delete(5)).toHaveLength(1); // Delete black node - expect(tree.root?.right?.color).toBe('BLACK'); // Ensure sibling color is black after fixup + expect(rbTree.delete(5)).toHaveLength(1); // Delete black node + expect(rbTree.root?.right?.color).toBe('BLACK'); // Ensure sibling color is black after fixup }); it('should handle deleting black node with black sibling', () => { - tree.add(10, 10); - tree.add(5, 5); // Black node - tree.add(20, 20); // Black sibling + rbTree.add(10, 10); + rbTree.add(5, 5); // Black node + rbTree.add(20, 20); // Black sibling - expect(tree.delete(5)).toHaveLength(1); // Delete black node - expect(tree.root?.left).toBe(tree.NIL); + expect(rbTree.delete(5)).toHaveLength(1); // Delete black node + expect(rbTree.root?.left).toBe(rbTree.NIL); }); it('should handle deleting the root node', () => { - tree.add(10, 10); // Root node - tree.add(5, 5); - tree.add(20, 20); + rbTree.add(10, 10); // Root node + rbTree.add(5, 5); + rbTree.add(20, 20); - expect(tree.delete(10)).toHaveLength(1); // Delete root node - expect(tree.root?.key).toBe(20); // New root should be 20 + expect(rbTree.delete(10)).toHaveLength(1); // Delete root node + expect(rbTree.root?.key).toBe(20); // New root should be 20 }); it('should handle complex case with multiple rotations', () => { - tree.add(10, 10); - tree.add(5, 5); - tree.add(15, 15); - tree.add(12, 12); - tree.add(18, 18); - tree.add(16, 16); + rbTree.add(10, 10); + rbTree.add(5, 5); + rbTree.add(15, 15); + rbTree.add(12, 12); + rbTree.add(18, 18); + rbTree.add(16, 16); // Delete a node that will cause rotations and color changes - expect(tree.delete(5)).toHaveLength(1); + expect(rbTree.delete(5)).toHaveLength(1); // Verify the color and structure after fixup - expect(tree.root?.color).toBe('BLACK'); - expect(tree.root?.left).toBe(tree.NIL); - expect(tree.root?.right?.left?.color).toBe('BLACK'); + expect(rbTree.root?.color).toBe('BLACK'); + expect(rbTree.root?.left).toBe(rbTree.NIL); + expect(rbTree.root?.right?.left?.color).toBe('BLACK'); }); it('should handle complex delete fixup scenarios', () => { - const tree = new RedBlackTree(); + const rbTree = new RedBlackTree(); - // Build a tree that will require complex fixup - tree.add(20, 20); - tree.add(10, 10); - tree.add(30, 30); - tree.add(5, 5); - tree.add(15, 15); - tree.add(25, 25); - tree.add(35, 35); - tree.add(2, 2); - tree.add(8, 8); + // Build a rbTree that will require complex fixup + rbTree.add(20, 20); + rbTree.add(10, 10); + rbTree.add(30, 30); + rbTree.add(5, 5); + rbTree.add(15, 15); + rbTree.add(25, 25); + rbTree.add(35, 35); + rbTree.add(2, 2); + rbTree.add(8, 8); // This deletion should trigger a complex fixup - tree.delete(2); - // tree.print(tree.root, { isShowNull: true, isShowRedBlackNIL: true, isShowUndefined: false }); + rbTree.delete(2); + // rbTree.print(rbTree.root, { isShowNull: true, isShowRedBlackNIL: true, isShowUndefined: false }); - expect(tree.isLeaf(2)).toBe(false); - expect(tree.isLeaf(8)).toBe(true); - expect(tree.isLeaf(15)).toBe(true); - expect(tree.isLeaf(25)).toBe(true); - expect(tree.isLeaf(35)).toBe(true); - expect(tree.isLeaf(20)).toBe(false); - expect(tree.isLeaf(30)).toBe(false); - // Verify tree structure and colors after fixup - expect(tree.root?.color).toBe('BLACK'); - expect(tree.root?.key).toBe(20); - expect(tree.root?.left?.color).toBe('RED'); - expect(tree.root?.left?.key).toBe(10); - expect(tree.root?.right?.color).toBe('BLACK'); - expect(tree.root?.right?.key).toBe(30); - expect(tree.root?.left?.left?.color).toBe('BLACK'); - expect(tree.root?.left?.left?.key).toBe(5); - expect(tree.root?.left?.right?.color).toBe('BLACK'); - expect(tree.root?.left?.right?.key).toBe(15); - expect(tree.leaves(node => (node === null ? '' : `${node.key} ${node.color}`), tree.root, 'RECURSIVE')).toEqual([ - '8 RED', - '15 BLACK', - '25 RED', - '35 RED' - ]); - expect(tree.listLevels(node => (node === tree.NIL ? 'NIL' : `${node.key} ${node.color}`))).toEqual([ + expect(rbTree.isLeaf(2)).toBe(false); + expect(rbTree.isLeaf(8)).toBe(true); + expect(rbTree.isLeaf(15)).toBe(true); + expect(rbTree.isLeaf(25)).toBe(true); + expect(rbTree.isLeaf(35)).toBe(true); + expect(rbTree.isLeaf(20)).toBe(false); + expect(rbTree.isLeaf(30)).toBe(false); + // Verify rbTree structure and colors after fixup + expect(rbTree.root?.color).toBe('BLACK'); + expect(rbTree.root?.key).toBe(20); + expect(rbTree.root?.left?.color).toBe('RED'); + expect(rbTree.root?.left?.key).toBe(10); + expect(rbTree.root?.right?.color).toBe('BLACK'); + expect(rbTree.root?.right?.key).toBe(30); + expect(rbTree.root?.left?.left?.color).toBe('BLACK'); + expect(rbTree.root?.left?.left?.key).toBe(5); + expect(rbTree.root?.left?.right?.color).toBe('BLACK'); + expect(rbTree.root?.left?.right?.key).toBe(15); + expect(rbTree.leaves(node => (node === null ? '' : `${node.key} ${node.color}`), rbTree.root, 'RECURSIVE')).toEqual( + ['8 RED', '15 BLACK', '25 RED', '35 RED'] + ); + expect(rbTree.listLevels(node => (node === rbTree.NIL ? 'NIL' : `${node.key} ${node.color}`))).toEqual([ ['20 BLACK'], ['10 RED', '30 BLACK'], ['5 BLACK', '15 BLACK', '25 RED', '35 RED'], diff --git a/test/unit/data-structures/binary-tree/tree-counter.test.ts b/test/unit/data-structures/binary-tree/tree-counter.test.ts new file mode 100644 index 0000000..42bec96 --- /dev/null +++ b/test/unit/data-structures/binary-tree/tree-counter.test.ts @@ -0,0 +1,975 @@ +import { BinaryTreeNode, BSTNode, RedBlackTreeNode, TreeCounter, TreeCounterNode } from '../../../../src'; +import { isDebugTest } from '../../../config'; +import { getRandomInt } from '../../../utils'; + +const isDebug = isDebugTest; +// const isDebug = true; + +describe('TreeCounter count', () => { + let treeCounter: TreeCounter; + beforeEach(() => { + treeCounter = new TreeCounter(); + }); + + it('Should added node count ', () => { + treeCounter.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new TreeCounterNode(3, 33, 10); + treeCounter.add(newNode); + expect(treeCounter.count).toBe(15); + expect(treeCounter.getComputedCount()).toBe(15); + expect(treeCounter.getNode(3)?.count).toBe(11); + }); + + it('Should count', () => { + treeCounter.addMany([ + [1, 1], + [2, 2], + [3, 3] + ]); + treeCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 1); + expect(treeCounter.getComputedCount()).toBe(7); + expect(treeCounter.count).toBe(3); + }); +}); + +describe('TreeCounter operations test1', () => { + it('should height ', () => { + const treeCounter = new TreeCounter(); + expect(treeCounter.getHeight()).toBe(-1); + expect(treeCounter.getMinHeight()).toBe(-1); + + treeCounter.addMany([1, 6, 7, 2, 3, 4, 9, 11, 8, 5, 10, 12, 16, 14, 13, 15]); + // treeCounter.print() + expect(treeCounter.getHeight()).toBe(5); + expect(treeCounter.getMinHeight()).toBe(2); + }); + + it('should size and count', () => { + const treeCounter = new TreeCounter(); + + expect(treeCounter instanceof TreeCounter); + + treeCounter.add([11, 11]); + treeCounter.add([3, 3]); + expect(treeCounter.count).toBe(2); + expect(treeCounter.getComputedCount()).toBe(2); + expect(treeCounter.size).toBe(2); + + const keyValuePairs: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + + treeCounter.addMany(keyValuePairs); + expect(treeCounter.size).toBe(16); + expect(treeCounter.count).toBe(18); + expect(treeCounter.getComputedCount()).toBe(18); + treeCounter.delete(11); + expect(treeCounter.count).toBe(17); + expect(treeCounter.getComputedCount()).toBe(17); + treeCounter.delete(3, true); + expect(treeCounter.count).toBe(15); + expect(treeCounter.getComputedCount()).toBe(15); + }); + + it('should perform various operations on a TreeCounter with numeric values1', () => { + const treeCounter = new TreeCounter(); + + expect(treeCounter instanceof TreeCounter); + + treeCounter.add([11, 11]); + treeCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + treeCounter.addMany(idAndValues); + expect(treeCounter.root instanceof TreeCounterNode); + + if (treeCounter.root) expect(treeCounter.root.key == 11); + + expect(treeCounter.size).toBe(16); + expect(treeCounter.count).toBe(18); + expect(treeCounter.getComputedCount()).toBe(18); + + expect(treeCounter.has(6)); + if (isDebug) treeCounter.print(); + expect(treeCounter.getHeight(6)).toBe(1); + expect(treeCounter.getDepth(6)).toBe(3); + const nodeId10 = treeCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = treeCounter.getNodes(node => node.count === 1); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = treeCounter.getNodes(node => node.count === 2); + expect(nodesByCount2.length).toBe(2); + const leftMost = treeCounter.getLeftMost(); + expect(leftMost).toBe(1); + + const node15 = treeCounter.getNode(15); + const minNodeBySpecificNode = node15 && treeCounter.getLeftMost(node => node, node15); + expect(minNodeBySpecificNode?.key).toBe(14); + + let subTreeSum = 0; + if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + expect(subTreeSum).toBe(45); + let lesserSum = 0; + treeCounter.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); + expect(lesserSum).toBe(45); + + expect(node15 instanceof TreeCounterNode); + if (node15 instanceof TreeCounterNode) { + const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', 15); + expect(subTreeAdd); + } + const node11 = treeCounter.getNode(11); + expect(node11 instanceof TreeCounterNode); + if (node11 instanceof TreeCounterNode) { + const allGreaterNodesAdded = treeCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); + expect(allGreaterNodesAdded); + } + + const dfsInorderNodes = treeCounter.dfs(node => node, 'IN'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + expect(treeCounter.isPerfectlyBalanced()).toBe(false); + treeCounter.perfectlyBalance(); + expect(treeCounter.isPerfectlyBalanced()).toBe(false); + + expect(treeCounter.isAVLBalanced()).toBe(false); + + const bfsNodesAfterBalanced = treeCounter.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(6); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = treeCounter.delete(11, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight(15)).toBe(1); + + const removed1 = treeCounter.delete(1, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight()).toBe(5); + + const removed4 = treeCounter.delete(4, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(5); + + const removed10 = treeCounter.delete(10, true); + expect(removed10 instanceof Array); + expect(removed10[0]); + expect(removed10[0].deleted); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight()).toBe(4); + + const removed15 = treeCounter.delete(15, true); + expect(removed15 instanceof Array); + expect(removed15[0]); + expect(removed15[0].deleted); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed5 = treeCounter.delete(5, true); + expect(removed5 instanceof Array); + expect(removed5[0]); + expect(removed5[0].deleted); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(3); + + const removed13 = treeCounter.delete(13, true); + expect(removed13 instanceof Array); + expect(removed13[0]); + expect(removed13[0].deleted); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(3); + + const removed3 = treeCounter.delete(3, true); + expect(removed3 instanceof Array); + expect(removed3[0]); + expect(removed3[0].deleted); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed8 = treeCounter.delete(8, true); + expect(removed8 instanceof Array); + expect(removed8[0]); + expect(removed8[0].deleted); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed6 = treeCounter.delete(6, true); + expect(removed6 instanceof Array); + expect(removed6[0]); + expect(removed6[0].deleted); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(treeCounter.delete(6, true).length).toBe(0); + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight()).toBe(3); + + const removed7 = treeCounter.delete(7, true); + expect(removed7 instanceof Array); + expect(removed7[0]); + expect(removed7[0].deleted); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed9 = treeCounter.delete(9, true); + expect(removed9 instanceof Array); + expect(removed9[0]); + expect(removed9[0].deleted); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(2); + + const removed14 = treeCounter.delete(14, true); + expect(removed14 instanceof Array); + expect(removed14[0]); + expect(removed14[0].deleted); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(1); + + expect(treeCounter.isAVLBalanced()).toBe(true); + + const bfsIDs = treeCounter.bfs(node => node.key); + + expect(bfsIDs[0]).toBe(12); + expect(bfsIDs[1]).toBe(2); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes = treeCounter.bfs(node => node); + + expect(bfsNodes[0].key).toBe(12); + expect(bfsNodes[1].key).toBe(2); + expect(bfsNodes[2].key).toBe(16); + + expect(treeCounter.count).toBe(6); + expect(treeCounter.getComputedCount()).toBe(8); + }); + + it('should perform various operations on a TreeCounter with object values', () => { + const objTreeCounter = new TreeCounter(); + expect(objTreeCounter).toBeInstanceOf(TreeCounter); + objTreeCounter.add([11, { key: 11, keyA: 11 }]); + objTreeCounter.add([3, { key: 3, keyA: 3 }]); + const values: [number, { key: number; keyA: number }][] = [ + [15, { key: 15, keyA: 15 }], + [1, { key: 1, keyA: 1 }], + [8, { key: 8, keyA: 8 }], + [13, { key: 13, keyA: 13 }], + [16, { key: 16, keyA: 16 }], + [2, { key: 2, keyA: 2 }], + [6, { key: 6, keyA: 6 }], + [9, { key: 9, keyA: 9 }], + [12, { key: 12, keyA: 12 }], + [14, { key: 14, keyA: 14 }], + [4, { key: 4, keyA: 4 }], + [7, { key: 7, keyA: 7 }], + [10, { key: 10, keyA: 10 }], + [5, { key: 5, keyA: 5 }] + ]; + + objTreeCounter.addMany(values); + + expect(objTreeCounter.root).toBeInstanceOf(TreeCounterNode); + + if (objTreeCounter.root) expect(objTreeCounter.root.key).toBe(5); + + expect(objTreeCounter.count).toBe(16); + expect(objTreeCounter.getComputedCount()).toBe(16); + + expect(objTreeCounter.has(6)).toBe(true); + }); +}); + +describe('TreeCounter operations test recursively1', () => { + it('should perform various operations on a TreeCounter with numeric values1', () => { + const treeCounter = new TreeCounter([], { + iterationType: 'RECURSIVE' + }); + + expect(treeCounter instanceof TreeCounter); + treeCounter.add([11, 11]); + treeCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + treeCounter.addMany(idAndValues); + expect(treeCounter.root).toBeInstanceOf(TreeCounterNode); + + if (treeCounter.root) expect(treeCounter.root.key).toBe(5); + + expect(treeCounter.size).toBe(16); + expect(treeCounter.count).toBe(18); + expect(treeCounter.getComputedCount()).toBe(18); + + expect(treeCounter.has(6)); + + expect(treeCounter.getHeight(6)).toBe(1); + expect(treeCounter.getDepth(6)).toBe(3); + const nodeId10 = treeCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = treeCounter.getNodes(node => node.count === 1); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = treeCounter.getNodes(node => node.count === 2); + expect(nodesByCount2.length).toBe(2); + const leftMost = treeCounter.getLeftMost(); + expect(leftMost).toBe(1); + + const node15 = treeCounter.getNode(15); + const minNodeBySpecificNode = node15 && treeCounter.getLeftMost(node => node, node15); + expect(minNodeBySpecificNode?.key).toBe(14); + + let subTreeSum = 0; + if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15); + expect(subTreeSum).toBe(45); + let lesserSum = 0; + expect(treeCounter.has(9)).toBe(true); + treeCounter.lesserOrGreaterTraverse( + node => { + lesserSum += node.key; + return node.key; + }, + -1, + 10 + ); + expect(lesserSum).toBe(45); + + expect(node15 instanceof TreeCounterNode); + if (node15 instanceof TreeCounterNode) { + const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', 15); + expect(subTreeAdd); + } + const node11 = treeCounter.getNode(11); + expect(node11 instanceof TreeCounterNode); + if (node11 instanceof TreeCounterNode) { + const allGreaterNodesAdded = treeCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); + expect(allGreaterNodesAdded); + } + + const dfsInorderNodes = treeCounter.dfs(node => node, 'IN'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + expect(treeCounter.isPerfectlyBalanced()).toBe(false); + + treeCounter.perfectlyBalance(); + + expect(treeCounter.isPerfectlyBalanced()).toBe(false); + expect(treeCounter.isAVLBalanced()).toBe(false); + + const bfsNodesAfterBalanced = treeCounter.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(6); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = treeCounter.delete(11, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight(15)).toBe(1); + + const removed1 = treeCounter.delete(1, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight()).toBe(5); + + const removed4 = treeCounter.delete(4, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(5); + + const removed10 = treeCounter.delete(10, true); + expect(removed10 instanceof Array); + expect(removed10[0]); + expect(removed10[0].deleted); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight()).toBe(4); + + const removed15 = treeCounter.delete(15, true); + expect(removed15 instanceof Array); + expect(removed15[0]); + expect(removed15[0].deleted); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed5 = treeCounter.delete(5, true); + expect(removed5 instanceof Array); + expect(removed5[0]); + expect(removed5[0].deleted); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(3); + + const removed13 = treeCounter.delete(13, true); + expect(removed13 instanceof Array); + expect(removed13[0]); + expect(removed13[0].deleted); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(3); + + const removed3 = treeCounter.delete(3, true); + expect(removed3 instanceof Array); + expect(removed3[0]); + expect(removed3[0].deleted); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed8 = treeCounter.delete(8, true); + expect(removed8 instanceof Array); + expect(removed8[0]); + expect(removed8[0].deleted); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed6 = treeCounter.delete(6, true); + expect(removed6 instanceof Array); + expect(removed6[0]); + expect(removed6[0].deleted); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(treeCounter.delete(6, true).length).toBe(0); + expect(treeCounter.isAVLBalanced()).toBe(false); + + expect(treeCounter.getHeight()).toBe(3); + + const removed7 = treeCounter.delete(7, true); + expect(removed7 instanceof Array); + expect(removed7[0]); + expect(removed7[0].deleted); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(treeCounter.isAVLBalanced()).toBe(false); + expect(treeCounter.getHeight()).toBe(3); + + const removed9 = treeCounter.delete(9, true); + expect(removed9 instanceof Array); + expect(removed9[0]); + expect(removed9[0].deleted); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(2); + + const removed14 = treeCounter.delete(14, true); + expect(removed14 instanceof Array); + expect(removed14[0]); + expect(removed14[0].deleted); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(treeCounter.isAVLBalanced()).toBe(true); + expect(treeCounter.getHeight()).toBe(1); + + expect(treeCounter.isAVLBalanced()).toBe(true); + + const bfsIDs = treeCounter.bfs(node => node.key); + + expect(bfsIDs[0]).toBe(12); + expect(bfsIDs[1]).toBe(2); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes = treeCounter.bfs(node => node); + + expect(bfsNodes[0].key).toBe(12); + expect(bfsNodes[1].key).toBe(2); + expect(bfsNodes[2].key).toBe(16); + + expect(treeCounter.count).toBe(6); + expect(treeCounter.getComputedCount()).toBe(8); + }); + + it('should perform various operations on a TreeCounter with object values', () => { + const objTreeCounter = new TreeCounter(); + expect(objTreeCounter).toBeInstanceOf(TreeCounter); + objTreeCounter.add([11, { key: 11, keyA: 11 }]); + objTreeCounter.add([3, { key: 3, keyA: 3 }]); + const values: [number, { key: number; keyA: number }][] = [ + [15, { key: 15, keyA: 15 }], + [1, { key: 1, keyA: 1 }], + [8, { key: 8, keyA: 8 }], + [13, { key: 13, keyA: 13 }], + [16, { key: 16, keyA: 16 }], + [2, { key: 2, keyA: 2 }], + [6, { key: 6, keyA: 6 }], + [9, { key: 9, keyA: 9 }], + [12, { key: 12, keyA: 12 }], + [14, { key: 14, keyA: 14 }], + [4, { key: 4, keyA: 4 }], + [7, { key: 7, keyA: 7 }], + [10, { key: 10, keyA: 10 }], + [5, { key: 5, keyA: 5 }] + ]; + + objTreeCounter.addMany(values); + + expect(objTreeCounter.root).toBeInstanceOf(TreeCounterNode); + + if (objTreeCounter.root) expect(objTreeCounter.root.key).toBe(5); + + expect(objTreeCounter.count).toBe(16); + expect(objTreeCounter.getComputedCount()).toBe(16); + + expect(objTreeCounter.has(6)).toBe(true); + }); +}); + +describe('TreeCounter delete test', function () { + const treeCounter = new TreeCounter(); + const inputSize = 1000; // Adjust input sizes as needed + + beforeEach(() => { + treeCounter.clear(); + }); + + it(`Observe the time consumption of TreeCounter.dfs be good`, function () { + const startDFS = performance.now(); + const dfs = treeCounter.dfs(node => node); + if (isDebug) console.log('---bfs', performance.now() - startDFS, dfs.length); + }); + + it('The structure remains normal after random deletion', function () { + for (let i = 0; i < inputSize; i++) { + treeCounter.add(i); + } + + expect(treeCounter.size).toBe(inputSize); + + for (let i = 0; i < inputSize; i++) { + const num = getRandomInt(0, inputSize - 1); + treeCounter.delete(num); + } + + let nilCount = 0; + const dfs = (cur: TreeCounterNode) => { + if (isNaN(cur.key)) nilCount++; + if (cur.left) dfs(cur.left); + if (cur.right) dfs(cur.right); + }; + if (treeCounter.root) dfs(treeCounter.root); + + expect(treeCounter.size).toBeLessThanOrEqual(inputSize); + expect(treeCounter.getHeight()).toBeGreaterThan(Math.log2(inputSize) - 1); + expect(treeCounter.getHeight()).toBeLessThan(Math.log2(inputSize) * 2); + + expect(nilCount).toBe(treeCounter.size + 1); + }); + + it(`Random additions, complete deletions of structures are normal`, function () { + for (let i = 0; i < inputSize; i++) { + const num = getRandomInt(0, inputSize - 1); + if (i === 0 && isDebug) console.log(`first:`, num); + treeCounter.add(num); + } + + for (let i = 0; i < inputSize; i++) { + treeCounter.delete(i, true); + } + + let nilCount = 0; + const dfs = (cur: TreeCounterNode) => { + if (isNaN(cur.key)) nilCount++; + if (cur.left) dfs(cur.left); + if (cur.right) dfs(cur.right); + }; + if (treeCounter.root) dfs(treeCounter.root); + + expect(treeCounter.size).toBe(0); + expect(treeCounter.getHeight()).toBe(-1); + expect(nilCount).toBe(treeCounter.size + 1); + + if (isDebug) treeCounter.print(); + }); + + it(`Random additions, count deletions of structures are normal`, function () { + for (let i = 0; i < inputSize; i++) { + const num = getRandomInt(0, inputSize - 1); + if (i === 0 && isDebug) console.log(`first:`, num); + treeCounter.add(num); + } + + for (let i = 0; i < inputSize; i++) { + treeCounter.delete(i); + } + + let nanCount = 0; + const dfs = (cur: TreeCounterNode) => { + if (isNaN(cur.key)) nanCount++; + if (cur.left) dfs(cur.left); + if (cur.right) dfs(cur.right); + }; + if (treeCounter.root) dfs(treeCounter.root); + + expect(treeCounter.size).toBeGreaterThanOrEqual(0); + expect(treeCounter.getHeight()).toBeGreaterThanOrEqual(0); + expect(nanCount).toBeLessThanOrEqual(inputSize); + + if (isDebug) treeCounter.print(); + }); + + it('should the clone method', () => { + function checkTreeStructure(treeCounter: TreeCounter) { + expect(treeCounter.size).toBe(4); + expect(treeCounter.root?.key).toBe('2'); + expect(treeCounter.root?.left?.key).toBe('1'); + expect(treeCounter.root?.left?.left?.key).toBe(NaN); + expect(treeCounter.root?.left?.right?.key).toBe(NaN); + expect(treeCounter.root?.right?.key).toBe('4'); + expect(treeCounter.root?.right?.left?.key).toBe(NaN); + expect(treeCounter.root?.right?.right?.key).toBe('5'); + } + + const treeCounter = new TreeCounter(); + treeCounter.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(treeCounter.size).toBe(5); + expect(treeCounter.root?.key).toBe('2'); + expect(treeCounter.root?.left?.key).toBe('1'); + expect(treeCounter.root?.left?.left?.key).toBe(NaN); + expect(treeCounter.root?.left?.right?.key).toBe(NaN); + expect(treeCounter.root?.right?.key).toBe('4'); + expect(treeCounter.root?.right?.left?.key).toBe(`3`); + expect(treeCounter.root?.right?.right?.key).toBe('5'); + treeCounter.delete('3'); + checkTreeStructure(treeCounter); + const cloned = treeCounter.clone(); + checkTreeStructure(cloned); + cloned.delete('1'); + expect(treeCounter.size).toBe(4); + expect(cloned.size).toBe(3); + }); +}); + +describe('TreeCounter iterative methods test', () => { + let treeCounter: TreeCounter; + beforeEach(() => { + treeCounter = new TreeCounter(); + treeCounter.add(1, 'a', 10); + treeCounter.add([2, 'b'], undefined, 10); + treeCounter.add([3, 'c'], undefined, 1); + }); + + it('The node obtained by get Node should match the node type', () => { + const node3 = treeCounter.getNode(3); + expect(node3).toBeInstanceOf(BinaryTreeNode); + expect(node3).toBeInstanceOf(BSTNode); + expect(node3).toBeInstanceOf(RedBlackTreeNode); + }); + + it('forEach should iterate over all elements', () => { + const mockCallback = jest.fn(); + treeCounter.forEach((key, value) => { + mockCallback(key, value); + }); + + expect(mockCallback.mock.calls.length).toBe(3); + expect(mockCallback.mock.calls[0]).toEqual([1, 'a']); + expect(mockCallback.mock.calls[1]).toEqual([2, 'b']); + expect(mockCallback.mock.calls[2]).toEqual([3, 'c']); + }); + + it('filter should return a new tree with filtered elements', () => { + const filteredTree = treeCounter.filter(key => key > 1); + expect(filteredTree.size).toBe(2); + expect([...filteredTree]).toEqual([ + [2, 'b'], + [3, 'c'] + ]); + }); + + it('map should return a new tree with modified elements', () => { + const treeCounterMapped = treeCounter.map((key, value) => [(key * 2).toString(), value]); + expect(treeCounterMapped.size).toBe(3); + expect([...treeCounterMapped]).toEqual([ + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] + ]); + }); + + it('reduce should accumulate values', () => { + const sum = treeCounter.reduce((acc, value, key) => acc + key, 0); + expect(sum).toBe(6); + }); + + it('[Symbol.iterator] should provide an iterator', () => { + const entries = []; + for (const entry of treeCounter) { + entries.push(entry); + } + + expect(entries.length).toBe(3); + expect(entries).toEqual([ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ]); + }); + + it('should clone work well', () => { + expect(treeCounter.count).toBe(21); + expect(treeCounter.getComputedCount()).toBe(21); + const cloned = treeCounter.clone(); + expect(cloned.root?.left?.key).toBe(1); + if (cloned.isMapMode) expect(cloned.get(cloned.root?.right)).toBe('c'); + else expect(cloned.root?.right?.value).toBe(undefined); + }); + + it('should keys', () => { + const keys = treeCounter.keys(); + expect([...keys]).toEqual([1, 2, 3]); + }); + + it('should values', () => { + const values = treeCounter.values(); + expect([...values]).toEqual(['a', 'b', 'c']); + }); + + it('should leaves', () => { + const leaves = treeCounter.leaves(); + expect(leaves).toEqual([1, 3]); + }); +}); + +describe('TreeCounter count not map mode', () => { + let treeCounter: TreeCounter; + beforeEach(() => { + treeCounter = new TreeCounter([], { isMapMode: false }); + }); + + it('Should added node count ', () => { + treeCounter.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new TreeCounterNode(3, undefined, 10); + treeCounter.add(newNode, 33, 20); + // TODO expect(treeCounter.count).toBe(25); + expect(treeCounter.count).toBe(15); + expect(treeCounter.getComputedCount()).toBe(15); + expect(treeCounter.getNode(3)?.count).toBe(11); + }); +}); + +describe('TreeCounter operations test1 not map mode', () => { + it('should perform various operations on a TreeCounter with numeric values1', () => { + const treeCounter = new TreeCounter([], { isMapMode: false }); + + expect(treeCounter instanceof TreeCounter); + + treeCounter.add([11, 11]); + treeCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + treeCounter.addMany(idAndValues); + expect(treeCounter.root instanceof TreeCounterNode); + + if (treeCounter.root) expect(treeCounter.root.key == 11); + + expect(treeCounter.size).toBe(16); + expect(treeCounter.count).toBe(18); + expect(treeCounter.getComputedCount()).toBe(18); + + expect(treeCounter.has(6)); + if (isDebug) treeCounter.print(); + expect(treeCounter.getHeight(6)).toBe(1); + expect(treeCounter.getDepth(6)).toBe(3); + const nodeId10 = treeCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); + +describe('TreeCounter operations test recursively1 not map mode', () => { + it('should perform various operations on a TreeCounter with numeric values1', () => { + const treeCounter = new TreeCounter([], { + iterationType: 'RECURSIVE', + isMapMode: false + }); + + expect(treeCounter instanceof TreeCounter); + treeCounter.add([11, 11]); + treeCounter.add([3, 3]); + const idAndValues: [number, number][] = [ + [11, 11], + [3, 3], + [15, 15], + [1, 1], + [8, 8], + [13, 13], + [16, 16], + [2, 2], + [6, 6], + [9, 9], + [12, 12], + [14, 14], + [4, 4], + [7, 7], + [10, 10], + [5, 5] + ]; + treeCounter.addMany(idAndValues); + expect(treeCounter.root).toBeInstanceOf(TreeCounterNode); + + if (treeCounter.root) expect(treeCounter.root.key).toBe(5); + + expect(treeCounter.size).toBe(16); + expect(treeCounter.count).toBe(18); + expect(treeCounter.getComputedCount()).toBe(18); + + expect(treeCounter.has(6)); + + expect(treeCounter.getHeight(6)).toBe(1); + expect(treeCounter.getDepth(6)).toBe(3); + const nodeId10 = treeCounter.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeCounter.getNode(node => node.key === 9); + expect(nodeVal9?.key).toBe(9); + }); +}); + +describe('TreeCounter iterative methods test not map mode', () => { + let treeCounter: TreeCounter; + beforeEach(() => { + treeCounter = new TreeCounter([], { isMapMode: false }); + treeCounter.add(1, 'a', 10); + treeCounter.add([2, 'b'], undefined, 10); + treeCounter.add([3, 'c'], undefined, 1); + }); + + it('should clone work well', () => { + expect(treeCounter.count).toBe(21); + expect(treeCounter.getComputedCount()).toBe(21); + const cloned = treeCounter.clone(); + expect(cloned.root?.left?.key).toBe(1); + expect(cloned.get(cloned.root?.right)).toBe(undefined); + }); +}); 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 ab43c41..f3d86bc 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 @@ -1,711 +1,165 @@ -import { BinaryTreeNode, BSTNode, RedBlackTreeNode, TreeMultiMap, TreeMultiMapNode } from '../../../../src'; -import { isDebugTest } from '../../../config'; +import { BinaryTreeNode, BSTNode, Range, TreeMultiMap, TreeMultiMapNode } from '../../../../src'; import { getRandomInt } from '../../../utils'; +import { isDebugTest } from '../../../config'; +import { costOfLiving } from './data/cost-of-living-by-country'; + const isDebug = isDebugTest; // const isDebug = true; -describe('TreeMultiMap count', () => { +describe('TreeMultiMap 1', () => { let tmm: TreeMultiMap; + beforeEach(() => { tmm = new TreeMultiMap(); }); - it('Should added node count ', () => { - tmm.addMany([ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5] - ]); - const newNode = new TreeMultiMapNode(3, 33, 10); - tmm.add(newNode); - expect(tmm.count).toBe(15); - expect(tmm.getComputedCount()).toBe(15); - expect(tmm.getNode(3)?.count).toBe(11); + it('Should add and delete values', () => { + tmm.add(3, 3); + tmm.add(3, 33); + tmm.add(3, 333); + expect(tmm.get(3)).toEqual([3, 33, 333]); + tmm.deleteValue(3, 33); + expect(tmm.get(3)).toEqual([3, 333]); + tmm.deleteValue(3, 3); + expect(tmm.get(3)).toEqual([333]); + tmm.deleteValue(3, 333); + expect(tmm.get(3)).toBe(undefined); + tmm.add(3, 3); + tmm.add([3, [3333, 33333]]); + expect(tmm.get(3)).toEqual([3, 3333, 33333]); }); - it('Should count', () => { - tmm.addMany([ - [1, 1], - [2, 2], - [3, 3] - ]); - tmm.lesserOrGreaterTraverse(node => (node.count += 2), 1, 1); - expect(tmm.getComputedCount()).toBe(7); - expect(tmm.count).toBe(3); - }); -}); + describe('add and getNode', () => { + it('should add and find a node in the tmm', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); -describe('TreeMultiMap operations test1', () => { - it('should height ', () => { - const tmm = new TreeMultiMap(); - expect(tmm.getHeight()).toBe(-1); - expect(tmm.getMinHeight()).toBe(-1); - - tmm.addMany([1, 6, 7, 2, 3, 4, 9, 11, 8, 5, 10, 12, 16, 14, 13, 15]); - // tmm.print() - expect(tmm.getHeight()).toBe(5); - expect(tmm.getMinHeight()).toBe(2); - }); - - it('should size and count', () => { - const tmm = new TreeMultiMap(); - - expect(tmm instanceof TreeMultiMap); - - tmm.add([11, 11]); - tmm.add([3, 3]); - expect(tmm.count).toBe(2); - expect(tmm.getComputedCount()).toBe(2); - expect(tmm.size).toBe(2); - - const keyValuePairs: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - - tmm.addMany(keyValuePairs); - expect(tmm.size).toBe(16); - expect(tmm.count).toBe(18); - expect(tmm.getComputedCount()).toBe(18); - tmm.delete(11); - expect(tmm.count).toBe(17); - expect(tmm.getComputedCount()).toBe(17); - tmm.delete(3, true); - expect(tmm.count).toBe(15); - expect(tmm.getComputedCount()).toBe(15); - }); - - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const tmm = new TreeMultiMap(); - - expect(tmm instanceof TreeMultiMap); - - tmm.add([11, 11]); - tmm.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - tmm.addMany(idAndValues); - expect(tmm.root instanceof TreeMultiMapNode); - - if (tmm.root) expect(tmm.root.key == 11); - - expect(tmm.size).toBe(16); - expect(tmm.count).toBe(18); - expect(tmm.getComputedCount()).toBe(18); - - expect(tmm.has(6)); - if (isDebug) tmm.print(); - expect(tmm.getHeight(6)).toBe(1); - expect(tmm.getDepth(6)).toBe(3); - const nodeId10 = tmm.getNode(10); - expect(nodeId10?.key).toBe(10); - - const nodeVal9 = tmm.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); - - const nodesByCount1 = tmm.getNodes(node => node.count === 1); - expect(nodesByCount1.length).toBe(14); - - const nodesByCount2 = tmm.getNodes(node => node.count === 2); - expect(nodesByCount2.length).toBe(2); - const leftMost = tmm.getLeftMost(); - expect(leftMost).toBe(1); - - const node15 = tmm.getNode(15); - const minNodeBySpecificNode = node15 && tmm.getLeftMost(node => node, node15); - expect(minNodeBySpecificNode?.key).toBe(14); - - let subTreeSum = 0; - if (node15) tmm.dfs(node => (subTreeSum += node.key), 'PRE', 15); - expect(subTreeSum).toBe(45); - let lesserSum = 0; - tmm.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10); - expect(lesserSum).toBe(45); - - expect(node15 instanceof TreeMultiMapNode); - if (node15 instanceof TreeMultiMapNode) { - const subTreeAdd = tmm.dfs(node => (node.count += 1), 'PRE', 15); - expect(subTreeAdd); - } - const node11 = tmm.getNode(11); - expect(node11 instanceof TreeMultiMapNode); - if (node11 instanceof TreeMultiMapNode) { - const allGreaterNodesAdded = tmm.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); - expect(allGreaterNodesAdded); - } - - const dfsInorderNodes = tmm.dfs(node => node, 'IN'); - expect(dfsInorderNodes[0].key).toBe(1); - expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); - expect(tmm.isPerfectlyBalanced()).toBe(false); - tmm.perfectlyBalance(); - expect(tmm.isPerfectlyBalanced()).toBe(false); - - expect(tmm.isAVLBalanced()).toBe(false); - - const bfsNodesAfterBalanced = tmm.bfs(node => node); - expect(bfsNodesAfterBalanced[0].key).toBe(6); - expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); - - const removed11 = tmm.delete(11, true); - expect(removed11 instanceof Array); - expect(removed11[0]); - expect(removed11[0].deleted); - - if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); - - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight(15)).toBe(1); - - const removed1 = tmm.delete(1, true); - expect(removed1 instanceof Array); - expect(removed1[0]); - expect(removed1[0].deleted); - if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); - - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight()).toBe(5); - - const removed4 = tmm.delete(4, true); - expect(removed4 instanceof Array); - expect(removed4[0]); - expect(removed4[0].deleted); - if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); - - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(5); - - const removed10 = tmm.delete(10, true); - expect(removed10 instanceof Array); - expect(removed10[0]); - expect(removed10[0].deleted); - if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight()).toBe(4); - - const removed15 = tmm.delete(15, true); - expect(removed15 instanceof Array); - expect(removed15[0]); - expect(removed15[0].deleted); - if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); - - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed5 = tmm.delete(5, true); - expect(removed5 instanceof Array); - expect(removed5[0]); - expect(removed5[0].deleted); - if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); - - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(3); - - const removed13 = tmm.delete(13, true); - expect(removed13 instanceof Array); - expect(removed13[0]); - expect(removed13[0].deleted); - if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(3); - - const removed3 = tmm.delete(3, true); - expect(removed3 instanceof Array); - expect(removed3[0]); - expect(removed3[0].deleted); - if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed8 = tmm.delete(8, true); - expect(removed8 instanceof Array); - expect(removed8[0]); - expect(removed8[0].deleted); - if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed6 = tmm.delete(6, true); - expect(removed6 instanceof Array); - expect(removed6[0]); - expect(removed6[0].deleted); - if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); - expect(tmm.delete(6, true).length).toBe(0); - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight()).toBe(3); - - const removed7 = tmm.delete(7, true); - expect(removed7 instanceof Array); - expect(removed7[0]); - expect(removed7[0].deleted); - if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed9 = tmm.delete(9, true); - expect(removed9 instanceof Array); - expect(removed9[0]); - expect(removed9[0].deleted); - if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(2); - - const removed14 = tmm.delete(14, true); - expect(removed14 instanceof Array); - expect(removed14[0]); - expect(removed14[0].deleted); - if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(1); - - expect(tmm.isAVLBalanced()).toBe(true); - - const bfsIDs = tmm.bfs(node => node.key); - - expect(bfsIDs[0]).toBe(12); - expect(bfsIDs[1]).toBe(2); - expect(bfsIDs[2]).toBe(16); - - const bfsNodes = tmm.bfs(node => node); - - expect(bfsNodes[0].key).toBe(12); - expect(bfsNodes[1].key).toBe(2); - expect(bfsNodes[2].key).toBe(16); - - expect(tmm.count).toBe(6); - expect(tmm.getComputedCount()).toBe(8); - }); - - it('should perform various operations on a Binary Search Tree with object values', () => { - const objTreeMultiMap = new TreeMultiMap(); - expect(objTreeMultiMap).toBeInstanceOf(TreeMultiMap); - objTreeMultiMap.add([11, { key: 11, keyA: 11 }]); - objTreeMultiMap.add([3, { key: 3, keyA: 3 }]); - const values: [number, { key: number; keyA: number }][] = [ - [15, { key: 15, keyA: 15 }], - [1, { key: 1, keyA: 1 }], - [8, { key: 8, keyA: 8 }], - [13, { key: 13, keyA: 13 }], - [16, { key: 16, keyA: 16 }], - [2, { key: 2, keyA: 2 }], - [6, { key: 6, keyA: 6 }], - [9, { key: 9, keyA: 9 }], - [12, { key: 12, keyA: 12 }], - [14, { key: 14, keyA: 14 }], - [4, { key: 4, keyA: 4 }], - [7, { key: 7, keyA: 7 }], - [10, { key: 10, keyA: 10 }], - [5, { key: 5, keyA: 5 }] - ]; - - objTreeMultiMap.addMany(values); - - expect(objTreeMultiMap.root).toBeInstanceOf(TreeMultiMapNode); - - if (objTreeMultiMap.root) expect(objTreeMultiMap.root.key).toBe(5); - - expect(objTreeMultiMap.count).toBe(16); - expect(objTreeMultiMap.getComputedCount()).toBe(16); - - expect(objTreeMultiMap.has(6)).toBe(true); - }); -}); - -describe('TreeMultiMap operations test recursively1', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const tmm = new TreeMultiMap([], { - iterationType: 'RECURSIVE' + expect(tmm.getNode(10)).toBeInstanceOf(TreeMultiMapNode); + expect(tmm.getNode(20)).toBeInstanceOf(TreeMultiMapNode); + expect(tmm.getNode(5)).toBeInstanceOf(TreeMultiMapNode); + expect(tmm.getNode(15)).toBe(undefined); }); - expect(tmm instanceof TreeMultiMap); - tmm.add([11, 11]); - tmm.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - tmm.addMany(idAndValues); - expect(tmm.root).toBeInstanceOf(TreeMultiMapNode); + it('should add and find nodes with negative keys', () => { + tmm.add(-10); + tmm.add(-20); - if (tmm.root) expect(tmm.root.key).toBe(5); - - expect(tmm.size).toBe(16); - expect(tmm.count).toBe(18); - expect(tmm.getComputedCount()).toBe(18); - - expect(tmm.has(6)); - - expect(tmm.getHeight(6)).toBe(1); - expect(tmm.getDepth(6)).toBe(3); - const nodeId10 = tmm.getNode(10); - expect(nodeId10?.key).toBe(10); - - const nodeVal9 = tmm.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); - - const nodesByCount1 = tmm.getNodes(node => node.count === 1); - expect(nodesByCount1.length).toBe(14); - - const nodesByCount2 = tmm.getNodes(node => node.count === 2); - expect(nodesByCount2.length).toBe(2); - const leftMost = tmm.getLeftMost(); - expect(leftMost).toBe(1); - - const node15 = tmm.getNode(15); - const minNodeBySpecificNode = node15 && tmm.getLeftMost(node => node, node15); - expect(minNodeBySpecificNode?.key).toBe(14); - - let subTreeSum = 0; - if (node15) tmm.dfs(node => (subTreeSum += node.key), 'PRE', 15); - expect(subTreeSum).toBe(45); - let lesserSum = 0; - expect(tmm.has(9)).toBe(true); - tmm.lesserOrGreaterTraverse( - node => { - lesserSum += node.key; - return node.key; - }, - -1, - 10 - ); - expect(lesserSum).toBe(45); - - expect(node15 instanceof TreeMultiMapNode); - if (node15 instanceof TreeMultiMapNode) { - const subTreeAdd = tmm.dfs(node => (node.count += 1), 'PRE', 15); - expect(subTreeAdd); - } - const node11 = tmm.getNode(11); - expect(node11 instanceof TreeMultiMapNode); - if (node11 instanceof TreeMultiMapNode) { - const allGreaterNodesAdded = tmm.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11); - expect(allGreaterNodesAdded); - } - - const dfsInorderNodes = tmm.dfs(node => node, 'IN'); - expect(dfsInorderNodes[0].key).toBe(1); - expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); - expect(tmm.isPerfectlyBalanced()).toBe(false); - - tmm.perfectlyBalance(); - - expect(tmm.isPerfectlyBalanced()).toBe(false); - expect(tmm.isAVLBalanced()).toBe(false); - - const bfsNodesAfterBalanced = tmm.bfs(node => node); - expect(bfsNodesAfterBalanced[0].key).toBe(6); - expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); - - const removed11 = tmm.delete(11, true); - expect(removed11 instanceof Array); - expect(removed11[0]); - expect(removed11[0].deleted); - - if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); - - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight(15)).toBe(1); - - const removed1 = tmm.delete(1, true); - expect(removed1 instanceof Array); - expect(removed1[0]); - expect(removed1[0].deleted); - if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); - - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight()).toBe(5); - - const removed4 = tmm.delete(4, true); - expect(removed4 instanceof Array); - expect(removed4[0]); - expect(removed4[0].deleted); - if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); - - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(5); - - const removed10 = tmm.delete(10, true); - expect(removed10 instanceof Array); - expect(removed10[0]); - expect(removed10[0].deleted); - if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight()).toBe(4); - - const removed15 = tmm.delete(15, true); - expect(removed15 instanceof Array); - expect(removed15[0]); - expect(removed15[0].deleted); - if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); - - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed5 = tmm.delete(5, true); - expect(removed5 instanceof Array); - expect(removed5[0]); - expect(removed5[0].deleted); - if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); - - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(3); - - const removed13 = tmm.delete(13, true); - expect(removed13 instanceof Array); - expect(removed13[0]); - expect(removed13[0].deleted); - if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(3); - - const removed3 = tmm.delete(3, true); - expect(removed3 instanceof Array); - expect(removed3[0]); - expect(removed3[0].deleted); - if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed8 = tmm.delete(8, true); - expect(removed8 instanceof Array); - expect(removed8[0]); - expect(removed8[0].deleted); - if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed6 = tmm.delete(6, true); - expect(removed6 instanceof Array); - expect(removed6[0]); - expect(removed6[0].deleted); - if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); - expect(tmm.delete(6, true).length).toBe(0); - expect(tmm.isAVLBalanced()).toBe(false); - - expect(tmm.getHeight()).toBe(3); - - const removed7 = tmm.delete(7, true); - expect(removed7 instanceof Array); - expect(removed7[0]); - expect(removed7[0].deleted); - if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); - expect(tmm.isAVLBalanced()).toBe(false); - expect(tmm.getHeight()).toBe(3); - - const removed9 = tmm.delete(9, true); - expect(removed9 instanceof Array); - expect(removed9[0]); - expect(removed9[0].deleted); - if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(2); - - const removed14 = tmm.delete(14, true); - expect(removed14 instanceof Array); - expect(removed14[0]); - expect(removed14[0].deleted); - if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); - expect(tmm.isAVLBalanced()).toBe(true); - expect(tmm.getHeight()).toBe(1); - - expect(tmm.isAVLBalanced()).toBe(true); - - const bfsIDs = tmm.bfs(node => node.key); - - expect(bfsIDs[0]).toBe(12); - expect(bfsIDs[1]).toBe(2); - expect(bfsIDs[2]).toBe(16); - - const bfsNodes = tmm.bfs(node => node); - - expect(bfsNodes[0].key).toBe(12); - expect(bfsNodes[1].key).toBe(2); - expect(bfsNodes[2].key).toBe(16); - - expect(tmm.count).toBe(6); - expect(tmm.getComputedCount()).toBe(8); + expect(tmm.getNode(-10)).toBeInstanceOf(TreeMultiMapNode); + expect(tmm.getNode(-20)).toBeInstanceOf(TreeMultiMapNode); + }); }); - it('should perform various operations on a Binary Search Tree with object values', () => { - const objTreeMultiMap = new TreeMultiMap(); - expect(objTreeMultiMap).toBeInstanceOf(TreeMultiMap); - objTreeMultiMap.add([11, { key: 11, keyA: 11 }]); - objTreeMultiMap.add([3, { key: 3, keyA: 3 }]); - const values: [number, { key: number; keyA: number }][] = [ - [15, { key: 15, keyA: 15 }], - [1, { key: 1, keyA: 1 }], - [8, { key: 8, keyA: 8 }], - [13, { key: 13, keyA: 13 }], - [16, { key: 16, keyA: 16 }], - [2, { key: 2, keyA: 2 }], - [6, { key: 6, keyA: 6 }], - [9, { key: 9, keyA: 9 }], - [12, { key: 12, keyA: 12 }], - [14, { key: 14, keyA: 14 }], - [4, { key: 4, keyA: 4 }], - [7, { key: 7, keyA: 7 }], - [10, { key: 10, keyA: 10 }], - [5, { key: 5, keyA: 5 }] - ]; + describe('deleteNode', () => { + it('should delete a node from the tmm', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.delete(20); - objTreeMultiMap.addMany(values); + expect(tmm.getNode(20)).toBe(undefined); + }); - expect(objTreeMultiMap.root).toBeInstanceOf(TreeMultiMapNode); + it('should handle deleting a non-existent node', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.delete(15); - if (objTreeMultiMap.root) expect(objTreeMultiMap.root.key).toBe(5); + expect(tmm.getNode(15)).toBe(undefined); + }); - expect(objTreeMultiMap.count).toBe(16); - expect(objTreeMultiMap.getComputedCount()).toBe(16); - - expect(objTreeMultiMap.has(6)).toBe(true); - }); -}); - -describe('TreeMultiMap delete test', function () { - const tmm = new TreeMultiMap(); - const inputSize = 1000; // Adjust input sizes as needed - - beforeEach(() => { - tmm.clear(); + it('should getNode performance O(log n)', () => { + for (let i = 0; i < 10; i++) tmm.add(i); + tmm.getNode(6); + }); }); - it(`Observe the time consumption of TreeMultiMap.dfs be good`, function () { - const startDFS = performance.now(); - const dfs = tmm.dfs(node => node); - if (isDebug) console.log('---bfs', performance.now() - startDFS, dfs.length); + describe('minimum', () => { + it('should find the minimum node in the tmm', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.add(15); + tmm.add(3); + + const minNode = tmm.getLeftMost(node => node, tmm.root); + expect(minNode?.key).toBe(3); + }); + + it('should handle an empty tmm', () => { + const minNode = tmm.getLeftMost(node => node, tmm.root); + expect(minNode).toBe(undefined); + }); }); - it('The structure remains normal after random deletion', function () { - for (let i = 0; i < inputSize; i++) { - tmm.add(i); - } + describe('getRightMost', () => { + it('should find the getRightMost node in the tmm', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.add(15); + tmm.add(25); - expect(tmm.size).toBe(inputSize); + const maxNode = tmm.getRightMost(node => node, tmm.root); + expect(maxNode?.key).toBe(25); + }); - for (let i = 0; i < inputSize; i++) { - const num = getRandomInt(0, inputSize - 1); - tmm.delete(num); - } - - let nilCount = 0; - const dfs = (cur: TreeMultiMapNode) => { - if (isNaN(cur.key)) nilCount++; - if (cur.left) dfs(cur.left); - if (cur.right) dfs(cur.right); - }; - if (tmm.root) dfs(tmm.root); - - expect(tmm.size).toBeLessThanOrEqual(inputSize); - expect(tmm.getHeight()).toBeGreaterThan(Math.log2(inputSize) - 1); - expect(tmm.getHeight()).toBeLessThan(Math.log2(inputSize) * 2); - - expect(nilCount).toBe(tmm.size + 1); + it('should handle an empty tmm', () => { + const maxNode = tmm.getRightMost(node => node, tmm.root); + expect(maxNode).toBe(undefined); + }); }); - it(`Random additions, complete deletions of structures are normal`, function () { - for (let i = 0; i < inputSize; i++) { - const num = getRandomInt(0, inputSize - 1); - if (i === 0 && isDebug) console.log(`first:`, num); - tmm.add(num); - } + describe('getSuccessor', () => { + it('should find the getSuccessor of a node', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.add(15); + tmm.add(25); - for (let i = 0; i < inputSize; i++) { - tmm.delete(i, true); - } + const node = tmm.getNode(15); + const successorNode = tmm.getSuccessor(node!); - let nilCount = 0; - const dfs = (cur: TreeMultiMapNode) => { - if (isNaN(cur.key)) nilCount++; - if (cur.left) dfs(cur.left); - if (cur.right) dfs(cur.right); - }; - if (tmm.root) dfs(tmm.root); + expect(successorNode?.key).toBe(20); + }); - expect(tmm.size).toBe(0); - expect(tmm.getHeight()).toBe(-1); - expect(nilCount).toBe(tmm.size + 1); + it('should handle a node with no getSuccessor', () => { + tmm.add(10); + tmm.add(5); - if (isDebug) tmm.print(); + const node = tmm.getNode(10); + const successorNode = tmm.getSuccessor(node!); + // TODO not sure if it should be undefined or tmm.NIL + expect(successorNode).toBe(undefined); + }); }); - it(`Random additions, count deletions of structures are normal`, function () { - for (let i = 0; i < inputSize; i++) { - const num = getRandomInt(0, inputSize - 1); - if (i === 0 && isDebug) console.log(`first:`, num); - tmm.add(num); - } + describe('getPredecessor', () => { + it('should find the getPredecessor of a node', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.add(15); + tmm.add(25); - for (let i = 0; i < inputSize; i++) { - tmm.delete(i); - } + const node = tmm.getNode(20); + const predecessorNode = tmm.getPredecessor(node!); - let nanCount = 0; - const dfs = (cur: TreeMultiMapNode) => { - if (isNaN(cur.key)) nanCount++; - if (cur.left) dfs(cur.left); - if (cur.right) dfs(cur.right); - }; - if (tmm.root) dfs(tmm.root); + expect(predecessorNode?.key).toBe(15); + }); - expect(tmm.size).toBeGreaterThanOrEqual(0); - expect(tmm.getHeight()).toBeGreaterThanOrEqual(0); - expect(nanCount).toBeLessThanOrEqual(inputSize); + it('should handle a node with no getPredecessor', () => { + tmm.add(10); + tmm.add(20); - if (isDebug) tmm.print(); + const node = tmm.getNode(20); + const predecessorNode = tmm.getPredecessor(node!); + // TODO not sure if it should be tmm.NIL or something else. + expect(predecessorNode).toBe(tmm.getNode(20)); + }); }); it('should the clone method', () => { @@ -734,7 +188,7 @@ describe('TreeMultiMap delete test', function () { expect(tmm.root?.left?.left?.key).toBe(NaN); expect(tmm.root?.left?.right?.key).toBe(NaN); expect(tmm.root?.right?.key).toBe('4'); - expect(tmm.root?.right?.left?.key).toBe(`3`); + expect(tmm.root?.right?.left?.key).toBe('3'); expect(tmm.root?.right?.right?.key).toBe('5'); tmm.delete('3'); checkTreeStructure(tmm); @@ -744,232 +198,650 @@ describe('TreeMultiMap delete test', function () { expect(tmm.size).toBe(4); expect(cloned.size).toBe(3); }); + + it('should add value', () => { + const tmm = new TreeMultiMap([4, 5, [1, ['1']], 2, 3]); + expect(tmm.get(1)).toEqual(['1']); + expect(tmm.getNode(1)?.value).toEqual([]); + tmm.add(1, 'a'); + expect(tmm.get(1)).toEqual(['1', 'a']); + tmm.add([1, ['b']]); + expect(tmm.getNode(1)?.value).toEqual([]); + expect(tmm.get(1)).toEqual(['1', 'a', 'b']); + const tmmMapped = new TreeMultiMap([4, 5, [1, ['1']], 2, 3]); + expect(tmmMapped.get(1)).toEqual(['1']); + expect(tmmMapped.getNode(1)?.value).toEqual([]); + tmmMapped.add(1, 'a'); + expect(tmmMapped.get(1)).toEqual(['1', 'a']); + tmmMapped.add([1, ['b']]); + expect(tmmMapped.getNode(1)?.value).toEqual([]); + expect(tmmMapped.get(1)).toEqual(['1', 'a', 'b']); + }); }); -describe('TreeMultiMap iterative methods test', () => { - let treeMM: TreeMultiMap; +describe('TreeMultiMap 2', () => { + let tmm: TreeMultiMap; + beforeEach(() => { - treeMM = new TreeMultiMap(); - treeMM.add(1, 'a', 10); - treeMM.add([2, 'b'], undefined, 10); - treeMM.add([3, 'c'], undefined, 1); + tmm = new TreeMultiMap(); }); - it('The node obtained by get Node should match the node type', () => { - const node3 = treeMM.getNode(3); - expect(node3).toBeInstanceOf(BinaryTreeNode); - expect(node3).toBeInstanceOf(BSTNode); - expect(node3).toBeInstanceOf(RedBlackTreeNode); + it('should add nodes into the tmm', () => { + tmm.add(10); + expect(tmm.getNode(10)).toBeDefined(); + tmm.add(20); + expect(tmm.getNode(20)).toBeDefined(); + tmm.add(5); + expect(tmm.getNode(5)).toBeDefined(); }); - it('forEach should iterate over all elements', () => { - const mockCallback = jest.fn(); - treeMM.forEach((key, value) => { - mockCallback(key, value); - }); - - expect(mockCallback.mock.calls.length).toBe(3); - expect(mockCallback.mock.calls[0]).toEqual([1, 'a']); - expect(mockCallback.mock.calls[1]).toEqual([2, 'b']); - expect(mockCallback.mock.calls[2]).toEqual([3, 'c']); + it('should delete nodes from the tmm', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.delete(20); + expect(tmm.getNode(20)).toBe(undefined); }); - it('filter should return a new tree with filtered elements', () => { - const filteredTree = treeMM.filter(key => key > 1); - expect(filteredTree.size).toBe(2); - expect([...filteredTree]).toEqual([ - [2, 'b'], - [3, 'c'] + it('should get the successor of a node', () => { + tmm.add(10); + tmm.add(20); + const node = tmm.getNode(10); + const successor = tmm.getSuccessor(node!); + expect(successor?.key).toBe(20); + }); + + it('should get the predecessor of a node', () => { + tmm.add(10); + tmm.add(20); + const node = tmm.getNode(20); + const predecessor = tmm.getPredecessor(node!); + expect(predecessor?.key).toBe(20); + }); + + it('should rotate nodes to the left', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + const node = tmm.getNode(10); + tmm.add(15); + // Verify that rotation has occurred + expect(node?.left?.key).toBe(5); + expect(node?.right?.key).toBe(20); + }); + + it('should rotate nodes to the right', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + const node = tmm.getNode(20); + tmm.add(25); + // Verify that rotation has occurred + expect(node?.left?.key).toBeNaN(); + expect(node?.right?.key).toBe(25); + }); + + it('should all node attributes fully conform to the red-black tmm standards.', () => { + tmm.add(10); + tmm.add(20); + tmm.add(5); + tmm.add(15); + tmm.add(21); + tmm.add(6); + tmm.add(2); + + let node10F = tmm.getNode(10); + let node20F = tmm.getNode(20); + let node5F = tmm.getNode(5); + let node15F = tmm.getNode(15); + let node21F = tmm.getNode(21); + let node6F = tmm.getNode(6); + let node2F = tmm.getNode(2); + expect(node10F?.key).toBe(10); + expect(node10F?.color).toBe('BLACK'); + expect(node10F?.left).toBe(node5F); + expect(node10F?.right).toBe(node20F); + expect(node10F?.parent).toBe(undefined); + expect(node20F?.key).toBe(20); + expect(node20F?.color).toBe('BLACK'); + expect(node20F?.left).toBe(node15F); + expect(node20F?.right).toBe(node21F); + expect(node20F?.parent).toBe(node10F); + expect(node5F?.key).toBe(5); + expect(node5F?.color).toBe('BLACK'); + expect(node5F?.left).toBe(node2F); + expect(node5F?.right).toBe(node6F); + expect(node5F?.parent).toBe(node10F); + expect(node15F?.key).toBe(15); + expect(node15F?.color).toBe('RED'); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(node20F); + expect(node21F?.key).toBe(21); + expect(node21F?.color).toBe('RED'); + expect(node21F?.left).toBe(tmm.NIL); + expect(node21F?.right).toBe(tmm.NIL); + expect(node21F?.parent).toBe(node20F); + expect(node6F?.key).toBe(6); + expect(node6F?.color).toBe('RED'); + expect(node6F?.left).toBe(tmm.NIL); + expect(node6F?.right).toBe(tmm.NIL); + expect(node6F?.parent).toBe(node5F); + expect(node2F?.key).toBe(2); + expect(node2F?.color).toBe('RED'); + expect(node2F?.left).toBe(tmm.NIL); + expect(node2F?.right).toBe(tmm.NIL); + expect(node2F?.parent).toBe(node5F); + expect(node15F?.key).toBe(15); + expect(node15F?.color).toBe('RED'); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(node20F); + tmm.delete(5); + node10F = tmm.getNode(10); + node20F = tmm.getNode(20); + node5F = tmm.getNode(5); + node15F = tmm.getNode(15); + node21F = tmm.getNode(21); + node6F = tmm.getNode(6); + node2F = tmm.getNode(2); + expect(node10F?.key).toBe(10); + expect(node10F?.color).toBe('BLACK'); + expect(node10F?.left).toBe(node6F); + expect(node10F?.right).toBe(node20F); + expect(node10F?.parent).toBe(undefined); + expect(node20F?.key).toBe(20); + expect(node20F?.color).toBe('BLACK'); + expect(node20F?.left).toBe(node15F); + expect(node20F?.right).toBe(node21F); + expect(node20F?.parent).toBe(node10F); + expect(node5F).toBe(undefined); + expect(node15F?.key).toBe(15); + expect(node15F?.color).toBe('RED'); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(node20F); + expect(node21F?.key).toBe(21); + expect(node21F?.color).toBe('RED'); + expect(node21F?.left).toBe(tmm.NIL); + expect(node21F?.right).toBe(tmm.NIL); + expect(node21F?.parent).toBe(node20F); + expect(node6F?.key).toBe(6); + expect(node6F?.color).toBe('BLACK'); + expect(node6F?.left).toBe(node2F); + expect(node6F?.right).toBe(tmm.NIL); + expect(node6F?.parent).toBe(node10F); + expect(node2F?.key).toBe(2); + expect(node2F?.color).toBe('RED'); + expect(node2F?.left).toBe(tmm.NIL); + expect(node2F?.right).toBe(tmm.NIL); + expect(node2F?.parent).toBe(node6F); + expect(node15F?.key).toBe(15); + expect(node15F?.color).toBe('RED'); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(node20F); + tmm.delete(20); + node10F = tmm.getNode(10); + node20F = tmm.getNode(20); + node5F = tmm.getNode(5); + node15F = tmm.getNode(15); + node21F = tmm.getNode(21); + node6F = tmm.getNode(6); + node2F = tmm.getNode(2); + expect(node10F?.key).toBe(10); + expect(node10F?.color).toBe('BLACK'); + expect(node10F?.left).toBe(node6F); + expect(node10F?.right).toBe(node21F); + expect(node10F?.parent).toBe(undefined); + expect(node20F).toBe(undefined); + expect(node5F).toBe(undefined); + expect(node15F?.key).toBe(15); + expect(node15F?.color).toBe('RED'); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(node21F); + expect(node21F?.key).toBe(21); + expect(node21F?.color).toBe('BLACK'); + expect(node21F?.left).toBe(node15F); + expect(node21F?.right).toBe(tmm.NIL); + expect(node21F?.parent).toBe(node10F); + expect(node6F?.key).toBe(6); + expect(node6F?.color).toBe('BLACK'); + expect(node6F?.left).toBe(node2F); + expect(node6F?.right).toBe(tmm.NIL); + expect(node6F?.parent).toBe(node10F); + expect(node2F?.key).toBe(2); + expect(node2F?.color).toBe('RED'); + expect(node2F?.left).toBe(tmm.NIL); + expect(node2F?.right).toBe(tmm.NIL); + expect(node2F?.parent).toBe(node6F); + expect(node15F?.key).toBe(15); + expect(node15F?.color).toBe('RED'); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(node21F); + }); + + it('should fix the tmm after insertion', () => { + tmm.add(1); + tmm.add(2); + tmm.add(5); + tmm.add(15); + const node15F = tmm.getNode(15); + expect(node15F?.left).toBe(tmm.NIL); + expect(node15F?.right).toBe(tmm.NIL); + expect(node15F?.parent).toBe(tmm.getNode(5)); + + tmm.add(25); + tmm.add(10); + tmm.add(8); + tmm.add(28); + tmm.add(111); + tmm.add(12); + tmm.delete(2); + tmm.add(22); + tmm.add(50); + tmm.add(155); + tmm.add(225); + const node225F = tmm.getNode(225); + expect(node225F?.left).toBe(tmm.NIL); + expect(node225F?.right).toBe(tmm.NIL); + expect(node225F?.parent?.key).toBe(155); + tmm.add(7); + if (isDebug) tmm.print(); + + const node15S = tmm.getNode(15); + expect(node15S?.left?.key).toBe(10); + expect(node15S?.right?.key).toBe(25); + expect(tmm.root).toBe(tmm.getNode(8)); + expect(node15S?.parent?.key).toBe(28); + tmm.delete(15); + expect(tmm.root?.key).toBe(8); + expect(tmm.root?.parent).toBe(undefined); + + const node15T = tmm.getNode(15); + expect(node15T).toBe(undefined); + + tmm.add(23); + tmm.add(33); + tmm.add(15); + + const nodeLM = tmm.getLeftMost(); + expect(nodeLM).toBe(1); + + const node50 = tmm.getNode(50); + expect(node50?.key).toBe(50); + expect(node50?.left?.key).toBe(33); + expect(node50?.right).toBe(tmm.NIL); + const node15Fo = tmm.getNode(15); + + expect(node15Fo?.key).toBe(15); + expect(node15Fo?.left).toBe(tmm.NIL); + const node225S = tmm.getNode(225); + expect(node225S?.left).toBe(tmm.NIL); + expect(node225S?.right).toBe(tmm.NIL); + expect(node225S?.parent?.key).toBe(155); + // TODO + // expect(tmm.getNode(0)).toBe(undefined); + tmm.add(2); + tmm.add(3); + tmm.add(4); + tmm.add(6); + tmm.add(9); + tmm.add(11); + tmm.add(13); + tmm.add(14); + tmm.add(16); + tmm.add(17); + tmm.add(18); + tmm.add(19); + tmm.add(110); + + if (isDebug) tmm.print(); + + expect(tmm.dfs()).toEqual([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 25, 28, 33, 50, 110, 111, 155, 225 ]); + + expect(tmm.isBST()).toBe(true); }); - it('map should return a new tree with modified elements', () => { - const mappedTree = treeMM.map((key, value) => [(key * 2).toString(), value]); - expect(mappedTree.size).toBe(3); - expect([...mappedTree]).toEqual([ - ['2', 'a'], - ['4', 'b'], - ['6', 'c'] - ]); - }); - - it('reduce should accumulate values', () => { - const sum = treeMM.reduce((acc, value, key) => acc + key, 0); - expect(sum).toBe(6); - }); - - it('[Symbol.iterator] should provide an iterator', () => { - const entries = []; - for (const entry of treeMM) { - entries.push(entry); + it('should fix the tmm after insertion and deletion', () => { + for (let i = 0; i < 100; i++) { + tmm.add(i); + } + for (let i = 0; i < 49; i++) { + tmm.delete(i); } - expect(entries.length).toBe(3); - expect(entries).toEqual([ - [1, 'a'], - [2, 'b'], - [3, 'c'] + expect(tmm.size).toBe(51); + expect(tmm.isBST()).toBe(true); + expect(tmm.isBST(tmm.root, 'RECURSIVE')).toBe(true); + + expect(tmm.dfs(n => n.key, 'IN', 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([ + 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 ]); }); - it('should clone work well', () => { - expect(treeMM.count).toBe(21); - expect(treeMM.getComputedCount()).toBe(21); - const cloned = treeMM.clone(); - expect(cloned.root?.left?.key).toBe(1); - if (cloned.isMapMode) expect(cloned.get(cloned.root?.right)).toBe('c'); - else expect(cloned.root?.right?.value).toBe(undefined); + it('should fix the tmm after large scale insertion and deletion', () => { + for (let i = 0; i < 10000; i++) { + tmm.add(i); + } + for (let i = 0; i < 10000; i++) { + tmm.delete(i); + } + + expect(tmm.size).toBe(0); + expect(tmm.isBST()).toBe(true); + expect(tmm.dfs(n => n.key, 'IN', tmm.root, 'ITERATIVE')).toEqual([]); + + tmm.clear(); + for (let i = 0; i < 1000; i++) { + tmm.add(getRandomInt(-100, 1000)); + tmm.delete(getRandomInt(-100, 1000)); + } + + // TODO there is a bug when dfs the tmm with NIL node + // expect(tmm.isBST()).toBe(true); }); - it('should keys', () => { - const keys = treeMM.keys(); - expect([...keys]).toEqual([1, 2, 3]); - }); - - it('should values', () => { - const values = treeMM.values(); - expect([...values]).toEqual(['a', 'b', 'c']); - }); - - it('should leaves', () => { - const leaves = treeMM.leaves(); - expect(leaves).toEqual([1, 3]); - }); -}); - -describe('TreeMultiMap count not map mode', () => { - let tmm: TreeMultiMap; - beforeEach(() => { - tmm = new TreeMultiMap([], { isMapMode: false }); - }); - - it('Should added node count ', () => { - tmm.addMany([ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5] - ]); - const newNode = new TreeMultiMapNode(3, undefined, 10); - tmm.add(newNode, 33, 20); - // TODO expect(tmm.count).toBe(25); - expect(tmm.count).toBe(15); - expect(tmm.getComputedCount()).toBe(15); - expect(tmm.getNode(3)?.count).toBe(11); - }); -}); - -describe('TreeMultiMap operations test1 not map mode', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const tmm = new TreeMultiMap([], { isMapMode: false }); - - expect(tmm instanceof TreeMultiMap); - - tmm.add([11, 11]); - tmm.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - tmm.addMany(idAndValues); - expect(tmm.root instanceof TreeMultiMapNode); - - if (tmm.root) expect(tmm.root.key == 11); - - expect(tmm.size).toBe(16); - expect(tmm.count).toBe(18); - expect(tmm.getComputedCount()).toBe(18); - - expect(tmm.has(6)); + it('duplicates', () => { + tmm.addMany([9, 8, 7, 8, 8, 8, 2, 3, 6, 5, 5, 4]); if (isDebug) tmm.print(); - expect(tmm.getHeight(6)).toBe(1); - expect(tmm.getDepth(6)).toBe(3); - const nodeId10 = tmm.getNode(10); - expect(nodeId10?.key).toBe(10); - const nodeVal9 = tmm.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); + expect(tmm.size).toBe(8); + expect(tmm.isBST()).toBe(true); + expect(tmm.isAVLBalanced()).toBe(true); + tmm.addMany([10, 5, 2, 11]); + expect(tmm.size).toBe(10); + expect(tmm.isBST()).toBe(true); + expect(tmm.isAVLBalanced()).toBe(true); + + tmm.clear(); + tmm.addMany([10, 20, 30, 40, 50, 60]); + expect(tmm.isAVLBalanced()).toBe(false); }); -}); -describe('TreeMultiMap operations test recursively1 not map mode', () => { - it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const tmm = new TreeMultiMap([], { - iterationType: 'RECURSIVE', - isMapMode: false + describe('TreeMultiMap delete test', function () { + const tmm = new TreeMultiMap(); + const inputSize = 100; // Adjust input sizes as needed + + beforeEach(() => { + tmm.clear(); + }); + it('The structure remains normal after random deletion', function () { + for (let i = 0; i < inputSize; i++) { + tmm.add(i); + } + + for (let i = 0; i < inputSize; i++) { + const num = getRandomInt(0, inputSize - 1); + tmm.delete(num); + } + + let nanCount = 0; + const dfs = (cur: TreeMultiMapNode) => { + if (isNaN(cur.key)) nanCount++; + if (cur.left) dfs(cur.left); + if (cur.right) dfs(cur.right); + }; + if (tmm.root) dfs(tmm.root); + + expect(tmm.size).toBeLessThanOrEqual(inputSize); + expect(tmm.getHeight()).toBeLessThan(Math.log2(inputSize) * 2); + + expect(nanCount).toBeLessThanOrEqual(inputSize); }); - expect(tmm instanceof TreeMultiMap); - tmm.add([11, 11]); - tmm.add([3, 3]); - const idAndValues: [number, number][] = [ - [11, 11], - [3, 3], - [15, 15], - [1, 1], - [8, 8], - [13, 13], - [16, 16], - [2, 2], - [6, 6], - [9, 9], - [12, 12], - [14, 14], - [4, 4], - [7, 7], - [10, 10], - [5, 5] - ]; - tmm.addMany(idAndValues); - expect(tmm.root).toBeInstanceOf(TreeMultiMapNode); + it(`Random additions, complete deletions of structures are normal`, function () { + for (let i = 0; i < inputSize; i++) { + const num = getRandomInt(0, inputSize - 1); + if (i === 0 && isDebug) console.log(`first:`, num); + tmm.add(num); + } - if (tmm.root) expect(tmm.root.key).toBe(5); + for (let i = 0; i < inputSize; i++) { + tmm.delete(i); + } - expect(tmm.size).toBe(16); - expect(tmm.count).toBe(18); - expect(tmm.getComputedCount()).toBe(18); + let nanCount = 0; + const dfs = (cur: TreeMultiMapNode) => { + if (isNaN(cur.key)) nanCount++; + if (cur.left) dfs(cur.left); + if (cur.right) dfs(cur.right); + }; + if (tmm.root) dfs(tmm.root); - expect(tmm.has(6)); + expect(tmm.size).toBe(0); + expect(tmm.getHeight()).toBe(-1); + expect(nanCount).toBeLessThanOrEqual(inputSize); - expect(tmm.getHeight(6)).toBe(1); - expect(tmm.getDepth(6)).toBe(3); - const nodeId10 = tmm.getNode(10); - expect(nodeId10?.key).toBe(10); + if (isDebug) tmm.print(); + }); + }); - const nodeVal9 = tmm.getNode(node => node.key === 9); - expect(nodeVal9?.key).toBe(9); + describe('TreeMultiMap iterative methods test', () => { + let tmm: TreeMultiMap; + beforeEach(() => { + tmm = new TreeMultiMap(); + tmm.add([1, ['a']]); + tmm.add(2, 'b'); + tmm.add([3, ['c']]); + }); + + it('The node obtained by get Node should match the node type', () => { + const node3 = tmm.getNode(3); + expect(node3).toBeInstanceOf(BinaryTreeNode); + expect(node3).toBeInstanceOf(BSTNode); + expect(node3).toBeInstanceOf(TreeMultiMapNode); + }); + + it('forEach should iterate over all elements', () => { + const mockCallback = jest.fn(); + tmm.forEach((key, value) => { + mockCallback(key, value); + }); + + expect(mockCallback.mock.calls.length).toBe(3); + expect(mockCallback.mock.calls[0]).toEqual([1, ['a']]); + expect(mockCallback.mock.calls[1]).toEqual([2, ['b']]); + expect(mockCallback.mock.calls[2]).toEqual([3, ['c']]); + }); + + it('filter should return a new tmm with filtered elements', () => { + const filteredTree = tmm.filter(key => key > 1); + expect(filteredTree.size).toBe(2); + expect([...filteredTree]).toEqual([ + [2, ['b']], + [3, ['c']] + ]); + }); + + it('map should return a new tmm with modified elements', () => { + const tmmMapped = tmm.map((key, value) => [(key * 2).toString(), value]); + expect(tmmMapped.size).toBe(3); + expect([...tmmMapped]).toEqual([ + ['2', ['a']], + ['4', ['b']], + ['6', ['c']] + ]); + }); + + it('reduce should accumulate values', () => { + const sum = tmm.reduce((acc, value, key) => acc + key, 0); + expect(sum).toBe(6); + }); + + it('[Symbol.iterator] should provide an iterator', () => { + const entries = []; + for (const entry of tmm) { + entries.push(entry); + } + + expect(entries.length).toBe(3); + expect(entries).toEqual([ + [1, ['a']], + [2, ['b']], + [3, ['c']] + ]); + }); }); }); -describe('TreeMultiMap iterative methods test not map mode', () => { - let treeMM: TreeMultiMap; +describe('TreeMultiMap - _deleteFixup', () => { + let tmm: TreeMultiMap; + beforeEach(() => { - treeMM = new TreeMultiMap([], { isMapMode: false }); - treeMM.add(1, 'a', 10); - treeMM.add([2, 'b'], undefined, 10); - treeMM.add([3, 'c'], undefined, 1); + tmm = new TreeMultiMap(); }); - it('should clone work well', () => { - expect(treeMM.count).toBe(21); - expect(treeMM.getComputedCount()).toBe(21); - const cloned = treeMM.clone(); - expect(cloned.root?.left?.key).toBe(1); - expect(cloned.get(cloned.root?.right)).toBe(undefined); + it('should handle deleting a red leaf node', () => { + tmm.add(10, 10); + tmm.add(5, 5); // Red leaf + tmm.add(20, 20); + + expect(tmm.delete(5)).toHaveLength(1); // Delete red leaf + expect(tmm.root?.left).toBe(tmm.NIL); // Left child should be NIL + }); + + it('should handle deleting a black leaf node', () => { + tmm.add(10, 10); + tmm.add(5, 5); // Black node + tmm.add(20, 20); + tmm.add(1, 1); // Black leaf node + + expect(tmm.delete(1)).toHaveLength(1); // Delete black leaf + expect(tmm.root?.left?.left).toBe(tmm.NIL); + }); + + it('should handle deleting black node with red sibling', () => { + tmm.add(10, 10); + tmm.add(5, 5); // Black node + tmm.add(20, 20); // Red sibling + tmm.add(25, 25); // Force the sibling to be red + + expect(tmm.delete(5)).toHaveLength(1); // Delete black node + expect(tmm.root?.right?.color).toBe('BLACK'); // Ensure sibling color is black after fixup + }); + + it('should handle deleting black node with black sibling', () => { + tmm.add(10, 10); + tmm.add(5, 5); // Black node + tmm.add(20, 20); // Black sibling + + expect(tmm.delete(5)).toHaveLength(1); // Delete black node + expect(tmm.root?.left).toBe(tmm.NIL); + }); + + it('should handle deleting the root node', () => { + tmm.add(10, 10); // Root node + tmm.add(5, 5); + tmm.add(20, 20); + + expect(tmm.delete(10)).toHaveLength(1); // Delete root node + expect(tmm.root?.key).toBe(20); // New root should be 20 + }); + + it('should handle complex case with multiple rotations', () => { + tmm.add(10, 10); + tmm.add(5, 5); + tmm.add(15, 15); + tmm.add(12, 12); + tmm.add(18, 18); + tmm.add(16, 16); + + // Delete a node that will cause rotations and color changes + expect(tmm.delete(5)).toHaveLength(1); + + // Verify the color and structure after fixup + expect(tmm.root?.color).toBe('BLACK'); + expect(tmm.root?.left).toBe(tmm.NIL); + expect(tmm.root?.right?.left?.color).toBe('BLACK'); + }); + + it('should handle complex delete fixup scenarios', () => { + const tmm = new TreeMultiMap(); + + // Build a tmm that will require complex fixup + tmm.add(20, 20); + tmm.add(10, 10); + tmm.add(30, 30); + tmm.add(5, 5); + tmm.add(15, 15); + tmm.add(25, 25); + tmm.add(35, 35); + tmm.add(2, 2); + tmm.add(8, 8); + + // This deletion should trigger a complex fixup + tmm.delete(2); + // tmm.print(tmm.root, { isShowNull: true, isShowRedBlackNIL: true, isShowUndefined: false }); + + expect(tmm.isLeaf(2)).toBe(false); + expect(tmm.isLeaf(8)).toBe(true); + expect(tmm.isLeaf(15)).toBe(true); + expect(tmm.isLeaf(25)).toBe(true); + expect(tmm.isLeaf(35)).toBe(true); + expect(tmm.isLeaf(20)).toBe(false); + expect(tmm.isLeaf(30)).toBe(false); + // Verify tmm structure and colors after fixup + expect(tmm.root?.color).toBe('BLACK'); + expect(tmm.root?.key).toBe(20); + expect(tmm.root?.left?.color).toBe('RED'); + expect(tmm.root?.left?.key).toBe(10); + expect(tmm.root?.right?.color).toBe('BLACK'); + expect(tmm.root?.right?.key).toBe(30); + expect(tmm.root?.left?.left?.color).toBe('BLACK'); + expect(tmm.root?.left?.left?.key).toBe(5); + expect(tmm.root?.left?.right?.color).toBe('BLACK'); + expect(tmm.root?.left?.right?.key).toBe(15); + expect(tmm.leaves(node => (node === null ? '' : `${node.key} ${node.color}`), tmm.root, 'RECURSIVE')).toEqual([ + '8 RED', + '15 BLACK', + '25 RED', + '35 RED' + ]); + expect(tmm.listLevels(node => (node === tmm.NIL ? 'NIL' : `${node.key} ${node.color}`))).toEqual([ + ['20 BLACK'], + ['10 RED', '30 BLACK'], + ['5 BLACK', '15 BLACK', '25 RED', '35 RED'], + ['NIL', '8 RED', 'NIL', 'NIL', 'NIL', 'NIL', 'NIL', 'NIL'], + ['NIL', 'NIL'] + ]); + }); +}); + +describe('real world data', () => { + it('cost of living', () => { + const indexedByRank = new TreeMultiMap(costOfLiving, { + specifyComparable: node => node.rank, + toEntryFn: raw => [raw, undefined] + }); + expect(indexedByRank.size).toBe(7); + expect(indexedByRank.dfs(node => node?.key?.country)).toEqual([ + 'Switzerland', + 'New Zealand', + 'Mexico', + 'South Africa', + 'Japan', + 'Brazil', + 'Taiwan' + ]); + }); +}); + +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(15, 20))).toEqual([15, 18]); }); }); diff --git a/test/unit/data-structures/hash/hash-map.test.ts b/test/unit/data-structures/hash/hash-map.test.ts index 9123563..510edb9 100644 --- a/test/unit/data-structures/hash/hash-map.test.ts +++ b/test/unit/data-structures/hash/hash-map.test.ts @@ -343,7 +343,7 @@ describe('HashMap', () => { }); it('filter() should remove elements that do not match the condition', () => { - const filteredHashMap = hashMap.filter((key, value) => key !== 'key1'); + const filteredHashMap = hashMap.filter(key => key !== 'key1'); expect(filteredHashMap.has('key1')).toBe(false); }); diff --git a/tsconfig-mjs.json b/tsconfig-esm.json similarity index 90% rename from tsconfig-mjs.json rename to tsconfig-esm.json index afdba48..77c9a70 100644 --- a/tsconfig-mjs.json +++ b/tsconfig-esm.json @@ -2,7 +2,7 @@ "extends": "./tsconfig-base.json", "compilerOptions": { "rootDir": "./src/", - "outDir": "dist/mjs", + "outDir": "dist/esm", "module": "ESNext", "target": "ESNext", "moduleResolution": "Node",