diff --git a/.prettierignore b/.prettierignore index 76e5fe5..3ad4bf6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ src/types/data-structures/binary-tree/binary-tree.ts src/types/data-structures/binary-tree/bst.ts src/types/data-structures/binary-tree/avl-tree.ts src/types/data-structures/binary-tree/rb-tree.ts -src/types/data-structures/binary-tree/tree-multimap.ts +src/types/data-structures/binary-tree/tree-multi-map.ts +src/types/data-structures/binary-tree/avl-tree-multi-map.ts diff --git a/README.md b/README.md index caa1712..461ab5d 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ yarn add data-structure-typed ```js import { Heap, Graph, Queue, Deque, PriorityQueue, BST, Trie, DoublyLinkedList, - AVLTree, SinglyLinkedList, DirectedGraph, RedBlackTree, TreeMultimap, + AVLTree, SinglyLinkedList, DirectedGraph, RedBlackTree, TreeMultiMap, DirectedVertex, Stack, AVLTreeNode } from 'data-structure-typed'; ``` @@ -273,7 +273,7 @@ avl.print(); // / \ // 7 9 -const treeMulti = new TreeMultimap(entries); +const treeMulti = new TreeMultiMap(entries); treeMulti.print(); // ___4___ // / \ @@ -516,7 +516,7 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', ' Tree Multimap -View +View Heap @@ -702,13 +702,13 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', ' - - TreeMultimap<K, V> + TreeMultiMap<K, V> multimap<K, V> - - - TreeMultimap<E> + TreeMultiMap<E> multiset<T> - - @@ -1017,7 +1017,7 @@ Copy the code below into the script tag of your HTML, and you're good to go with const {Heap} = dataStructureTyped; const { BinaryTree, Graph, Queue, Stack, PriorityQueue, BST, Trie, DoublyLinkedList, - AVLTree, MinHeap, SinglyLinkedList, DirectedGraph, TreeMultimap, + AVLTree, MinHeap, SinglyLinkedList, DirectedGraph, TreeMultiMap, DirectedVertex, AVLTreeNode } = dataStructureTyped; ``` \ No newline at end of file diff --git a/README_zh-CN.md b/README_zh-CN.md index 2b494b7..00ec82a 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -121,7 +121,7 @@ yarn add data-structure-typed ```js import { BinaryTree, Graph, Queue, Stack, PriorityQueue, BST, Trie, DoublyLinkedList, - AVLTree, MinHeap, SinglyLinkedList, DirectedGraph, TreeMultimap, + AVLTree, MinHeap, SinglyLinkedList, DirectedGraph, TreeMultiMap, DirectedVertex, AVLTreeNode } from 'data-structure-typed'; ``` @@ -148,7 +148,7 @@ import { const {Heap} = dataStructureTyped; const { BinaryTree, Graph, Queue, Stack, PriorityQueue, BST, Trie, DoublyLinkedList, - AVLTree, MinHeap, SinglyLinkedList, DirectedGraph, TreeMultimap, + AVLTree, MinHeap, SinglyLinkedList, DirectedGraph, TreeMultiMap, DirectedVertex, AVLTreeNode } = dataStructureTyped; ``` @@ -442,7 +442,7 @@ avl.print(); // / \ // 7 9 -const treeMulti = new TreeMultimap(entries); +const treeMulti = new TreeMultiMap(entries); treeMulti.print(); // ___4___ // / \ @@ -563,7 +563,7 @@ avl2.print(); Tree Multimap -View +View Heap @@ -743,13 +743,13 @@ avl2.print(); - - TreeMultimap<K, V> + TreeMultiMap<K, V> multimap<K, V> - - - TreeMultimap<E> + TreeMultiMap<E> multiset<T> - - diff --git a/src/data-structures/binary-tree/tree-multimap.ts b/src/data-structures/binary-tree/avl-tree-multi-map.ts similarity index 91% rename from src/data-structures/binary-tree/tree-multimap.ts rename to src/data-structures/binary-tree/avl-tree-multi-map.ts index 2c7c4ac..07bec46 100644 --- a/src/data-structures/binary-tree/tree-multimap.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -6,22 +6,22 @@ * @license MIT License */ import type { + AVLTreeMultiMapNested, + AVLTreeMultiMapNodeNested, + AVLTreeMultiMapOptions, BinaryTreeDeleteResult, BSTNKeyOrNode, BTNCallback, - KeyOrNodeOrEntry, - TreeMultimapNested, - TreeMultimapNodeNested, - TreeMultimapOptions + KeyOrNodeOrEntry } from '../../types'; import { FamilyPosition, IterationType } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { AVLTree, AVLTreeNode } from './avl-tree'; -export class TreeMultimapNode< +export class AVLTreeMultiMapNode< K = any, V = any, - NODE extends TreeMultimapNode = TreeMultimapNodeNested + NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNodeNested > extends AVLTreeNode { /** * The constructor function initializes a BinaryTreeNode object with a key, value, and count. @@ -59,17 +59,17 @@ export class TreeMultimapNode< } /** - * The only distinction between a TreeMultimap and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters. + * 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 TreeMultimap< +export class AVLTreeMultiMap< K = any, V = any, - NODE extends TreeMultimapNode = TreeMultimapNode>, - TREE extends TreeMultimap = TreeMultimap> + NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNode>, + TREE extends AVLTreeMultiMap = AVLTreeMultiMap> > extends AVLTree implements IBinaryTree { - constructor(keysOrNodesOrEntries: Iterable> = [], options?: TreeMultimapOptions) { + constructor(keysOrNodesOrEntries: Iterable> = [], options?: AVLTreeMultiMapOptions) { super([], options); if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } @@ -98,20 +98,20 @@ export class TreeMultimap< * @returns A new instance of the BSTNode class with the specified key, value, and count (if provided). */ override createNode(key: K, value?: V, count?: number): NODE { - return new TreeMultimapNode(key, value, count) as NODE; + return new AVLTreeMultiMapNode(key, value, count) as NODE; } /** - * The function creates a new TreeMultimap object with the specified options and returns it. + * 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 `TreeMultimap` object. It can include properties such as + * configuration options for creating the `AVLTreeMultiMap` object. It can include properties such as * `iterationType` and `variant`, which are used to specify the type of iteration and the variant of * the tree, respectively. These properties can be - * @returns a new instance of the `TreeMultimap` class, with the provided options merged with the + * @returns a new instance of the `AVLTreeMultiMap` class, with the provided options merged with the * default options. The returned value is casted as `TREE`. */ - override createTree(options?: TreeMultimapOptions): TREE { - return new TreeMultimap([], { + override createTree(options?: AVLTreeMultiMapOptions): TREE { + return new AVLTreeMultiMap([], { iterationType: this.iterationType, variant: this.variant, ...options @@ -155,13 +155,13 @@ export class TreeMultimap< } /** - * The function checks if an keyOrNodeOrEntry is an instance of the TreeMultimapNode class. + * The function checks if an keyOrNodeOrEntry is an instance of the AVLTreeMultiMapNode class. * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter is of type `KeyOrNodeOrEntry`. - * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the TreeMultimapNode + * @returns a boolean value indicating whether the keyOrNodeOrEntry is an instance of the AVLTreeMultiMapNode * class. */ override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { - return keyOrNodeOrEntry instanceof TreeMultimapNode; + return keyOrNodeOrEntry instanceof AVLTreeMultiMapNode; } /** diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 702e3c7..d8366f1 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -708,9 +708,8 @@ export class BST< const compared = this._compare(cur.key, targetKey); if (compared === lesserOrGreater) ans.push(callback(cur)); - if (!cur.left && !cur.right) return; - if (cur.left && this._compare(cur.left.key, targetKey) === lesserOrGreater) _traverse(cur.left); - if (cur.right && this._compare(cur.right.key, targetKey) === lesserOrGreater) _traverse(cur.right); + if (this.isRealNode(cur.left)) _traverse(cur.left); + if (this.isRealNode(cur.right)) _traverse(cur.right); }; _traverse(this.root); @@ -719,12 +718,12 @@ export class BST< const queue = new Queue([this.root]); while (queue.size > 0) { const cur = queue.shift(); - if (cur) { + if (this.isRealNode(cur)) { const compared = this._compare(cur.key, targetKey); if (compared === lesserOrGreater) ans.push(callback(cur)); - if (cur.left && this._compare(cur.left.key, targetKey) === lesserOrGreater) queue.push(cur.left); - if (cur.right && this._compare(cur.right.key, targetKey) === lesserOrGreater) queue.push(cur.right); + if (this.isRealNode(cur.left)) queue.push(cur.left); + if (this.isRealNode(cur.right)) queue.push(cur.right); } } return ans; diff --git a/src/data-structures/binary-tree/index.ts b/src/data-structures/binary-tree/index.ts index 05f0f9b..0d38e6a 100644 --- a/src/data-structures/binary-tree/index.ts +++ b/src/data-structures/binary-tree/index.ts @@ -4,4 +4,5 @@ export * from './binary-indexed-tree'; export * from './segment-tree'; export * from './avl-tree'; export * from './rb-tree'; -export * from './tree-multimap'; +export * from './avl-tree-multi-map'; +export * from './tree-multi-map'; diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index f441046..1330d83 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -527,12 +527,14 @@ export class RedBlackTree< */ protected _fixInsert(k: NODE): void { let u: NODE | undefined; - while (k.parent && k.parent.color === 1) { + while (k.parent && k.parent.color === RBTNColor.RED) { if (k.parent.parent && k.parent === k.parent.parent.right) { u = k.parent.parent.left; - if (u && u.color === 1) { - u.color = RBTNColor.BLACK; + + if (u && u.color === RBTNColor.RED) { + // Delay color flip k.parent.color = RBTNColor.BLACK; + u.color = RBTNColor.BLACK; k.parent.parent.color = RBTNColor.RED; k = k.parent.parent; } else { @@ -541,16 +543,20 @@ export class RedBlackTree< this._rightRotate(k); } - k.parent!.color = RBTNColor.BLACK; - k.parent!.parent!.color = RBTNColor.RED; + // Check color before rotation + if (k.parent!.color === RBTNColor.RED) { + k.parent!.color = RBTNColor.BLACK; + k.parent!.parent!.color = RBTNColor.RED; + } this._leftRotate(k.parent!.parent!); } } else { - u = k.parent.parent!.right; + u = k.parent!.parent!.right; - if (u && u.color === 1) { - u.color = RBTNColor.BLACK; + if (u && u.color === RBTNColor.RED) { + // Delay color flip k.parent.color = RBTNColor.BLACK; + u.color = RBTNColor.BLACK; k.parent.parent!.color = RBTNColor.RED; k = k.parent.parent!; } else { @@ -559,11 +565,15 @@ export class RedBlackTree< this._leftRotate(k); } - k.parent!.color = RBTNColor.BLACK; - k.parent!.parent!.color = RBTNColor.RED; + // Check color before rotation + if (k.parent!.color === RBTNColor.RED) { + k.parent!.color = RBTNColor.BLACK; + k.parent!.parent!.color = RBTNColor.RED; + } this._rightRotate(k.parent!.parent!); } } + if (k === this.root) { break; } diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts new file mode 100644 index 0000000..ec32c77 --- /dev/null +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -0,0 +1,463 @@ +/** + * data-structure-typed + * + * @author Tyler Zeng + * @copyright Copyright (c) 2022 Tyler Zeng + * @license MIT License + */ +import type { + BinaryTreeDeleteResult, + BSTNKeyOrNode, + BTNCallback, + KeyOrNodeOrEntry, + TreeMultiMapNested, + TreeMultiMapNodeNested, + TreeMultiMapOptions +} from '../../types'; +import { IterationType, RBTNColor } from '../../types'; +import { IBinaryTree } from '../../interfaces'; +import { RedBlackTree, RedBlackTreeNode } from './rb-tree'; + +export class TreeMultiMapNode< + K = any, + V = any, + NODE extends TreeMultiMapNode = TreeMultiMapNodeNested +> extends RedBlackTreeNode { + /** + * The constructor function initializes an instance of a class with a key, value, and count. + * @param {K} key - The key parameter is of type K, which represents the type of the key for the + * constructor. It is required and must be provided when creating an instance of the class. + * @param {V} [value] - The `value` parameter is an optional parameter of type `V`. It represents the + * value associated with the key in the constructor. If no value is provided, it will be `undefined`. + * @param [count=1] - The "count" parameter is an optional parameter that specifies the number of + * times the key-value pair should be repeated. If no value is provided for "count", it defaults to + * 1. + */ + constructor(key: K, value?: V, count = 1) { + super(key, value); + this.count = count; + } + + protected _count: number = 1; + + /** + * The function returns the value of the private variable _count. + * @returns The count property of the object, which is of type number. + */ + get count(): number { + return this._count; + } + + /** + * The above function sets the value of the count property. + * @param {number} value - The value parameter is of type number, which means it can accept any + * numeric value. + */ + set count(value: number) { + this._count = value; + } +} + +export class TreeMultiMap< + K = any, + V = any, + NODE extends TreeMultiMapNode = TreeMultiMapNode>, + TREE extends TreeMultiMap = TreeMultiMap> +> + extends RedBlackTree + implements IBinaryTree { + /** + * The constructor function initializes a new instance of the TreeMultiMap class with optional + * initial keys, nodes, or entries. + * @param keysOrNodesOrEntries - The `keysOrNodesOrEntries` parameter is an iterable object that can + * contain keys, nodes, or entries. It is used to initialize the TreeMultiMap with the provided keys, + * nodes, or entries. + * @param [options] - The `options` parameter is an optional object that can be passed to the + * constructor. It allows you to customize the behavior of the `TreeMultiMap` instance. + */ + constructor(keysOrNodesOrEntries: Iterable> = [], options?: TreeMultiMapOptions) { + super([], options); + if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); + } + + 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 { + let sum = 0; + this.dfs(node => (sum += node.count)); + return sum; + // return this._count; + } + + /** + * The function creates a new TreeMultiMapNode object 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 TreeMultiMap. It is an optional parameter, so if it is not provided, it will + * default to 1. + * @returns a new instance of the TreeMultiMapNode class, casted as NODE. + */ + override createNode(key: K, value?: V, count?: number): NODE { + return new TreeMultiMapNode(key, value, count) as NODE; + } + + /** + * 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 can include properties such as + * `keyComparator`, `valueComparator`, `allowDuplicates`, etc. + * @returns a new instance of the `TreeMultiMap` class, with the provided options merged with the + * existing `iterationType` option. The returned value is casted as `TREE`. + */ + override createTree(options?: TreeMultiMapOptions): TREE { + return new TreeMultiMap([], { + iterationType: this.iterationType, + ...options + }) as TREE; + } + + /** + * The function `keyValueOrEntryToNode` takes a key, value, and count and returns a node if the input + * is valid. + * @param keyOrNodeOrEntry - The parameter `keyOrNodeOrEntry` can be of type `KeyOrNodeOrEntry`. It can accept three types of values: + * @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. + * @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 node. If not provided, it defaults to 1. + * @returns a NODE object or undefined. + */ + override keyValueOrEntryToNode( + keyOrNodeOrEntry: KeyOrNodeOrEntry, + value?: V, + count = 1 + ): NODE | undefined { + let node: NODE | undefined; + if (keyOrNodeOrEntry === undefined || keyOrNodeOrEntry === null) { + return; + } else if (this.isNode(keyOrNodeOrEntry)) { + node = keyOrNodeOrEntry; + } else if (this.isEntry(keyOrNodeOrEntry)) { + const [key, value] = keyOrNodeOrEntry; + if (key === undefined || key === null) { + return; + } else { + node = this.createNode(key, value, count); + } + } else if (!this.isNode(keyOrNodeOrEntry)) { + node = this.createNode(keyOrNodeOrEntry, value, count); + } else { + return; + } + return node; + } + + /** + * The function "isNode" checks if a given key, node, or entry is an instance of the TreeMultiMapNode + * class. + * @param keyOrNodeOrEntry - The parameter `keyOrNodeOrEntry` can be of type `KeyOrNodeOrEntry`. + * @returns a boolean value indicating whether the input parameter `keyOrNodeOrEntry` is an instance + * of the `TreeMultiMapNode` class. + */ + override isNode(keyOrNodeOrEntry: KeyOrNodeOrEntry): keyOrNodeOrEntry is NODE { + return keyOrNodeOrEntry instanceof TreeMultiMapNode; + } + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + */ + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + * The function overrides the add method in TypeScript and adds a new node to the data structure. + * @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can accept three types of values: + * @param {V} [value] - The `value` parameter represents the value associated with the key in the + * data structure. + * @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(keyOrNodeOrEntry: KeyOrNodeOrEntry, value?: V, count = 1): boolean { + const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value, count); + if (newNode === undefined) return false; + + const orgNodeCount = newNode?.count || 0; + const inserted = super.add(newNode); + if (inserted) { + this._count += orgNodeCount; + } + return true; + } + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + */ + + /** + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + * The `delete` function in a TypeScript class is used to delete nodes from a binary tree based on a + * given identifier, and it returns an array of results containing information about the deleted + * nodes. + * @param {ReturnType | null | undefined} identifier - The identifier parameter is the value used + * to identify the node to be deleted. It can be of any type that is returned by the callback + * function. It can also be null or undefined if no node needs to be deleted. + * @param {C} callback - The `callback` parameter is a function that takes a node of type `NODE` as + * input and returns a value of type `ReturnType`. It is used to determine if a node matches the + * identifier for deletion. If no callback is provided, the `_defaultOneParamCallback` function is + * used + * @param [ignoreCount=false] - A boolean value indicating whether to ignore the count of the target + * node when performing deletion. If set to true, the count of the target node will not be considered + * and the node will be deleted regardless of its count. If set to false (default), the count of the + * target node will be decremented + * @returns an array of BinaryTreeDeleteResult objects. + */ + override delete>( + identifier: ReturnType | null | undefined, + callback: C = this._defaultOneParamCallback as C, + ignoreCount = false + ): BinaryTreeDeleteResult[] { + const deleteResults: BinaryTreeDeleteResult[] = []; + if (identifier === null) return deleteResults; + + // Helper function to perform deletion + const deleteHelper = (node: NODE | undefined): void => { + // Initialize targetNode to the sentinel node + let targetNode: NODE = this._Sentinel; + let currentNode: NODE | undefined; + + // Find the node to be deleted based on the identifier + while (node !== this._Sentinel) { + // Update targetNode if the current node matches the identifier + if (node && callback(node) === identifier) { + targetNode = node; + } + + // Move to the right or left based on the comparison with the identifier + if (node && identifier && callback(node) <= identifier) { + node = node.right; + } else { + node = node?.left; + } + } + + // If the target node is not found, decrement size and return + if (targetNode === this._Sentinel) { + return; + } + + if (ignoreCount || targetNode.count <= 1) { + // Store the parent of the target node and its original color + let parentNode = targetNode; + let parentNodeOriginalColor: number = parentNode.color; + + // Handle deletion based on the number of children of the target node + if (targetNode.left === this._Sentinel) { + // Target node has no left child - deletion case 1 + currentNode = targetNode.right; + this._rbTransplant(targetNode, targetNode.right!); + } else if (targetNode.right === this._Sentinel) { + // Target node has no right child - deletion case 2 + currentNode = targetNode.left; + this._rbTransplant(targetNode, targetNode.left!); + } else { + // Target node has both left and right children - deletion case 3 + parentNode = this.getLeftMost(targetNode.right)!; + parentNodeOriginalColor = parentNode.color; + currentNode = parentNode.right; + + if (parentNode.parent === targetNode) { + // Target node's right child becomes its parent's left child + currentNode!.parent = parentNode; + } else { + // Replace parentNode with its right child and update connections + this._rbTransplant(parentNode, parentNode.right!); + parentNode.right = targetNode.right; + parentNode.right!.parent = parentNode; + } + + // Replace the target node with its in-order successor + this._rbTransplant(targetNode, parentNode); + parentNode.left = targetNode.left; + parentNode.left!.parent = parentNode; + parentNode.color = targetNode.color; + } + + // Fix the Red-Black Tree properties after deletion + if (parentNodeOriginalColor === RBTNColor.BLACK) { + this._fixDelete(currentNode!); + } + + // Decrement the size and store information about the deleted node + this._size--; + this._count -= targetNode.count; + deleteResults.push({ deleted: targetNode, needBalanced: undefined }); + } else { + targetNode.count--; + this._count--; + } + }; + + // Call the helper function with the root of the tree + deleteHelper(this.root); + + // Return the result array + return deleteResults; + } + + /** + * Time Complexity: O(1) + * Space Complexity: O(1) + */ + + /** + * 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) + */ + + /** + * 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 - The `iterationType` parameter is an optional parameter that specifies the + * type of iteration to use when building the balanced binary search tree. It can have two possible + * values: + * @returns a boolean value. + */ + override perfectlyBalance(iterationType = this.iterationType): boolean { + const sorted = this.dfs(node => node, 'in'), + n = sorted.length; + if (sorted.length < 1) return false; + + this.clear(); + + if (iterationType === IterationType.RECURSIVE) { + const buildBalanceBST = (l: number, r: number) => { + if (l > r) return; + const m = l + Math.floor((r - l) / 2); + const midNode = sorted[m]; + 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]; + 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) + */ + + /** + * 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(): TREE { + const cloned = this.createTree(); + this.bfs(node => cloned.add(node.key, node.value, node.count)); + return cloned; + } + + /** + * The function swaps the properties of two nodes in a binary search tree. + * @param srcNode - The source node that needs to be swapped with the destination node. It can be + * either a key or a node object. + * @param destNode - The `destNode` parameter is the node in the binary search tree where the + * properties will be swapped with the `srcNode`. + * @returns The method is returning the `destNode` after swapping its properties with the `srcNode`. + * If both `srcNode` and `destNode` are valid nodes, the method swaps their `key`, `value`, `count`, + * and `color` properties. If the swapping is successful, the method returns the modified `destNode`. + * If either `srcNode` or `destNode` is + */ + protected override _swapProperties( + srcNode: BSTNKeyOrNode, + destNode: BSTNKeyOrNode + ): NODE | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); + if (srcNode && destNode) { + const { key, value, count, color } = destNode; + const tempNode = this.createNode(key, value, count); + if (tempNode) { + tempNode.color = color; + + destNode.key = srcNode.key; + destNode.value = srcNode.value; + destNode.count = srcNode.count; + destNode.color = srcNode.color; + + srcNode.key = tempNode.key; + srcNode.value = tempNode.value; + srcNode.count = tempNode.count; + srcNode.color = tempNode.color; + } + + return destNode; + } + return undefined; + } + + /** + * The function replaces an old node with a new node and updates the count property of the new node. + * @param {NODE} oldNode - The `oldNode` parameter is of type `NODE` and represents the node that + * needs to be replaced in the data structure. + * @param {NODE} newNode - The `newNode` parameter is an object of type `NODE`. + * @returns The method is returning the result of calling the `_replaceNode` method from the + * superclass, after updating the `count` property of the `newNode` object. + */ + protected _replaceNode(oldNode: NODE, newNode: NODE): NODE { + newNode.count = oldNode.count + newNode.count; + return super._replaceNode(oldNode, newNode); + } +} 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 new file mode 100644 index 0000000..ac54c68 --- /dev/null +++ b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts @@ -0,0 +1,8 @@ +import { AVLTreeMultiMap, AVLTreeMultiMapNode } from '../../../data-structures'; +import type { AVLTreeOptions } from './avl-tree'; + +export type AVLTreeMultiMapNodeNested = AVLTreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +export type AVLTreeMultiMapNested> = AVLTreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +export type AVLTreeMultiMapOptions = AVLTreeOptions & {} diff --git a/src/types/data-structures/binary-tree/index.ts b/src/types/data-structures/binary-tree/index.ts index ea30893..7d8dcb1 100644 --- a/src/types/data-structures/binary-tree/index.ts +++ b/src/types/data-structures/binary-tree/index.ts @@ -2,5 +2,6 @@ export * from './binary-tree'; export * from './bst'; export * from './avl-tree'; export * from './segment-tree'; -export * from './tree-multimap'; +export * from './avl-tree-multi-map'; export * from './rb-tree'; +export * from './tree-multi-map'; diff --git a/src/types/data-structures/binary-tree/tree-multi-map.ts b/src/types/data-structures/binary-tree/tree-multi-map.ts new file mode 100644 index 0000000..3277da1 --- /dev/null +++ b/src/types/data-structures/binary-tree/tree-multi-map.ts @@ -0,0 +1,8 @@ +import { TreeMultiMap, TreeMultiMapNode } from '../../../data-structures'; +import type { RBTreeOptions } from './rb-tree'; + +export type TreeMultiMapNodeNested = TreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +export type TreeMultiMapNested> = TreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +export type TreeMultiMapOptions = RBTreeOptions & {} diff --git a/src/types/data-structures/binary-tree/tree-multimap.ts b/src/types/data-structures/binary-tree/tree-multimap.ts deleted file mode 100644 index c1bb3c0..0000000 --- a/src/types/data-structures/binary-tree/tree-multimap.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TreeMultimap, TreeMultimapNode } from '../../../data-structures'; -import type { AVLTreeOptions } from './avl-tree'; - -export type TreeMultimapNodeNested = TreeMultimapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type TreeMultimapNested> = TreeMultimap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type TreeMultimapOptions = AVLTreeOptions & {} diff --git a/test/integration/index.html b/test/integration/index.html index ae19e7f..50e80bd 100644 --- a/test/integration/index.html +++ b/test/integration/index.html @@ -232,7 +232,7 @@ RedBlackTree, SinglyLinkedList, Stack, - TreeMultimap, + TreeMultiMap, Trie } = dataStructureTyped; const orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8]; @@ -302,7 +302,7 @@ // / \ // 7 9 - const treeMulti = new TreeMultimap(entries); + const treeMulti = new TreeMultiMap(entries); treeMulti.print(); // ___4___ // / \ diff --git a/test/unit/data-structures/binary-tree/tree-multimap.test.ts b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts similarity index 86% rename from test/unit/data-structures/binary-tree/tree-multimap.test.ts rename to test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts index db3c97f..d49a28b 100644 --- a/test/unit/data-structures/binary-tree/tree-multimap.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts @@ -1,20 +1,20 @@ import { + AVLTreeMultiMap, + AVLTreeMultiMapNode, AVLTreeNode, BinaryTreeNode, BSTNode, CP, - IterationType, - TreeMultimap, - TreeMultimapNode + IterationType } from '../../../../src'; import { isDebugTest } from '../../../config'; const isDebug = isDebugTest; -describe('TreeMultimap count', () => { - let tm: TreeMultimap; +describe('AVLTreeMultiMap count', () => { + let tm: AVLTreeMultiMap; beforeEach(() => { - tm = new TreeMultimap(); + tm = new AVLTreeMultiMap(); }); it('Should added isolated node count ', () => { tm.addMany([ @@ -24,7 +24,7 @@ describe('TreeMultimap count', () => { [4, 4], [5, 5] ]); - const newNode = new TreeMultimapNode(3, 33, 10); + const newNode = new AVLTreeMultiMapNode(3, 33, 10); tm.add(newNode); expect(tm.count).toBe(15); }); @@ -40,11 +40,11 @@ describe('TreeMultimap count', () => { }); }); -describe('TreeMultimap operations test1', () => { +describe('AVLTreeMultiMap operations test1', () => { it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const treeMultimap = new TreeMultimap(); + const treeMultimap = new AVLTreeMultiMap(); - expect(treeMultimap instanceof TreeMultimap); + expect(treeMultimap instanceof AVLTreeMultiMap); treeMultimap.add([11, 11]); treeMultimap.add([3, 3]); const idAndValues: [number, number][] = [ @@ -66,7 +66,7 @@ describe('TreeMultimap operations test1', () => { [5, 5] ]; treeMultimap.addMany(idAndValues); - expect(treeMultimap.root instanceof TreeMultimapNode); + expect(treeMultimap.root instanceof AVLTreeMultiMapNode); if (treeMultimap.root) expect(treeMultimap.root.key == 11); @@ -99,17 +99,17 @@ describe('TreeMultimap operations test1', () => { node15 && treeMultimap.dfs(node => (subTreeSum += node.key), 'pre', 15); expect(subTreeSum).toBe(31); let lesserSum = 0; - treeMultimap.lesserOrGreaterTraverse((node: TreeMultimapNode) => (lesserSum += node.key), CP.lt, 10); - expect(lesserSum).toBe(21); + treeMultimap.lesserOrGreaterTraverse((node: AVLTreeMultiMapNode) => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); - expect(node15 instanceof TreeMultimapNode); - if (node15 instanceof TreeMultimapNode) { + 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 TreeMultimapNode); - if (node11 instanceof TreeMultimapNode) { + expect(node11 instanceof AVLTreeMultiMapNode); + if (node11 instanceof AVLTreeMultiMapNode) { const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11); expect(allGreaterNodesAdded); } @@ -257,14 +257,14 @@ describe('TreeMultimap operations test1', () => { expect(bfsNodes[1].key).toBe(2); expect(bfsNodes[2].key).toBe(16); - expect(treeMultimap.count).toBe(4); + expect(treeMultimap.count).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 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 }], @@ -282,23 +282,23 @@ describe('TreeMultimap operations test1', () => { [5, { key: 5, keyA: 5 }] ]; - objTreeMultimap.addMany(values); + objAVLTreeMultiMap.addMany(values); - expect(objTreeMultimap.root).toBeInstanceOf(TreeMultimapNode); + expect(objAVLTreeMultiMap.root).toBeInstanceOf(AVLTreeMultiMapNode); - if (objTreeMultimap.root) expect(objTreeMultimap.root.key).toBe(6); + if (objAVLTreeMultiMap.root) expect(objAVLTreeMultiMap.root.key).toBe(6); - expect(objTreeMultimap.count).toBe(16); + expect(objAVLTreeMultiMap.count).toBe(16); - expect(objTreeMultimap.has(6)).toBe(true); + expect(objAVLTreeMultiMap.has(6)).toBe(true); }); }); -describe('TreeMultimap operations test recursively1', () => { +describe('AVLTreeMultiMap operations test recursively1', () => { it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const treeMultimap = new TreeMultimap([], { iterationType: IterationType.RECURSIVE }); + const treeMultimap = new AVLTreeMultiMap([], { iterationType: IterationType.RECURSIVE }); - expect(treeMultimap instanceof TreeMultimap); + expect(treeMultimap instanceof AVLTreeMultiMap); treeMultimap.add([11, 11]); treeMultimap.add([3, 3]); const idAndValues: [number, number][] = [ @@ -320,7 +320,7 @@ describe('TreeMultimap operations test recursively1', () => { [5, 5] ]; treeMultimap.addMany(idAndValues); - expect(treeMultimap.root).toBeInstanceOf(TreeMultimapNode); + expect(treeMultimap.root).toBeInstanceOf(AVLTreeMultiMapNode); if (treeMultimap.root) expect(treeMultimap.root.key).toBe(6); @@ -353,17 +353,17 @@ describe('TreeMultimap operations test recursively1', () => { node15 && treeMultimap.dfs(node => (subTreeSum += node.key), 'pre', 15); expect(subTreeSum).toBe(31); let lesserSum = 0; - treeMultimap.lesserOrGreaterTraverse((node: TreeMultimapNode) => (lesserSum += node.key), CP.lt, 10); - expect(lesserSum).toBe(21); + treeMultimap.lesserOrGreaterTraverse((node: AVLTreeMultiMapNode) => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); - expect(node15 instanceof TreeMultimapNode); - if (node15 instanceof TreeMultimapNode) { + 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 TreeMultimapNode); - if (node11 instanceof TreeMultimapNode) { + expect(node11 instanceof AVLTreeMultiMapNode); + if (node11 instanceof AVLTreeMultiMapNode) { const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11); expect(allGreaterNodesAdded); } @@ -511,14 +511,14 @@ describe('TreeMultimap operations test recursively1', () => { expect(bfsNodes[1].key).toBe(2); expect(bfsNodes[2].key).toBe(16); - expect(treeMultimap.count).toBe(4); + expect(treeMultimap.count).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 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 }], @@ -536,27 +536,27 @@ describe('TreeMultimap operations test recursively1', () => { [5, { key: 5, keyA: 5 }] ]; - objTreeMultimap.addMany(values); + objAVLTreeMultiMap.addMany(values); - expect(objTreeMultimap.root).toBeInstanceOf(TreeMultimapNode); + expect(objAVLTreeMultiMap.root).toBeInstanceOf(AVLTreeMultiMapNode); - if (objTreeMultimap.root) expect(objTreeMultimap.root.key).toBe(6); + if (objAVLTreeMultiMap.root) expect(objAVLTreeMultiMap.root.key).toBe(6); - expect(objTreeMultimap.count).toBe(16); + expect(objAVLTreeMultiMap.count).toBe(16); - expect(objTreeMultimap.has(6)).toBe(true); + expect(objAVLTreeMultiMap.has(6)).toBe(true); }); }); -describe('TreeMultimap Performance test', function () { - const treeMS = new TreeMultimap(); +describe('AVLTreeMultiMap Performance test', function () { + const treeMS = new AVLTreeMultiMap(); const inputSize = 100000; // Adjust input sizes as needed beforeEach(() => { treeMS.clear(); }); - it(`Observe the time consumption of TreeMultimap.dfs be good`, function () { + it(`Observe the time consumption of AVLTreeMultiMap.dfs be good`, function () { const startDFS = performance.now(); const dfs = treeMS.dfs(node => node); isDebug && console.log('---bfs', performance.now() - startDFS, dfs.length); @@ -574,7 +574,7 @@ describe('TreeMultimap Performance test', function () { }); it('should the clone method', () => { - function checkTreeStructure(treeMultimap: TreeMultimap) { + function checkTreeStructure(treeMultimap: AVLTreeMultiMap) { expect(treeMultimap.size).toBe(4); expect(treeMultimap.root?.key).toBe('2'); expect(treeMultimap.root?.left?.key).toBe('1'); @@ -585,7 +585,7 @@ describe('TreeMultimap Performance test', function () { expect(treeMultimap.root?.right?.right?.key).toBe('5'); } - const treeMultimap = new TreeMultimap(); + const treeMultimap = new AVLTreeMultiMap(); treeMultimap.addMany([ ['2', 2], ['4', 4], @@ -611,10 +611,10 @@ describe('TreeMultimap Performance test', function () { }); }); -describe('TreeMultimap iterative methods test', () => { - let treeMM: TreeMultimap; +describe('AVLTreeMultiMap iterative methods test', () => { + let treeMM: AVLTreeMultiMap; beforeEach(() => { - treeMM = new TreeMultimap(); + treeMM = new AVLTreeMultiMap(); treeMM.add(1, 'a', 10); treeMM.add([2, 'b'], undefined, 10); treeMM.add([3, 'c'], undefined, 1); diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index cfbc458..e71550f 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -1,4 +1,4 @@ -import { AVLTree, BST, BSTVariant, IterationType, RedBlackTree, TreeMultimap } from '../../../../src'; +import { AVLTree, BST, BSTVariant, IterationType, RedBlackTree, TreeMultiMap } from '../../../../src'; describe('Overall BinaryTree Test', () => { it('should perform various operations on BinaryTree', () => { @@ -146,10 +146,9 @@ describe('Overall BinaryTree Test', () => { expect(clonedAVL.bfs()).toEqual([3, 5, 1, 9, 4, 2, 6]); }); - it('Should clone a TreeMultimap works fine', () => { - const tmm = new TreeMultimap([3, 6, 7, 1, 9], { + it('Should clone a TreeMultiMap works fine', () => { + const tmm = new TreeMultiMap([3, 6, 7, 1, 9], { iterationType: IterationType.RECURSIVE, - variant: BSTVariant.INVERSE, extractor: key => key }); expect(tmm.size).toBe(5); @@ -159,40 +158,40 @@ describe('Overall BinaryTree Test', () => { tmm.add(5); tmm.add(4); expect(tmm.count).toBe(10); - expect(tmm.root?.key).toBe(3); - expect(tmm.root?.left?.key).toBe(7); - expect(tmm.root?.left?.left?.key).toBe(9); - expect(tmm.root?.right?.key).toBe(1); - expect(tmm.root?.right?.left?.key).toBe(2); - expect(tmm.getNodeByKey(7)?.left?.key).toBe(9); - expect(tmm.getHeight()).toBe(3); + expect(tmm.root?.key).toBe(6); + expect(tmm.root?.left?.key).toBe(1); + expect(tmm.root?.left?.left?.key).toBe(NaN); + expect(tmm.root?.right?.key).toBe(7); + expect(tmm.root?.right?.left?.key).toBe(NaN); + expect(tmm.getNodeByKey(7)?.left?.key).toBe(NaN); + expect(tmm.getHeight()).toBe(5); expect(tmm.has(9)).toBe(true); expect(tmm.has(7)).toBe(true); 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(5); - expect(tmm.root?.right?.key).toBe(1); - expect(tmm.root?.right?.left?.key).toBe(2); - expect(tmm.getNodeByKey(6)?.left?.key).toBe(undefined); - expect(tmm.getHeight()).toBe(3); + expect(tmm.root?.key).toBe(6); + expect(tmm.root?.left?.key).toBe(1); + expect(tmm.root?.right?.key).toBe(9); + expect(tmm.root?.right?.left?.key).toBe(NaN); + expect(tmm.getNodeByKey(6)?.left?.key).toBe(1); + expect(tmm.getHeight()).toBe(5); expect(tmm.has(9)).toBe(true); expect(tmm.has(7)).toBe(false); - expect(tmm.bfs()).toEqual([3, 5, 1, 9, 4, 2, 6]); + 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(5); - expect(clonedTMM.root?.right?.key).toBe(1); - expect(clonedTMM.root?.right?.left?.key).toBe(2); - expect(clonedTMM.getNodeByKey(6)?.left?.key).toBe(undefined); - expect(clonedTMM.getHeight()).toBe(3); + expect(clonedTMM.root?.key).toBe(6); + expect(clonedTMM.root?.left?.key).toBe(1); + expect(clonedTMM.root?.right?.key).toBe(9); + expect(clonedTMM.root?.right?.left?.key).toBe(NaN); + expect(clonedTMM.getNodeByKey(6)?.left?.key).toBe(1); + expect(clonedTMM.getHeight()).toBe(5); expect(clonedTMM.has(9)).toBe(true); expect(clonedTMM.has(7)).toBe(false); - expect(clonedTMM.bfs()).toEqual([3, 5, 1, 9, 4, 2, 6]); + expect(clonedTMM.bfs()).toEqual([6, 1, 9, 3, 2, 5, 4]); }); it('Should clone a RedBlackTree works fine', () => { 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 new file mode 100644 index 0000000..9ea3a19 --- /dev/null +++ b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts @@ -0,0 +1,706 @@ +import { + BinaryTreeNode, + BSTNode, + CP, + IterationType, + RedBlackTreeNode, + TreeMultiMap, + TreeMultiMapNode +} from '../../../../src'; +import { isDebugTest } from '../../../config'; + +const isDebug = isDebugTest; +// const isDebug = true; + +describe('TreeMultiMap count', () => { + let tm: TreeMultiMap; + beforeEach(() => { + tm = new TreeMultiMap(); + }); + it('Should added isolated node count ', () => { + tm.addMany([ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5] + ]); + const newNode = new TreeMultiMapNode(3, 33, 10); + tm.add(newNode); + expect(tm.count).toBe(15); + }); + + it('Should count', () => { + tm.addMany([ + [1, 1], + [2, 2], + [3, 3] + ]); + tm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 1); + expect(tm.count).toBe(7); + }); +}); + +describe('TreeMultiMap operations test1', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { + const treeMultimap = new TreeMultiMap(); + + expect(treeMultimap instanceof TreeMultiMap); + 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 TreeMultiMapNode); + + 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(2); + expect(treeMultimap.getDepth(6)).toBe(4); + const nodeId10 = treeMultimap.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeMultimap.getNode(9, node => node.value); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = treeMultimap.getNodes(1, node => node.count); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = treeMultimap.getNodes(2, node => node.count); + expect(nodesByCount2.length).toBe(2); + const leftMost = treeMultimap.getLeftMost(); + expect(leftMost?.key).toBe(1); + + const node15 = treeMultimap.getNode(15); + const minNodeBySpecificNode = node15 && treeMultimap.getLeftMost(node15); + expect(minNodeBySpecificNode?.key).toBe(15); + + let subTreeSum = 0; + node15 && treeMultimap.dfs(node => (subTreeSum += node.key), 'pre', 15); + expect(subTreeSum).toBe(31); + let lesserSum = 0; + treeMultimap.lesserOrGreaterTraverse((node: TreeMultiMapNode) => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); + + expect(node15 instanceof TreeMultiMapNode); + if (node15 instanceof TreeMultiMapNode) { + const subTreeAdd = treeMultimap.dfs(node => (node.count += 1), 'pre', 15); + expect(subTreeAdd); + } + const node11 = treeMultimap.getNode(11); + expect(node11 instanceof TreeMultiMapNode); + if (node11 instanceof TreeMultiMapNode) { + const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11); + expect(allGreaterNodesAdded); + } + + 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); + + treeMultimap.perfectlyBalance(); + + expect(treeMultimap.isPerfectlyBalanced()).toBe(true); + expect(treeMultimap.isAVLBalanced()).toBe(true); + + const bfsNodesAfterBalanced = treeMultimap.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = treeMultimap.delete(11, undefined, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(treeMultimap.isAVLBalanced()).toBe(true); + + expect(treeMultimap.getHeight(15)).toBe(2); + + const removed1 = treeMultimap.delete(1, undefined, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(treeMultimap.isAVLBalanced()).toBe(true); + + expect(treeMultimap.getHeight()).toBe(5); + + const removed4 = treeMultimap.delete(4, undefined, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(treeMultimap.getHeight()).toBe(5); + + const removed10 = treeMultimap.delete(10, undefined, 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(false); + + expect(treeMultimap.getHeight()).toBe(5); + + const removed15 = treeMultimap.delete(15, undefined, 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(4); + + const removed5 = treeMultimap.delete(5, undefined, 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(4); + + const removed13 = treeMultimap.delete(13, undefined, 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(4); + + const removed3 = treeMultimap.delete(3, undefined, 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(4); + + const removed8 = treeMultimap.delete(8, undefined, 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(false); + expect(treeMultimap.getHeight()).toBe(4); + + const removed6 = treeMultimap.delete(6, undefined, 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, undefined, true).length).toBe(0); + expect(treeMultimap.isAVLBalanced()).toBe(false); + + expect(treeMultimap.getHeight()).toBe(4); + + const removed7 = treeMultimap.delete(7, undefined, 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(false); + expect(treeMultimap.getHeight()).toBe(4); + + const removed9 = treeMultimap.delete(9, undefined, 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(3); + + const removed14 = treeMultimap.delete(14, undefined, 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(2); + + 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 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(11); + + expect(objTreeMultiMap.count).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 treeMultimap = new TreeMultiMap([], { iterationType: IterationType.RECURSIVE }); + + expect(treeMultimap instanceof TreeMultiMap); + 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(TreeMultiMapNode); + + if (treeMultimap.root) expect(treeMultimap.root.key).toBe(11); + + expect(treeMultimap.size).toBe(16); + expect(treeMultimap.count).toBe(18); + + expect(treeMultimap.has(6)); + + expect(treeMultimap.getHeight(6)).toBe(2); + expect(treeMultimap.getDepth(6)).toBe(4); + const nodeId10 = treeMultimap.getNode(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeMultimap.getNode(9, node => node.value); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = treeMultimap.getNodes(1, node => node.count); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = treeMultimap.getNodes(2, node => node.count); + expect(nodesByCount2.length).toBe(2); + const leftMost = treeMultimap.getLeftMost(); + expect(leftMost?.key).toBe(1); + + const node15 = treeMultimap.getNode(15); + const minNodeBySpecificNode = node15 && treeMultimap.getLeftMost(node15); + expect(minNodeBySpecificNode?.key).toBe(15); + + let subTreeSum = 0; + node15 && treeMultimap.dfs(node => (subTreeSum += node.key), 'pre', 15); + expect(subTreeSum).toBe(31); + let lesserSum = 0; + expect(treeMultimap.has(9)).toBe(true); + treeMultimap.lesserOrGreaterTraverse( + node => { + lesserSum += node.key; + return node.key; + }, + CP.lt, + 10 + ); + expect(lesserSum).toBe(45); + + expect(node15 instanceof TreeMultiMapNode); + if (node15 instanceof TreeMultiMapNode) { + const subTreeAdd = treeMultimap.dfs(node => (node.count += 1), 'pre', 15); + expect(subTreeAdd); + } + const node11 = treeMultimap.getNode(11); + expect(node11 instanceof TreeMultiMapNode); + if (node11 instanceof TreeMultiMapNode) { + const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11); + expect(allGreaterNodesAdded); + } + + 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); + + treeMultimap.perfectlyBalance(); + + expect(treeMultimap.isPerfectlyBalanced()).toBe(true); + expect(treeMultimap.isAVLBalanced()).toBe(true); + + const bfsNodesAfterBalanced = treeMultimap.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = treeMultimap.delete(11, undefined, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(treeMultimap.isAVLBalanced()).toBe(true); + + expect(treeMultimap.getHeight(15)).toBe(2); + + const removed1 = treeMultimap.delete(1, undefined, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(treeMultimap.isAVLBalanced()).toBe(true); + + expect(treeMultimap.getHeight()).toBe(5); + + const removed4 = treeMultimap.delete(4, undefined, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(treeMultimap.isAVLBalanced()).toBe(true); + expect(treeMultimap.getHeight()).toBe(5); + + const removed10 = treeMultimap.delete(10, undefined, 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(false); + + expect(treeMultimap.getHeight()).toBe(5); + + const removed15 = treeMultimap.delete(15, undefined, 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(4); + + const removed5 = treeMultimap.delete(5, undefined, 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(4); + + const removed13 = treeMultimap.delete(13, undefined, 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(4); + + const removed3 = treeMultimap.delete(3, undefined, 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(4); + + const removed8 = treeMultimap.delete(8, undefined, 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(false); + expect(treeMultimap.getHeight()).toBe(4); + + const removed6 = treeMultimap.delete(6, undefined, 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, undefined, true).length).toBe(0); + expect(treeMultimap.isAVLBalanced()).toBe(false); + + expect(treeMultimap.getHeight()).toBe(4); + + const removed7 = treeMultimap.delete(7, undefined, 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(false); + expect(treeMultimap.getHeight()).toBe(4); + + const removed9 = treeMultimap.delete(9, undefined, 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(3); + + const removed14 = treeMultimap.delete(14, undefined, 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(2); + + 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 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(11); + + expect(objTreeMultiMap.count).toBe(16); + + expect(objTreeMultiMap.has(6)).toBe(true); + }); +}); + +describe('TreeMultiMap Performance test', function () { + const treeMS = new TreeMultiMap(); + const inputSize = 1000; // Adjust input sizes as needed + + beforeEach(() => { + treeMS.clear(); + }); + + it(`Observe the time consumption of TreeMultiMap.dfs be good`, function () { + const startDFS = performance.now(); + const dfs = treeMS.dfs(node => node); + 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++) { + treeMS.add(i); + } + + isDebug && console.log('---add', performance.now() - start); + const startL = performance.now(); + treeMS.lesserOrGreaterTraverse(node => (node.count += 1), CP.lt, inputSize / 2); + isDebug && console.log('---lesserOrGreaterTraverse', performance.now() - startL); + }); + + it('should the clone method', () => { + function checkTreeStructure(treeMultimap: TreeMultiMap) { + expect(treeMultimap.size).toBe(4); + expect(treeMultimap.root?.key).toBe('4'); + expect(treeMultimap.root?.left?.key).toBe('1'); + expect(treeMultimap.root?.left?.left?.key).toBe(NaN); + expect(treeMultimap.root?.left?.right?.key).toBe('2'); + expect(treeMultimap.root?.right?.key).toBe('5'); + expect(treeMultimap.root?.right?.left?.key).toBe(NaN); + expect(treeMultimap.root?.right?.right?.key).toBe(NaN); + } + + const treeMultimap = new TreeMultiMap(); + treeMultimap.addMany([ + ['2', 2], + ['4', 4], + ['5', 5], + ['3', 3], + ['1', 1] + ]); + expect(treeMultimap.size).toBe(5); + expect(treeMultimap.root?.key).toBe('3'); + expect(treeMultimap.root?.left?.key).toBe('1'); + expect(treeMultimap.root?.left?.left?.key).toBe(NaN); + expect(treeMultimap.root?.left?.right?.key).toBe('2'); + expect(treeMultimap.root?.right?.key).toBe('4'); + expect(treeMultimap.root?.right?.left?.key).toBe(NaN); + expect(treeMultimap.root?.right?.right?.key).toBe('5'); + treeMultimap.delete('3'); + checkTreeStructure(treeMultimap); + const cloned = treeMultimap.clone(); + checkTreeStructure(cloned); + cloned.delete('1'); + expect(treeMultimap.size).toBe(4); + expect(cloned.size).toBe(3); + }); +}); + +describe('TreeMultiMap iterative methods test', () => { + let treeMM: TreeMultiMap; + beforeEach(() => { + treeMM = new TreeMultiMap(); + treeMM.add(1, 'a', 10); + treeMM.add([2, 'b'], undefined, 10); + treeMM.add([3, 'c'], undefined, 1); + }); + + test('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); + }); + + test('forEach should iterate over all elements', () => { + const mockCallback = jest.fn(); + treeMM.forEach((value, key) => { + mockCallback(value, key); + }); + + expect(mockCallback.mock.calls.length).toBe(3); + expect(mockCallback.mock.calls[0]).toEqual(['a', 1]); + expect(mockCallback.mock.calls[1]).toEqual(['b', 2]); + expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); + }); + + test('filter should return a new tree with filtered elements', () => { + const filteredTree = treeMM.filter((value, key) => key > 1); + expect(filteredTree.size).toBe(2); + expect([...filteredTree]).toEqual([ + [2, 'b'], + [3, 'c'] + ]); + }); + + test('map should return a new tree with modified elements', () => { + const mappedTree = treeMM.map((value, key) => (key * 2).toString()); + expect(mappedTree.size).toBe(3); + expect([...mappedTree]).toEqual([ + [1, '2'], + [2, '4'], + [3, '6'] + ]); + }); + + test('reduce should accumulate values', () => { + const sum = treeMM.reduce((acc, value, key) => acc + key, 0); + expect(sum).toBe(6); + }); + + test('[Symbol.iterator] should provide an iterator', () => { + const entries = []; + for (const entry of treeMM) { + entries.push(entry); + } + + expect(entries.length).toBe(3); + expect(entries).toEqual([ + [1, 'a'], + [2, 'b'], + [3, 'c'] + ]); + }); + + test('should clone work well', () => { + expect(treeMM.count).toBe(21); + const cloned = treeMM.clone(); + expect(cloned.root?.left?.key).toBe(NaN); + expect(cloned.root?.right?.value).toBe('b'); + }); + + test('should keys', () => { + const keys = treeMM.keys(); + expect([...keys]).toEqual([1, 2, 3]); + }); + + test('should values', () => { + const values = treeMM.values(); + expect([...values]).toEqual(['a', 'b', 'c']); + }); +}); diff --git a/test/unit/data-structures/graph/undirected-graph.test.ts b/test/unit/data-structures/graph/undirected-graph.test.ts index 0db482c..10660f8 100644 --- a/test/unit/data-structures/graph/undirected-graph.test.ts +++ b/test/unit/data-structures/graph/undirected-graph.test.ts @@ -526,10 +526,10 @@ describe('UndirectedGraph tarjan', () => { }); // test('should cuttable graph tarjan CCs return correct result', () => { - // const graph = createExampleGraph3(); - // const ccs = graph.tarjan().CCs; - // expect(ccs.size).toBe(3); - // expect(getAsVerticesArrays(ccs)).toEqual([["D", "C", "B"], ["G", "F", "E"], ["A"]]); + // const graph = createExampleGraph3(); + // const ccs = graph.tarjan().CCs; + // expect(ccs.size).toBe(3); + // expect(getAsVerticesArrays(ccs)).toEqual([["D", "C", "B"], ["G", "F", "E"], ["A"]]); // }); function createExampleGraph4() { diff --git a/test/unit/unrestricted-interconversion.test.ts b/test/unit/unrestricted-interconversion.test.ts index af31ea3..a6d36cb 100644 --- a/test/unit/unrestricted-interconversion.test.ts +++ b/test/unit/unrestricted-interconversion.test.ts @@ -14,7 +14,7 @@ import { RedBlackTree, SinglyLinkedList, Stack, - TreeMultimap, + TreeMultiMap, Trie } from '../../src'; import { isDebugTest } from '../config'; @@ -167,8 +167,8 @@ describe('conversions', () => { ]); }); - it('Entry Array to TreeMultimap', () => { - const treeMulti = new TreeMultimap(entries); + it('Entry Array to TreeMultiMap', () => { + const treeMulti = new TreeMultiMap(entries); expect(treeMulti.size).toBe(9); isDebug && treeMulti.print(); expect([...treeMulti]).toEqual([