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([