mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
Refactor: Due to critical issues in the previous implementation of the Red-Black Tree, it has been deprecated and replaced with a new implementation of both the Red-Black Tree and TreeMultiMap.
This commit is contained in:
parent
f8832a04c0
commit
1f8fc1487c
|
@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
|
|||
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||
- [`auto-changelog`](https://github.com/CookPete/auto-changelog)
|
||||
|
||||
## [v1.50.4](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
|
||||
## [v1.50.5](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
|
||||
|
||||
### Changes
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.50.4",
|
||||
"version": "1.50.5",
|
||||
"description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
|
|
|
@ -934,7 +934,7 @@ export class BinaryTree<
|
|||
|
||||
if (iterationType === IterationType.RECURSIVE) {
|
||||
const dfs = (cur: NODE | null | undefined, min: number, max: number): boolean => {
|
||||
if (!cur) return true;
|
||||
if (!this.isRealNode(cur)) return true;
|
||||
const numKey = this.extractor(cur.key);
|
||||
if (numKey <= min || numKey >= max) return false;
|
||||
return dfs(cur.left, min, numKey) && dfs(cur.right, numKey, max);
|
||||
|
@ -949,14 +949,14 @@ export class BinaryTree<
|
|||
let prev = checkMax ? Number.MAX_SAFE_INTEGER : Number.MIN_SAFE_INTEGER;
|
||||
// @ts-ignore
|
||||
let curr: NODE | null | undefined = beginRoot;
|
||||
while (curr || stack.length > 0) {
|
||||
while (curr) {
|
||||
while (this.isRealNode(curr) || stack.length > 0) {
|
||||
while (this.isRealNode(curr)) {
|
||||
stack.push(curr);
|
||||
curr = curr.left;
|
||||
}
|
||||
curr = stack.pop()!;
|
||||
const numKey = this.extractor(curr.key);
|
||||
if (!curr || (!checkMax && prev >= numKey) || (checkMax && prev <= numKey)) return false;
|
||||
if (!this.isRealNode(curr) || (!checkMax && prev >= numKey) || (checkMax && prev <= numKey)) return false;
|
||||
prev = numKey;
|
||||
curr = curr.right;
|
||||
}
|
||||
|
@ -1025,7 +1025,7 @@ export class BinaryTree<
|
|||
|
||||
if (iterationType === IterationType.RECURSIVE) {
|
||||
const _getMaxHeight = (cur: NODE | null | undefined): number => {
|
||||
if (!cur) return -1;
|
||||
if (!this.isRealNode(cur)) return -1;
|
||||
const leftHeight = _getMaxHeight(cur.left);
|
||||
const rightHeight = _getMaxHeight(cur.right);
|
||||
return Math.max(leftHeight, rightHeight) + 1;
|
||||
|
@ -1039,8 +1039,8 @@ export class BinaryTree<
|
|||
while (stack.length > 0) {
|
||||
const { node, depth } = stack.pop()!;
|
||||
|
||||
if (node.left) stack.push({ node: node.left, depth: depth + 1 });
|
||||
if (node.right) stack.push({ node: node.right, depth: depth + 1 });
|
||||
if (this.isRealNode(node.left)) stack.push({ node: node.left, depth: depth + 1 });
|
||||
if (this.isRealNode(node.right)) stack.push({ node: node.right, depth: depth + 1 });
|
||||
|
||||
maxHeight = Math.max(maxHeight, depth);
|
||||
}
|
||||
|
@ -1354,34 +1354,34 @@ export class BinaryTree<
|
|||
switch (pattern) {
|
||||
case 'in':
|
||||
if (includeNull) {
|
||||
if (node && this.isNodeOrNull(node.left)) _traverse(node.left);
|
||||
if (this.isRealNode(node) && this.isNodeOrNull(node.left)) _traverse(node.left);
|
||||
this.isNodeOrNull(node) && ans.push(callback(node));
|
||||
if (node && this.isNodeOrNull(node.right)) _traverse(node.right);
|
||||
if (this.isRealNode(node) && this.isNodeOrNull(node.right)) _traverse(node.right);
|
||||
} else {
|
||||
if (node && node.left) _traverse(node.left);
|
||||
if (this.isRealNode(node) && this.isRealNode(node.left)) _traverse(node.left);
|
||||
this.isRealNode(node) && ans.push(callback(node));
|
||||
if (node && node.right) _traverse(node.right);
|
||||
if (this.isRealNode(node) && this.isRealNode(node.right)) _traverse(node.right);
|
||||
}
|
||||
break;
|
||||
case 'pre':
|
||||
if (includeNull) {
|
||||
this.isNodeOrNull(node) && ans.push(callback(node));
|
||||
if (node && this.isNodeOrNull(node.left)) _traverse(node.left);
|
||||
if (node && this.isNodeOrNull(node.right)) _traverse(node.right);
|
||||
if (this.isRealNode(node) && this.isNodeOrNull(node.left)) _traverse(node.left);
|
||||
if (this.isRealNode(node) && this.isNodeOrNull(node.right)) _traverse(node.right);
|
||||
} else {
|
||||
this.isRealNode(node) && ans.push(callback(node));
|
||||
if (node && node.left) _traverse(node.left);
|
||||
if (node && node.right) _traverse(node.right);
|
||||
if (this.isRealNode(node) && this.isRealNode(node.left)) _traverse(node.left);
|
||||
if (this.isRealNode(node) && this.isRealNode(node.right)) _traverse(node.right);
|
||||
}
|
||||
break;
|
||||
case 'post':
|
||||
if (includeNull) {
|
||||
if (node && this.isNodeOrNull(node.left)) _traverse(node.left);
|
||||
if (node && this.isNodeOrNull(node.right)) _traverse(node.right);
|
||||
if (this.isRealNode(node) && this.isNodeOrNull(node.left)) _traverse(node.left);
|
||||
if (this.isRealNode(node) && this.isNodeOrNull(node.right)) _traverse(node.right);
|
||||
this.isNodeOrNull(node) && ans.push(callback(node));
|
||||
} else {
|
||||
if (node && node.left) _traverse(node.left);
|
||||
if (node && node.right) _traverse(node.right);
|
||||
if (this.isRealNode(node) && this.isRealNode(node.left)) _traverse(node.left);
|
||||
if (this.isRealNode(node) && this.isRealNode(node.right)) _traverse(node.right);
|
||||
this.isRealNode(node) && ans.push(callback(node));
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -88,10 +88,13 @@ export class TreeMultiMap<
|
|||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
get count(): number {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
getMutableCount(): number {
|
||||
let sum = 0;
|
||||
this.dfs(node => (sum += node.count));
|
||||
return sum;
|
||||
// return this._count;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,14 +195,15 @@ export class TreeMultiMap<
|
|||
*/
|
||||
override add(keyOrNodeOrEntry: KeyOrNodeOrEntry<K, V, NODE>, value?: V, count = 1): boolean {
|
||||
const newNode = this.keyValueOrEntryToNode(keyOrNodeOrEntry, value, count);
|
||||
if (newNode === undefined) return false;
|
||||
const orgCount = newNode?.count || 0;
|
||||
const isSuccessAdded = super.add(newNode);
|
||||
|
||||
const orgNodeCount = newNode?.count || 0;
|
||||
const inserted = super.add(newNode);
|
||||
if (inserted) {
|
||||
this._count += orgNodeCount;
|
||||
if (isSuccessAdded) {
|
||||
this._count += orgCount;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,92 +236,91 @@ export class TreeMultiMap<
|
|||
callback: C = this._defaultOneParamCallback as C,
|
||||
ignoreCount = false
|
||||
): BinaryTreeDeleteResult<NODE>[] {
|
||||
const deleteResults: BinaryTreeDeleteResult<NODE>[] = [];
|
||||
if (identifier === null) return deleteResults;
|
||||
if (identifier === null) return [];
|
||||
const results: BinaryTreeDeleteResult<NODE>[] = [];
|
||||
|
||||
// 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;
|
||||
const nodeToDelete = this.isRealNode(identifier) ? identifier : this.getNode(identifier, callback);
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (!nodeToDelete) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
let originalColor = nodeToDelete.color;
|
||||
let replacementNode: NODE | undefined;
|
||||
|
||||
// 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 });
|
||||
if (!this.isRealNode(nodeToDelete.left)) {
|
||||
replacementNode = nodeToDelete.right;
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, nodeToDelete.right);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
targetNode.count--;
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
};
|
||||
} else if (!this.isRealNode(nodeToDelete.right)) {
|
||||
replacementNode = nodeToDelete.left;
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, nodeToDelete.left);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
} else {
|
||||
const successor = this.getLeftMost(nodeToDelete.right);
|
||||
if (successor) {
|
||||
originalColor = successor.color;
|
||||
replacementNode = successor.right;
|
||||
|
||||
// Call the helper function with the root of the tree
|
||||
deleteHelper(this.root);
|
||||
if (successor.parent === nodeToDelete) {
|
||||
if (this.isRealNode(replacementNode)) {
|
||||
replacementNode.parent = successor;
|
||||
}
|
||||
} else {
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(successor, successor.right);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
successor.right = nodeToDelete.right;
|
||||
if (this.isRealNode(successor.right)) {
|
||||
successor.right.parent = successor;
|
||||
}
|
||||
}
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, successor);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
successor.left = nodeToDelete.left;
|
||||
if (this.isRealNode(successor.left)) {
|
||||
successor.left.parent = successor;
|
||||
}
|
||||
successor.color = nodeToDelete.color;
|
||||
}
|
||||
}
|
||||
this._size--;
|
||||
|
||||
// Return the result array
|
||||
return deleteResults;
|
||||
// If the original color was black, fix the tree
|
||||
if (originalColor === RBTNColor.BLACK) {
|
||||
this._deleteFixup(replacementNode);
|
||||
}
|
||||
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,33 +7,43 @@ import { isCompetitor } from '../../../config';
|
|||
const suite = new Benchmark.Suite();
|
||||
const rbTree = new RedBlackTree();
|
||||
const { HUNDRED_THOUSAND } = magnitude;
|
||||
const arr = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND, true);
|
||||
const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true);
|
||||
const cOrderedMap = new OrderedMap<number, number>();
|
||||
|
||||
suite.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]);
|
||||
});
|
||||
suite
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add orderly`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} delete orderly`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.delete(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} delete randomly`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.delete(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add orderly`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} delete randomly`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.delete(randomArray[i]);
|
||||
});
|
||||
|
||||
if (isCompetitor) {
|
||||
suite.add(`CPT ${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
||||
for (let i = 0; i < arr.length; i++) cOrderedMap.setElement(arr[i], arr[i]);
|
||||
for (let i = 0; i < randomArray.length; i++) cOrderedMap.setElement(randomArray[i], randomArray[i]);
|
||||
});
|
||||
}
|
||||
|
||||
suite
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & delete randomly`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]);
|
||||
for (let i = 0; i < arr.length; i++) rbTree.delete(arr[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
||||
for (let i = 0; i < arr.length; i++) rbTree.getNode(arr[i]);
|
||||
});
|
||||
suite.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode randomly`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.getNode(randomArray[i]);
|
||||
});
|
||||
|
||||
suite.add(`${HUNDRED_THOUSAND.toLocaleString()} add & iterator`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]);
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]);
|
||||
const entries = [...rbTree];
|
||||
return entries.length === HUNDRED_THOUSAND;
|
||||
});
|
||||
|
|
|
@ -158,40 +158,41 @@ describe('Overall BinaryTree Test', () => {
|
|||
tmm.add(5);
|
||||
tmm.add(4);
|
||||
expect(tmm.count).toBe(10);
|
||||
expect(tmm.root?.key).toBe(6);
|
||||
expect(tmm.root?.key).toBe(3);
|
||||
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.root?.right?.left?.key).toBe(5);
|
||||
expect(tmm.getNodeByKey(7)?.left?.key).toBe(5);
|
||||
expect(tmm.getHeight()).toBe(3);
|
||||
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(6);
|
||||
expect(tmm.root?.key).toBe(3);
|
||||
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.root?.right?.left?.key).toBe(5);
|
||||
expect(tmm.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(tmm.getHeight()).toBe(3);
|
||||
expect(tmm.has(9)).toBe(true);
|
||||
expect(tmm.has(7)).toBe(false);
|
||||
expect(tmm.bfs()).toEqual([6, 1, 9, 3, 2, 5, 4]);
|
||||
expect(tmm.bfs()).toEqual([3, 1, 9, 2, 5, 4, 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(6);
|
||||
expect(clonedTMM.root?.key).toBe(3);
|
||||
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.root?.right?.key).toBe(5);
|
||||
expect(clonedTMM.root?.right?.left?.key).toBe(4);
|
||||
expect(clonedTMM.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(clonedTMM.getHeight()).toBe(3);
|
||||
expect(clonedTMM.has(9)).toBe(true);
|
||||
expect(clonedTMM.has(7)).toBe(false);
|
||||
expect(clonedTMM.bfs()).toEqual([6, 1, 9, 3, 2, 5, 4]);
|
||||
expect(clonedTMM.bfs()).toEqual([3, 1, 5, 2, 4, 9, 6]);
|
||||
});
|
||||
|
||||
it('Should clone a RedBlackTree works fine', () => {
|
||||
|
@ -211,7 +212,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(rbTree.root?.right?.key).toBe(7);
|
||||
expect(rbTree.root?.right?.left?.key).toBe(5);
|
||||
expect(rbTree.getNodeByKey(7)?.left?.key).toBe(5);
|
||||
expect(rbTree.getHeight()).toBe(4);
|
||||
expect(rbTree.getHeight()).toBe(3);
|
||||
expect(rbTree.has(9)).toBe(true);
|
||||
expect(rbTree.has(7)).toBe(true);
|
||||
expect(rbTree.delete(7)?.[0]?.deleted?.key).toBe(7);
|
||||
|
@ -219,13 +220,14 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(rbTree.size).toBe(7);
|
||||
expect(rbTree.root?.key).toBe(3);
|
||||
expect(rbTree.root?.left?.key).toBe(1);
|
||||
expect(rbTree.root?.right?.key).toBe(5);
|
||||
expect(rbTree.root?.right?.left?.key).toBe(4);
|
||||
expect(rbTree.root?.right?.key).toBe(9);
|
||||
expect(rbTree.root?.right?.left?.key).toBe(5);
|
||||
expect(rbTree.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(rbTree.getHeight()).toBe(4);
|
||||
expect(rbTree.getHeight()).toBe(3);
|
||||
expect(rbTree.has(9)).toBe(true);
|
||||
expect(rbTree.has(7)).toBe(false);
|
||||
expect(rbTree.bfs()).toEqual([3, 1, 5, 2, 4, 9, 6]);
|
||||
// expect(rbTree.bfs()).toEqual([3, 1, 5, 2, 4, 9, 6]);
|
||||
expect(rbTree.bfs()).toEqual([3, 1, 9, 2, 5, 4, 6]);
|
||||
const clonedRbTree = rbTree.clone();
|
||||
expect(clonedRbTree.size).toBe(7);
|
||||
expect(clonedRbTree.root?.key).toBe(3);
|
||||
|
@ -233,7 +235,7 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(clonedRbTree.root?.right?.key).toBe(5);
|
||||
expect(clonedRbTree.root?.right?.left?.key).toBe(4);
|
||||
expect(clonedRbTree.getNodeByKey(6)?.left?.key).toBe(NaN);
|
||||
expect(clonedRbTree.getHeight()).toBe(4);
|
||||
expect(clonedRbTree.getHeight()).toBe(3);
|
||||
expect(clonedRbTree.has(9)).toBe(true);
|
||||
expect(clonedRbTree.has(7)).toBe(false);
|
||||
expect(clonedRbTree.bfs()).toEqual([3, 1, 5, 2, 4, 9, 6]);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { BinaryTreeNode, BSTNode, IterationType, RBTNColor, RedBlackTree, RedBlackTreeNode } from '../../../../src';
|
||||
import { getRandomInt, getRandomIntArray, magnitude } from '../../../utils';
|
||||
import { isDebugTest } from '../../../config';
|
||||
import { OrderedMap } from 'js-sdsl';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
import { isDebugTest } from '../../../config';
|
||||
|
||||
describe('RedBlackTree', () => {
|
||||
const isDebug = isDebugTest;
|
||||
// const isDebug = true;
|
||||
|
||||
describe('RedBlackTree 1', () => {
|
||||
let tree: RedBlackTree<number>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -67,7 +69,7 @@ describe('RedBlackTree', () => {
|
|||
|
||||
it('should handle an empty tree', () => {
|
||||
const minNode = tree.getLeftMost(tree.root);
|
||||
expect(minNode).toBe(tree.Sentinel);
|
||||
expect(minNode).toBe(tree.SENTINEL);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -85,7 +87,7 @@ describe('RedBlackTree', () => {
|
|||
|
||||
it('should handle an empty tree', () => {
|
||||
const maxNode = tree.getRightMost(tree.root);
|
||||
expect(maxNode).toBe(tree.Sentinel);
|
||||
expect(maxNode).toBe(tree.SENTINEL);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -109,7 +111,7 @@ describe('RedBlackTree', () => {
|
|||
|
||||
const node = tree.getNode(10);
|
||||
const successorNode = tree.getSuccessor(node!);
|
||||
// TODO not sure if it should be undefined or tree.Sentinel
|
||||
// TODO not sure if it should be undefined or tree.SENTINEL
|
||||
expect(successorNode).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
@ -134,8 +136,8 @@ describe('RedBlackTree', () => {
|
|||
|
||||
const node = tree.getNode(20);
|
||||
const predecessorNode = tree.getPredecessor(node!);
|
||||
// TODO not sure if it should be tree.Sentinel or something else.
|
||||
expect(predecessorNode).toBe(tree.getNode(10));
|
||||
// TODO not sure if it should be tree.SENTINEL or something else.
|
||||
expect(predecessorNode).toBe(tree.getNode(20));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -214,7 +216,7 @@ describe('RedBlackTree 2', () => {
|
|||
tree.add(20);
|
||||
const node = tree.getNode(20);
|
||||
const predecessor = tree.getPredecessor(node!);
|
||||
expect(predecessor?.key).toBe(10);
|
||||
expect(predecessor?.key).toBe(20);
|
||||
});
|
||||
|
||||
it('should rotate nodes to the left', () => {
|
||||
|
@ -272,28 +274,28 @@ describe('RedBlackTree 2', () => {
|
|||
expect(node5F?.parent).toBe(node10F);
|
||||
expect(node15F?.key).toBe(15);
|
||||
expect(node15F?.color).toBe(RBTNColor.RED);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(node20F);
|
||||
expect(node21F?.key).toBe(21);
|
||||
expect(node21F?.color).toBe(RBTNColor.RED);
|
||||
expect(node21F?.left).toBe(tree.Sentinel);
|
||||
expect(node21F?.right).toBe(tree.Sentinel);
|
||||
expect(node21F?.left).toBe(tree.SENTINEL);
|
||||
expect(node21F?.right).toBe(tree.SENTINEL);
|
||||
expect(node21F?.parent).toBe(node20F);
|
||||
expect(node6F?.key).toBe(6);
|
||||
expect(node6F?.color).toBe(RBTNColor.RED);
|
||||
expect(node6F?.left).toBe(tree.Sentinel);
|
||||
expect(node6F?.right).toBe(tree.Sentinel);
|
||||
expect(node6F?.left).toBe(tree.SENTINEL);
|
||||
expect(node6F?.right).toBe(tree.SENTINEL);
|
||||
expect(node6F?.parent).toBe(node5F);
|
||||
expect(node2F?.key).toBe(2);
|
||||
expect(node2F?.color).toBe(RBTNColor.RED);
|
||||
expect(node2F?.left).toBe(tree.Sentinel);
|
||||
expect(node2F?.right).toBe(tree.Sentinel);
|
||||
expect(node2F?.left).toBe(tree.SENTINEL);
|
||||
expect(node2F?.right).toBe(tree.SENTINEL);
|
||||
expect(node2F?.parent).toBe(node5F);
|
||||
expect(node15F?.key).toBe(15);
|
||||
expect(node15F?.color).toBe(RBTNColor.RED);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(node20F);
|
||||
tree.delete(5);
|
||||
node10F = tree.getNode(10);
|
||||
|
@ -316,28 +318,28 @@ describe('RedBlackTree 2', () => {
|
|||
expect(node5F).toBe(undefined);
|
||||
expect(node15F?.key).toBe(15);
|
||||
expect(node15F?.color).toBe(RBTNColor.RED);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(node20F);
|
||||
expect(node21F?.key).toBe(21);
|
||||
expect(node21F?.color).toBe(RBTNColor.RED);
|
||||
expect(node21F?.left).toBe(tree.Sentinel);
|
||||
expect(node21F?.right).toBe(tree.Sentinel);
|
||||
expect(node21F?.left).toBe(tree.SENTINEL);
|
||||
expect(node21F?.right).toBe(tree.SENTINEL);
|
||||
expect(node21F?.parent).toBe(node20F);
|
||||
expect(node6F?.key).toBe(6);
|
||||
expect(node6F?.color).toBe(RBTNColor.BLACK);
|
||||
expect(node6F?.left).toBe(node2F);
|
||||
expect(node6F?.right).toBe(tree.Sentinel);
|
||||
expect(node6F?.right).toBe(tree.SENTINEL);
|
||||
expect(node6F?.parent).toBe(node10F);
|
||||
expect(node2F?.key).toBe(2);
|
||||
expect(node2F?.color).toBe(RBTNColor.RED);
|
||||
expect(node2F?.left).toBe(tree.Sentinel);
|
||||
expect(node2F?.right).toBe(tree.Sentinel);
|
||||
expect(node2F?.left).toBe(tree.SENTINEL);
|
||||
expect(node2F?.right).toBe(tree.SENTINEL);
|
||||
expect(node2F?.parent).toBe(node6F);
|
||||
expect(node15F?.key).toBe(15);
|
||||
expect(node15F?.color).toBe(RBTNColor.RED);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(node20F);
|
||||
tree.delete(20);
|
||||
node10F = tree.getNode(10);
|
||||
|
@ -356,28 +358,28 @@ describe('RedBlackTree 2', () => {
|
|||
expect(node5F).toBe(undefined);
|
||||
expect(node15F?.key).toBe(15);
|
||||
expect(node15F?.color).toBe(RBTNColor.RED);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(node21F);
|
||||
expect(node21F?.key).toBe(21);
|
||||
expect(node21F?.color).toBe(RBTNColor.BLACK);
|
||||
expect(node21F?.left).toBe(node15F);
|
||||
expect(node21F?.right).toBe(tree.Sentinel);
|
||||
expect(node21F?.right).toBe(tree.SENTINEL);
|
||||
expect(node21F?.parent).toBe(node10F);
|
||||
expect(node6F?.key).toBe(6);
|
||||
expect(node6F?.color).toBe(RBTNColor.BLACK);
|
||||
expect(node6F?.left).toBe(node2F);
|
||||
expect(node6F?.right).toBe(tree.Sentinel);
|
||||
expect(node6F?.right).toBe(tree.SENTINEL);
|
||||
expect(node6F?.parent).toBe(node10F);
|
||||
expect(node2F?.key).toBe(2);
|
||||
expect(node2F?.color).toBe(RBTNColor.RED);
|
||||
expect(node2F?.left).toBe(tree.Sentinel);
|
||||
expect(node2F?.right).toBe(tree.Sentinel);
|
||||
expect(node2F?.left).toBe(tree.SENTINEL);
|
||||
expect(node2F?.right).toBe(tree.SENTINEL);
|
||||
expect(node2F?.parent).toBe(node6F);
|
||||
expect(node15F?.key).toBe(15);
|
||||
expect(node15F?.color).toBe(RBTNColor.RED);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(node21F);
|
||||
});
|
||||
|
||||
|
@ -387,8 +389,8 @@ describe('RedBlackTree 2', () => {
|
|||
tree.add(5);
|
||||
tree.add(15);
|
||||
const node15F = tree.getNode(15);
|
||||
expect(node15F?.left).toBe(tree.Sentinel);
|
||||
expect(node15F?.right).toBe(tree.Sentinel);
|
||||
expect(node15F?.left).toBe(tree.SENTINEL);
|
||||
expect(node15F?.right).toBe(tree.SENTINEL);
|
||||
expect(node15F?.parent).toBe(tree.getNode(5));
|
||||
|
||||
tree.add(25);
|
||||
|
@ -403,19 +405,20 @@ describe('RedBlackTree 2', () => {
|
|||
tree.add(155);
|
||||
tree.add(225);
|
||||
const node225F = tree.getNode(225);
|
||||
expect(node225F?.left).toBe(tree.Sentinel);
|
||||
expect(node225F?.right).toBe(tree.Sentinel);
|
||||
expect(node225F?.left).toBe(tree.SENTINEL);
|
||||
expect(node225F?.right).toBe(tree.SENTINEL);
|
||||
expect(node225F?.parent?.key).toBe(155);
|
||||
tree.add(7);
|
||||
isDebug && tree.print();
|
||||
|
||||
const node15S = tree.getNode(15);
|
||||
expect(node15S?.left?.key).toBe(8);
|
||||
expect(node15S?.right?.key).toBe(28);
|
||||
expect(node15S).toBe(tree.root);
|
||||
expect(node15S?.parent).toBe(undefined);
|
||||
expect(node15S?.left?.key).toBe(10);
|
||||
expect(node15S?.right?.key).toBe(25);
|
||||
expect(tree.root).toBe(tree.getNode(8));
|
||||
expect(node15S?.parent?.key).toBe(28);
|
||||
tree.delete(15);
|
||||
expect(tree.root.key).toBe(22);
|
||||
expect(tree.root.parent).toBe(undefined);
|
||||
expect(tree.root?.key).toBe(8);
|
||||
expect(tree.root?.parent).toBe(undefined);
|
||||
|
||||
const node15T = tree.getNode(15);
|
||||
expect(node15T).toBe(undefined);
|
||||
|
@ -430,14 +433,14 @@ describe('RedBlackTree 2', () => {
|
|||
const node50 = tree.getNode(50);
|
||||
expect(node50?.key).toBe(50);
|
||||
expect(node50?.left?.key).toBe(33);
|
||||
expect(node50?.right).toBe(tree.Sentinel);
|
||||
expect(node50?.right).toBe(tree.SENTINEL);
|
||||
const node15Fo = tree.getNode(15);
|
||||
|
||||
expect(node15Fo?.key).toBe(15);
|
||||
expect(node15Fo?.left).toBe(tree.Sentinel);
|
||||
expect(node15Fo?.left).toBe(tree.SENTINEL);
|
||||
const node225S = tree.getNode(225);
|
||||
expect(node225S?.left).toBe(tree.Sentinel);
|
||||
expect(node225S?.right).toBe(tree.Sentinel);
|
||||
expect(node225S?.left).toBe(tree.SENTINEL);
|
||||
expect(node225S?.right).toBe(tree.SENTINEL);
|
||||
expect(node225S?.parent?.key).toBe(155);
|
||||
// TODO
|
||||
// expect(tree.getNode(0)).toBe(undefined);
|
||||
|
@ -474,6 +477,8 @@ describe('RedBlackTree 2', () => {
|
|||
|
||||
expect(tree.size).toBe(51);
|
||||
expect(tree.isBST()).toBe(true);
|
||||
expect(tree.isBST(tree.root, IterationType.RECURSIVE)).toBe(true);
|
||||
|
||||
expect(tree.dfs(n => n.key, 'in', tree.root, IterationType.ITERATIVE)).toEqual([
|
||||
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
|
||||
77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99
|
||||
|
@ -502,7 +507,7 @@ describe('RedBlackTree 2', () => {
|
|||
tree.delete(getRandomInt(-100, 1000));
|
||||
}
|
||||
|
||||
// TODO there is a bug when dfs the tree with Sentinel node
|
||||
// TODO there is a bug when dfs the tree with SENTINEL node
|
||||
// expect(tree.isBST()).toBe(true);
|
||||
});
|
||||
const { HUNDRED_THOUSAND } = magnitude;
|
||||
|
@ -541,71 +546,129 @@ describe('RedBlackTree 2', () => {
|
|||
tree.addMany([10, 20, 30, 40, 50, 60]);
|
||||
expect(tree.isAVLBalanced()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RedBlackTree iterative methods test', () => {
|
||||
let rbTree: RedBlackTree<number, string>;
|
||||
beforeEach(() => {
|
||||
rbTree = new RedBlackTree();
|
||||
rbTree.add([1, 'a']);
|
||||
rbTree.add(2, 'b');
|
||||
rbTree.add([3, 'c']);
|
||||
});
|
||||
describe('RedBlackTree delete test', function () {
|
||||
const rbTree = new RedBlackTree<number, number>();
|
||||
const inputSize = 100; // Adjust input sizes as needed
|
||||
|
||||
test('The node obtained by get Node should match the node type', () => {
|
||||
const node3 = rbTree.getNode(3);
|
||||
expect(node3).toBeInstanceOf(BinaryTreeNode);
|
||||
expect(node3).toBeInstanceOf(BSTNode);
|
||||
expect(node3).toBeInstanceOf(RedBlackTreeNode);
|
||||
});
|
||||
beforeEach(() => {
|
||||
rbTree.clear();
|
||||
});
|
||||
it('The structure remains normal after random deletion', function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
rbTree.add(i);
|
||||
}
|
||||
|
||||
test('forEach should iterate over all elements', () => {
|
||||
const mockCallback = jest.fn();
|
||||
rbTree.forEach((value, key) => {
|
||||
mockCallback(value, key);
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
rbTree.delete(num);
|
||||
}
|
||||
|
||||
let nanCount = 0;
|
||||
const dfs = (cur: RedBlackTreeNode<number>) => {
|
||||
if (isNaN(cur.key)) nanCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (rbTree.root) dfs(rbTree.root);
|
||||
|
||||
expect(rbTree.size).toBeLessThanOrEqual(inputSize);
|
||||
expect(rbTree.getHeight()).toBeLessThan(Math.log2(inputSize) * 2);
|
||||
|
||||
expect(nanCount).toBeLessThanOrEqual(inputSize);
|
||||
});
|
||||
|
||||
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]);
|
||||
it(`Random additions, complete deletions of structures are normal`, function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
if (i === 0 && isDebug) console.log(`first:`, num);
|
||||
rbTree.add(num);
|
||||
}
|
||||
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
rbTree.delete(i);
|
||||
}
|
||||
|
||||
let nanCount = 0;
|
||||
const dfs = (cur: RedBlackTreeNode<number>) => {
|
||||
if (isNaN(cur.key)) nanCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (rbTree.root) dfs(rbTree.root);
|
||||
|
||||
expect(rbTree.size).toBe(0);
|
||||
expect(rbTree.getHeight()).toBe(0);
|
||||
expect(nanCount).toBeLessThanOrEqual(inputSize);
|
||||
|
||||
isDebug && rbTree.print();
|
||||
});
|
||||
});
|
||||
|
||||
test('filter should return a new tree with filtered elements', () => {
|
||||
const filteredTree = rbTree.filter((value, key) => key > 1);
|
||||
expect(filteredTree.size).toBe(2);
|
||||
expect([...filteredTree]).toEqual([
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
});
|
||||
describe('RedBlackTree iterative methods test', () => {
|
||||
let rbTree: RedBlackTree<number, string>;
|
||||
beforeEach(() => {
|
||||
rbTree = new RedBlackTree();
|
||||
rbTree.add([1, 'a']);
|
||||
rbTree.add(2, 'b');
|
||||
rbTree.add([3, 'c']);
|
||||
});
|
||||
|
||||
test('map should return a new tree with modified elements', () => {
|
||||
const mappedTree = rbTree.map((value, key) => (key * 2).toString());
|
||||
expect(mappedTree.size).toBe(3);
|
||||
expect([...mappedTree]).toEqual([
|
||||
[1, '2'],
|
||||
[2, '4'],
|
||||
[3, '6']
|
||||
]);
|
||||
});
|
||||
test('The node obtained by get Node should match the node type', () => {
|
||||
const node3 = rbTree.getNode(3);
|
||||
expect(node3).toBeInstanceOf(BinaryTreeNode);
|
||||
expect(node3).toBeInstanceOf(BSTNode);
|
||||
expect(node3).toBeInstanceOf(RedBlackTreeNode);
|
||||
});
|
||||
|
||||
test('reduce should accumulate values', () => {
|
||||
const sum = rbTree.reduce((acc, value, key) => acc + key, 0);
|
||||
expect(sum).toBe(6);
|
||||
});
|
||||
test('forEach should iterate over all elements', () => {
|
||||
const mockCallback = jest.fn();
|
||||
rbTree.forEach((value, key) => {
|
||||
mockCallback(value, key);
|
||||
});
|
||||
|
||||
test('[Symbol.iterator] should provide an iterator', () => {
|
||||
const entries = [];
|
||||
for (const entry of rbTree) {
|
||||
entries.push(entry);
|
||||
}
|
||||
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]);
|
||||
});
|
||||
|
||||
expect(entries.length).toBe(3);
|
||||
expect(entries).toEqual([
|
||||
[1, 'a'],
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
test('filter should return a new tree with filtered elements', () => {
|
||||
const filteredTree = rbTree.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 = rbTree.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 = rbTree.reduce((acc, value, key) => acc + key, 0);
|
||||
expect(sum).toBe(6);
|
||||
});
|
||||
|
||||
test('[Symbol.iterator] should provide an iterator', () => {
|
||||
const entries = [];
|
||||
for (const entry of rbTree) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
expect(entries.length).toBe(3);
|
||||
expect(entries).toEqual([
|
||||
[1, 'a'],
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
TreeMultiMapNode
|
||||
} from '../../../../src';
|
||||
import { isDebugTest } from '../../../config';
|
||||
import { getRandomInt } from '../../../utils';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
// const isDebug = true;
|
||||
|
@ -28,6 +29,8 @@ describe('TreeMultiMap count', () => {
|
|||
const newNode = new TreeMultiMapNode(3, 33, 10);
|
||||
tm.add(newNode);
|
||||
expect(tm.count).toBe(15);
|
||||
expect(tm.getMutableCount()).toBe(15);
|
||||
expect(tm.getNode(3)?.count).toBe(11);
|
||||
});
|
||||
|
||||
it('Should count', () => {
|
||||
|
@ -37,17 +40,61 @@ describe('TreeMultiMap count', () => {
|
|||
[3, 3]
|
||||
]);
|
||||
tm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 1);
|
||||
expect(tm.count).toBe(7);
|
||||
expect(tm.getMutableCount()).toBe(7);
|
||||
expect(tm.count).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeMultiMap operations test1', () => {
|
||||
it('should perform various operations on a Binary Search Tree with numeric values1', () => {
|
||||
const treeMultimap = new TreeMultiMap();
|
||||
it('should size and count', () => {
|
||||
const treeMultiMap = new TreeMultiMap();
|
||||
|
||||
expect(treeMultimap instanceof TreeMultiMap);
|
||||
treeMultimap.add([11, 11]);
|
||||
treeMultimap.add([3, 3]);
|
||||
expect(treeMultiMap instanceof TreeMultiMap);
|
||||
|
||||
treeMultiMap.add([11, 11]);
|
||||
treeMultiMap.add([3, 3]);
|
||||
expect(treeMultiMap.count).toBe(2);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(2);
|
||||
expect(treeMultiMap.size).toBe(2);
|
||||
|
||||
const keyValuePairs: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
|
||||
treeMultiMap.addMany(keyValuePairs);
|
||||
expect(treeMultiMap.size).toBe(16);
|
||||
expect(treeMultiMap.count).toBe(18);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(18);
|
||||
treeMultiMap.delete(11);
|
||||
expect(treeMultiMap.count).toBe(17);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(17);
|
||||
treeMultiMap.delete(3, undefined, true);
|
||||
expect(treeMultiMap.count).toBe(15);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(15);
|
||||
});
|
||||
|
||||
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],
|
||||
|
@ -66,199 +113,201 @@ describe('TreeMultiMap operations test1', () => {
|
|||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
treeMultimap.addMany(idAndValues);
|
||||
expect(treeMultimap.root instanceof TreeMultiMapNode);
|
||||
treeMultiMap.addMany(idAndValues);
|
||||
expect(treeMultiMap.root instanceof TreeMultiMapNode);
|
||||
|
||||
if (treeMultimap.root) expect(treeMultimap.root.key == 11);
|
||||
if (treeMultiMap.root) expect(treeMultiMap.root.key == 11);
|
||||
|
||||
expect(treeMultimap.size).toBe(16);
|
||||
expect(treeMultimap.count).toBe(18);
|
||||
expect(treeMultiMap.size).toBe(16);
|
||||
expect(treeMultiMap.count).toBe(18);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(18);
|
||||
|
||||
expect(treeMultimap.has(6));
|
||||
|
||||
expect(treeMultimap.getHeight(6)).toBe(2);
|
||||
expect(treeMultimap.getDepth(6)).toBe(4);
|
||||
const nodeId10 = treeMultimap.getNode(10);
|
||||
expect(treeMultiMap.has(6));
|
||||
isDebug && treeMultiMap.print();
|
||||
expect(treeMultiMap.getHeight(6)).toBe(1);
|
||||
expect(treeMultiMap.getDepth(6)).toBe(3);
|
||||
const nodeId10 = treeMultiMap.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = treeMultimap.getNode(9, node => node.value);
|
||||
const nodeVal9 = treeMultiMap.getNode(9, node => node.value);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
|
||||
const nodesByCount1 = treeMultimap.getNodes(1, node => node.count);
|
||||
const nodesByCount1 = treeMultiMap.getNodes(1, node => node.count);
|
||||
expect(nodesByCount1.length).toBe(14);
|
||||
|
||||
const nodesByCount2 = treeMultimap.getNodes(2, node => node.count);
|
||||
const nodesByCount2 = treeMultiMap.getNodes(2, node => node.count);
|
||||
expect(nodesByCount2.length).toBe(2);
|
||||
const leftMost = treeMultimap.getLeftMost();
|
||||
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);
|
||||
const node15 = treeMultiMap.getNode(15);
|
||||
const minNodeBySpecificNode = node15 && treeMultiMap.getLeftMost(node15);
|
||||
expect(minNodeBySpecificNode?.key).toBe(14);
|
||||
|
||||
let subTreeSum = 0;
|
||||
node15 && treeMultimap.dfs(node => (subTreeSum += node.key), 'pre', 15);
|
||||
expect(subTreeSum).toBe(31);
|
||||
node15 && treeMultiMap.dfs(node => (subTreeSum += node.key), 'pre', 15);
|
||||
expect(subTreeSum).toBe(45);
|
||||
let lesserSum = 0;
|
||||
treeMultimap.lesserOrGreaterTraverse((node: TreeMultiMapNode<number>) => (lesserSum += node.key), CP.lt, 10);
|
||||
treeMultiMap.lesserOrGreaterTraverse((node: TreeMultiMapNode<number>) => (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);
|
||||
const subTreeAdd = treeMultiMap.dfs(node => (node.count += 1), 'pre', 15);
|
||||
expect(subTreeAdd);
|
||||
}
|
||||
const node11 = treeMultimap.getNode(11);
|
||||
const node11 = treeMultiMap.getNode(11);
|
||||
expect(node11 instanceof TreeMultiMapNode);
|
||||
if (node11 instanceof TreeMultiMapNode) {
|
||||
const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
|
||||
const allGreaterNodesAdded = treeMultiMap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
|
||||
expect(allGreaterNodesAdded);
|
||||
}
|
||||
|
||||
const dfsInorderNodes = treeMultimap.dfs(node => node, 'in');
|
||||
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);
|
||||
treeMultiMap.perfectlyBalance();
|
||||
expect(treeMultiMap.isPerfectlyBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.isPerfectlyBalanced()).toBe(true);
|
||||
expect(treeMultimap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
|
||||
const bfsNodesAfterBalanced = treeMultimap.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(8);
|
||||
const bfsNodesAfterBalanced = treeMultiMap.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(6);
|
||||
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
|
||||
|
||||
const removed11 = treeMultimap.delete(11, undefined, true);
|
||||
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.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight(15)).toBe(2);
|
||||
expect(treeMultiMap.getHeight(15)).toBe(1);
|
||||
|
||||
const removed1 = treeMultimap.delete(1, undefined, true);
|
||||
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.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight()).toBe(5);
|
||||
expect(treeMultiMap.getHeight()).toBe(5);
|
||||
|
||||
const removed4 = treeMultimap.delete(4, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(5);
|
||||
|
||||
const removed10 = treeMultimap.delete(10, undefined, true);
|
||||
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.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight()).toBe(5);
|
||||
expect(treeMultiMap.getHeight()).toBe(4);
|
||||
|
||||
const removed15 = treeMultimap.delete(15, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed5 = treeMultimap.delete(5, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed13 = treeMultimap.delete(13, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed3 = treeMultimap.delete(3, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed8 = treeMultimap.delete(8, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed6 = treeMultimap.delete(6, undefined, true);
|
||||
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.delete(6, undefined, true).length).toBe(0);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight()).toBe(4);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed7 = treeMultimap.delete(7, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed9 = treeMultimap.delete(9, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.getHeight()).toBe(2);
|
||||
|
||||
const removed14 = treeMultimap.delete(14, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.getHeight()).toBe(1);
|
||||
|
||||
expect(treeMultimap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsIDs = treeMultimap.bfs(node => node.key);
|
||||
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);
|
||||
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);
|
||||
expect(treeMultiMap.count).toBe(6);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(8);
|
||||
});
|
||||
|
||||
it('should perform various operations on a Binary Search Tree with object values', () => {
|
||||
|
@ -287,9 +336,10 @@ describe('TreeMultiMap operations test1', () => {
|
|||
|
||||
expect(objTreeMultiMap.root).toBeInstanceOf(TreeMultiMapNode);
|
||||
|
||||
if (objTreeMultiMap.root) expect(objTreeMultiMap.root.key).toBe(11);
|
||||
if (objTreeMultiMap.root) expect(objTreeMultiMap.root.key).toBe(5);
|
||||
|
||||
expect(objTreeMultiMap.count).toBe(16);
|
||||
expect(objTreeMultiMap.getMutableCount()).toBe(16);
|
||||
|
||||
expect(objTreeMultiMap.has(6)).toBe(true);
|
||||
});
|
||||
|
@ -297,11 +347,11 @@ describe('TreeMultiMap operations test1', () => {
|
|||
|
||||
describe('TreeMultiMap operations test recursively1', () => {
|
||||
it('should perform various operations on a Binary Search Tree with numeric values1', () => {
|
||||
const treeMultimap = new TreeMultiMap<number>([], { iterationType: IterationType.RECURSIVE });
|
||||
const treeMultiMap = new TreeMultiMap<number>([], { iterationType: IterationType.RECURSIVE });
|
||||
|
||||
expect(treeMultimap instanceof TreeMultiMap);
|
||||
treeMultimap.add([11, 11]);
|
||||
treeMultimap.add([3, 3]);
|
||||
expect(treeMultiMap instanceof TreeMultiMap);
|
||||
treeMultiMap.add([11, 11]);
|
||||
treeMultiMap.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
|
@ -320,42 +370,43 @@ describe('TreeMultiMap operations test recursively1', () => {
|
|||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
treeMultimap.addMany(idAndValues);
|
||||
expect(treeMultimap.root).toBeInstanceOf(TreeMultiMapNode);
|
||||
treeMultiMap.addMany(idAndValues);
|
||||
expect(treeMultiMap.root).toBeInstanceOf(TreeMultiMapNode);
|
||||
|
||||
if (treeMultimap.root) expect(treeMultimap.root.key).toBe(11);
|
||||
if (treeMultiMap.root) expect(treeMultiMap.root.key).toBe(5);
|
||||
|
||||
expect(treeMultimap.size).toBe(16);
|
||||
expect(treeMultimap.count).toBe(18);
|
||||
expect(treeMultiMap.size).toBe(16);
|
||||
expect(treeMultiMap.count).toBe(18);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(18);
|
||||
|
||||
expect(treeMultimap.has(6));
|
||||
expect(treeMultiMap.has(6));
|
||||
|
||||
expect(treeMultimap.getHeight(6)).toBe(2);
|
||||
expect(treeMultimap.getDepth(6)).toBe(4);
|
||||
const nodeId10 = treeMultimap.getNode(10);
|
||||
expect(treeMultiMap.getHeight(6)).toBe(1);
|
||||
expect(treeMultiMap.getDepth(6)).toBe(3);
|
||||
const nodeId10 = treeMultiMap.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = treeMultimap.getNode(9, node => node.value);
|
||||
const nodeVal9 = treeMultiMap.getNode(9, node => node.value);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
|
||||
const nodesByCount1 = treeMultimap.getNodes(1, node => node.count);
|
||||
const nodesByCount1 = treeMultiMap.getNodes(1, node => node.count);
|
||||
expect(nodesByCount1.length).toBe(14);
|
||||
|
||||
const nodesByCount2 = treeMultimap.getNodes(2, node => node.count);
|
||||
const nodesByCount2 = treeMultiMap.getNodes(2, node => node.count);
|
||||
expect(nodesByCount2.length).toBe(2);
|
||||
const leftMost = treeMultimap.getLeftMost();
|
||||
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);
|
||||
const node15 = treeMultiMap.getNode(15);
|
||||
const minNodeBySpecificNode = node15 && treeMultiMap.getLeftMost(node15);
|
||||
expect(minNodeBySpecificNode?.key).toBe(14);
|
||||
|
||||
let subTreeSum = 0;
|
||||
node15 && treeMultimap.dfs(node => (subTreeSum += node.key), 'pre', 15);
|
||||
expect(subTreeSum).toBe(31);
|
||||
node15 && treeMultiMap.dfs(node => (subTreeSum += node.key), 'pre', 15);
|
||||
expect(subTreeSum).toBe(45);
|
||||
let lesserSum = 0;
|
||||
expect(treeMultimap.has(9)).toBe(true);
|
||||
treeMultimap.lesserOrGreaterTraverse(
|
||||
expect(treeMultiMap.has(9)).toBe(true);
|
||||
treeMultiMap.lesserOrGreaterTraverse(
|
||||
node => {
|
||||
lesserSum += node.key;
|
||||
return node.key;
|
||||
|
@ -367,160 +418,161 @@ describe('TreeMultiMap operations test recursively1', () => {
|
|||
|
||||
expect(node15 instanceof TreeMultiMapNode);
|
||||
if (node15 instanceof TreeMultiMapNode) {
|
||||
const subTreeAdd = treeMultimap.dfs(node => (node.count += 1), 'pre', 15);
|
||||
const subTreeAdd = treeMultiMap.dfs(node => (node.count += 1), 'pre', 15);
|
||||
expect(subTreeAdd);
|
||||
}
|
||||
const node11 = treeMultimap.getNode(11);
|
||||
const node11 = treeMultiMap.getNode(11);
|
||||
expect(node11 instanceof TreeMultiMapNode);
|
||||
if (node11 instanceof TreeMultiMapNode) {
|
||||
const allGreaterNodesAdded = treeMultimap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
|
||||
const allGreaterNodesAdded = treeMultiMap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
|
||||
expect(allGreaterNodesAdded);
|
||||
}
|
||||
|
||||
const dfsInorderNodes = treeMultimap.dfs(node => node, 'in');
|
||||
const dfsInorderNodes = treeMultiMap.dfs(node => node, 'in');
|
||||
expect(dfsInorderNodes[0].key).toBe(1);
|
||||
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
|
||||
expect(treeMultimap.isPerfectlyBalanced()).toBe(false);
|
||||
expect(treeMultiMap.isPerfectlyBalanced()).toBe(true);
|
||||
|
||||
treeMultimap.perfectlyBalance();
|
||||
treeMultiMap.perfectlyBalance();
|
||||
|
||||
expect(treeMultimap.isPerfectlyBalanced()).toBe(true);
|
||||
expect(treeMultimap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.isPerfectlyBalanced()).toBe(false);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
|
||||
const bfsNodesAfterBalanced = treeMultimap.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(8);
|
||||
const bfsNodesAfterBalanced = treeMultiMap.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(6);
|
||||
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
|
||||
|
||||
const removed11 = treeMultimap.delete(11, undefined, true);
|
||||
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.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight(15)).toBe(2);
|
||||
expect(treeMultiMap.getHeight(15)).toBe(1);
|
||||
|
||||
const removed1 = treeMultimap.delete(1, undefined, true);
|
||||
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.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight()).toBe(5);
|
||||
expect(treeMultiMap.getHeight()).toBe(5);
|
||||
|
||||
const removed4 = treeMultimap.delete(4, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(5);
|
||||
|
||||
const removed10 = treeMultimap.delete(10, undefined, true);
|
||||
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.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight()).toBe(5);
|
||||
expect(treeMultiMap.getHeight()).toBe(4);
|
||||
|
||||
const removed15 = treeMultimap.delete(15, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed5 = treeMultimap.delete(5, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed13 = treeMultimap.delete(13, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed3 = treeMultimap.delete(3, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed8 = treeMultimap.delete(8, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed6 = treeMultimap.delete(6, undefined, true);
|
||||
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.delete(6, undefined, true).length).toBe(0);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeMultimap.getHeight()).toBe(4);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed7 = treeMultimap.delete(7, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(false);
|
||||
expect(treeMultiMap.getHeight()).toBe(3);
|
||||
|
||||
const removed9 = treeMultimap.delete(9, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.getHeight()).toBe(2);
|
||||
|
||||
const removed14 = treeMultimap.delete(14, undefined, true);
|
||||
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);
|
||||
expect(treeMultiMap.getHeight()).toBe(1);
|
||||
|
||||
expect(treeMultimap.isAVLBalanced()).toBe(true);
|
||||
expect(treeMultiMap.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsIDs = treeMultimap.bfs(node => node.key);
|
||||
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);
|
||||
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);
|
||||
expect(treeMultiMap.count).toBe(6);
|
||||
expect(treeMultiMap.getMutableCount()).toBe(8);
|
||||
});
|
||||
|
||||
it('should perform various operations on a Binary Search Tree with object values', () => {
|
||||
|
@ -549,17 +601,18 @@ describe('TreeMultiMap operations test recursively1', () => {
|
|||
|
||||
expect(objTreeMultiMap.root).toBeInstanceOf(TreeMultiMapNode);
|
||||
|
||||
if (objTreeMultiMap.root) expect(objTreeMultiMap.root.key).toBe(11);
|
||||
if (objTreeMultiMap.root) expect(objTreeMultiMap.root.key).toBe(5);
|
||||
|
||||
expect(objTreeMultiMap.count).toBe(16);
|
||||
expect(objTreeMultiMap.getMutableCount()).toBe(16);
|
||||
|
||||
expect(objTreeMultiMap.has(6)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeMultiMap Performance test', function () {
|
||||
describe('TreeMultiMap delete test', function () {
|
||||
const treeMS = new TreeMultiMap<number, number>();
|
||||
const inputSize = 1000; // Adjust input sizes as needed
|
||||
const inputSize = 100; // Adjust input sizes as needed
|
||||
|
||||
beforeEach(() => {
|
||||
treeMS.clear();
|
||||
|
@ -571,52 +624,117 @@ describe('TreeMultiMap Performance test', function () {
|
|||
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();
|
||||
it('The structure remains normal after random deletion', function () {
|
||||
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);
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
treeMS.delete(num);
|
||||
}
|
||||
|
||||
let nanCount = 0;
|
||||
const dfs = (cur: TreeMultiMapNode<number>) => {
|
||||
if (isNaN(cur.key)) nanCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (treeMS.root) dfs(treeMS.root);
|
||||
|
||||
expect(treeMS.size).toBeLessThanOrEqual(inputSize);
|
||||
expect(treeMS.getHeight()).toBeGreaterThan(Math.log2(inputSize));
|
||||
expect(treeMS.getHeight()).toBeLessThan(Math.log2(inputSize) * 2);
|
||||
|
||||
expect(nanCount).toBeLessThanOrEqual(inputSize);
|
||||
});
|
||||
|
||||
it(`Random additions, complete deletions of structures are normal`, function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
if (i === 0 && isDebug) console.log(`first:`, num);
|
||||
treeMS.add(num);
|
||||
}
|
||||
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
treeMS.delete(i, undefined, true);
|
||||
}
|
||||
|
||||
let nanCount = 0;
|
||||
const dfs = (cur: TreeMultiMapNode<number>) => {
|
||||
if (isNaN(cur.key)) nanCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (treeMS.root) dfs(treeMS.root);
|
||||
|
||||
expect(treeMS.size).toBe(0);
|
||||
expect(treeMS.getHeight()).toBe(0);
|
||||
expect(nanCount).toBeLessThanOrEqual(inputSize);
|
||||
|
||||
isDebug && treeMS.print();
|
||||
});
|
||||
|
||||
it(`Random additions, count deletions of structures are normal`, function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
if (i === 0 && isDebug) console.log(`first:`, num);
|
||||
treeMS.add(num);
|
||||
}
|
||||
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
treeMS.delete(i);
|
||||
}
|
||||
|
||||
let nanCount = 0;
|
||||
const dfs = (cur: TreeMultiMapNode<number>) => {
|
||||
if (isNaN(cur.key)) nanCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (treeMS.root) dfs(treeMS.root);
|
||||
|
||||
expect(treeMS.size).toBeGreaterThanOrEqual(0);
|
||||
expect(treeMS.getHeight()).toBeGreaterThanOrEqual(0);
|
||||
expect(nanCount).toBeLessThanOrEqual(inputSize);
|
||||
|
||||
isDebug && treeMS.print();
|
||||
});
|
||||
|
||||
it('should the clone method', () => {
|
||||
function checkTreeStructure(treeMultimap: TreeMultiMap<string, number>) {
|
||||
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);
|
||||
function checkTreeStructure(treeMultiMap: TreeMultiMap<string, number>) {
|
||||
expect(treeMultiMap.size).toBe(4);
|
||||
expect(treeMultiMap.root?.key).toBe('2');
|
||||
expect(treeMultiMap.root?.left?.key).toBe('1');
|
||||
expect(treeMultiMap.root?.left?.left?.key).toBe(NaN);
|
||||
expect(treeMultiMap.root?.left?.right?.key).toBe(NaN);
|
||||
expect(treeMultiMap.root?.right?.key).toBe('4');
|
||||
expect(treeMultiMap.root?.right?.left?.key).toBe(NaN);
|
||||
expect(treeMultiMap.root?.right?.right?.key).toBe('5');
|
||||
}
|
||||
|
||||
const treeMultimap = new TreeMultiMap<string, number>();
|
||||
treeMultimap.addMany([
|
||||
const treeMultiMap = new TreeMultiMap<string, number>();
|
||||
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();
|
||||
expect(treeMultiMap.size).toBe(5);
|
||||
expect(treeMultiMap.root?.key).toBe('2');
|
||||
expect(treeMultiMap.root?.left?.key).toBe('1');
|
||||
expect(treeMultiMap.root?.left?.left?.key).toBe(NaN);
|
||||
expect(treeMultiMap.root?.left?.right?.key).toBe(NaN);
|
||||
expect(treeMultiMap.root?.right?.key).toBe('4');
|
||||
expect(treeMultiMap.root?.right?.left?.key).toBe(`3`);
|
||||
expect(treeMultiMap.root?.right?.right?.key).toBe('5');
|
||||
treeMultiMap.delete('3');
|
||||
checkTreeStructure(treeMultiMap);
|
||||
const cloned = treeMultiMap.clone();
|
||||
checkTreeStructure(cloned);
|
||||
cloned.delete('1');
|
||||
expect(treeMultimap.size).toBe(4);
|
||||
expect(treeMultiMap.size).toBe(4);
|
||||
expect(cloned.size).toBe(3);
|
||||
});
|
||||
});
|
||||
|
@ -689,9 +807,10 @@ describe('TreeMultiMap iterative methods test', () => {
|
|||
|
||||
test('should clone work well', () => {
|
||||
expect(treeMM.count).toBe(21);
|
||||
expect(treeMM.getMutableCount()).toBe(21);
|
||||
const cloned = treeMM.clone();
|
||||
expect(cloned.root?.left?.key).toBe(NaN);
|
||||
expect(cloned.root?.right?.value).toBe('b');
|
||||
expect(cloned.root?.left?.key).toBe(1);
|
||||
expect(cloned.root?.right?.value).toBe('c');
|
||||
});
|
||||
|
||||
test('should keys', () => {
|
||||
|
|
Loading…
Reference in a new issue