Refactor: Use 'this.isRealNode' instead of explicit null checks in getHeight, getMinHeight, and getNodeByKey methods in the binary tree. In the red-black tree, directly invoke the parent class getNode method, significantly improving the speed of red-black tree node deletion and retrieval.

This commit is contained in:
Revone 2024-01-12 14:15:05 +08:00
parent 644ce69190
commit a653d465fa
6 changed files with 534 additions and 521 deletions

View file

@ -1021,7 +1021,7 @@ export class BinaryTree<
*/
getHeight(beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root, iterationType = this.iterationType): number {
beginRoot = this.ensureNode(beginRoot);
if (!beginRoot) return -1;
if (!this.isRealNode(beginRoot)) return -1;
if (iterationType === IterationType.RECURSIVE) {
const _getMaxHeight = (cur: NODE | null | undefined): number => {
@ -1073,8 +1073,8 @@ export class BinaryTree<
if (iterationType === IterationType.RECURSIVE) {
const _getMinHeight = (cur: NODE | null | undefined): number => {
if (!cur) return 0;
if (!cur.left && !cur.right) return 0;
if (!this.isRealNode(cur)) return 0;
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return 0;
const leftMinHeight = _getMinHeight(cur.left);
const rightMinHeight = _getMinHeight(cur.right);
return Math.min(leftMinHeight, rightMinHeight) + 1;
@ -1088,16 +1088,16 @@ export class BinaryTree<
const depths: Map<NODE, number> = new Map();
while (stack.length > 0 || node) {
if (node) {
if (this.isRealNode(node)) {
stack.push(node);
node = node.left;
} else {
node = stack[stack.length - 1];
if (!node.right || last === node.right) {
if (!this.isRealNode(node.right) || last === node.right) {
node = stack.pop();
if (node) {
const leftMinHeight = node.left ? depths.get(node.left) ?? -1 : -1;
const rightMinHeight = node.right ? depths.get(node.right) ?? -1 : -1;
if (this.isRealNode(node)) {
const leftMinHeight = this.isRealNode(node.left) ? depths.get(node.left) ?? -1 : -1;
const rightMinHeight = this.isRealNode(node.right) ? depths.get(node.right) ?? -1 : -1;
depths.set(node, 1 + Math.min(leftMinHeight, rightMinHeight));
last = node;
node = null;
@ -1169,9 +1169,10 @@ export class BinaryTree<
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType = this.iterationType
): NODE | null | undefined {
if (this.isNIL(beginRoot)) return beginRoot as NODE;
beginRoot = this.ensureNode(beginRoot);
if (!beginRoot) return beginRoot;
if (!this.isRealNode(beginRoot)) return beginRoot;
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: NODE): NODE => {
@ -1215,6 +1216,7 @@ export class BinaryTree<
beginRoot: KeyOrNodeOrEntry<K, V, NODE> = this.root,
iterationType = this.iterationType
): NODE | null | undefined {
if (this.isNIL(beginRoot)) return beginRoot as NODE;
// TODO support get right most by passing key in
beginRoot = this.ensureNode(beginRoot);
if (!beginRoot) return beginRoot;

View file

@ -426,14 +426,14 @@ export class BST<
* found in the binary tree. If no node is found, it returns `undefined`.
*/
override getNodeByKey(key: K, iterationType = IterationType.ITERATIVE): NODE | undefined {
if (!this.root) return undefined;
if (!this.isRealNode(this.root)) return undefined;
if (iterationType === IterationType.RECURSIVE) {
const _dfs = (cur: NODE): NODE | undefined => {
if (cur.key === key) return cur;
if (!cur.left && !cur.right) return;
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
if (this._compare(cur.key, key) === CP.gt && cur.left) return _dfs(cur.left);
if (this._compare(cur.key, key) === CP.lt && cur.right) return _dfs(cur.right);
if (this._compare(cur.key, key) === CP.gt && this.isRealNode(cur.left)) return _dfs(cur.left);
if (this._compare(cur.key, key) === CP.lt && this.isRealNode(cur.right)) return _dfs(cur.right);
};
return _dfs(this.root);
@ -441,10 +441,10 @@ export class BST<
const queue = new Queue<NODE>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (this.isRealNode(cur)) {
if (this._compare(cur.key, key) === CP.eq) return cur;
if (this._compare(cur.key, key) === CP.gt) cur.left && queue.push(cur.left);
if (this._compare(cur.key, key) === CP.lt) cur.right && queue.push(cur.right);
if (this._compare(cur.key, key) === CP.gt) this.isRealNode(cur.left) && queue.push(cur.left);
if (this._compare(cur.key, key) === CP.lt) this.isRealNode(cur.right) && queue.push(cur.right);
}
}
}
@ -497,14 +497,14 @@ export class BST<
if (onlyOne) return;
}
if (!cur.left && !cur.right) return;
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
// TODO potential bug
if (callback === this._defaultOneParamCallback) {
if (this._compare(cur.key, identifier as K) === CP.gt) cur.left && _traverse(cur.left);
if (this._compare(cur.key, identifier as K) === CP.lt) cur.right && _traverse(cur.right);
if (this._compare(cur.key, identifier as K) === CP.gt) this.isRealNode(cur.left) && _traverse(cur.left);
if (this._compare(cur.key, identifier as K) === CP.lt) this.isRealNode(cur.right) && _traverse(cur.right);
} else {
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
this.isRealNode(cur.left) && _traverse(cur.left);
this.isRealNode(cur.right) && _traverse(cur.right);
}
};
@ -513,7 +513,7 @@ export class BST<
const queue = new Queue<NODE>([beginRoot]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (this.isRealNode(cur)) {
const callbackResult = callback(cur);
if (callbackResult === identifier) {
ans.push(cur);
@ -521,11 +521,11 @@ export class BST<
}
// TODO potential bug
if (callback === this._defaultOneParamCallback) {
if (this._compare(cur.key, identifier as K) === CP.gt) cur.left && queue.push(cur.left);
if (this._compare(cur.key, identifier as K) === CP.lt) cur.right && queue.push(cur.right);
if (this._compare(cur.key, identifier as K) === CP.gt) this.isRealNode(cur.left) && queue.push(cur.left);
if (this._compare(cur.key, identifier as K) === CP.lt) this.isRealNode(cur.right) && queue.push(cur.right);
} else {
cur.left && queue.push(cur.left);
cur.right && queue.push(cur.right);
this.isRealNode(cur.left) && queue.push(cur.left);
this.isRealNode(cur.right) && queue.push(cur.right);
}
}
}

View file

@ -235,8 +235,7 @@ export class RedBlackTree<
iterationType = this.iterationType
): NODE | null | undefined {
if ((identifier as any) instanceof RedBlackTreeNode) callback = (node => node) as C;
beginRoot = this.ensureNode(beginRoot);
return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined;
return super.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined;
}
/**

View file

@ -35,7 +35,7 @@ describe('AVLTreeMultiMap count', () => {
[2, 2],
[3, 3]
]);
tm.add([2, 2], undefined, 10)
tm.add([2, 2], undefined, 10);
tm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 1);
tm.delete(2);
expect(tm.count).toBe(12);

View file

@ -8,136 +8,136 @@ const isDebug = isDebugTest;
// const isDebug = true;
describe('RedBlackTree 1', () => {
let tree: RedBlackTree<number>;
let rbTree: RedBlackTree<number>;
beforeEach(() => {
tree = new RedBlackTree<number>();
rbTree = new RedBlackTree<number>();
});
describe('add and getNode', () => {
it('should add and find a node in the tree', () => {
tree.add(10);
tree.add(20);
tree.add(5);
it('should add and find a node in the rbTree', () => {
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
expect(tree.getNode(10)).toBeInstanceOf(RedBlackTreeNode);
expect(tree.getNode(20)).toBeInstanceOf(RedBlackTreeNode);
expect(tree.getNode(5)).toBeInstanceOf(RedBlackTreeNode);
expect(tree.getNode(15)).toBe(undefined);
expect(rbTree.getNode(10)).toBeInstanceOf(RedBlackTreeNode);
expect(rbTree.getNode(20)).toBeInstanceOf(RedBlackTreeNode);
expect(rbTree.getNode(5)).toBeInstanceOf(RedBlackTreeNode);
expect(rbTree.getNode(15)).toBe(undefined);
});
it('should add and find nodes with negative keys', () => {
tree.add(-10);
tree.add(-20);
rbTree.add(-10);
rbTree.add(-20);
expect(tree.getNode(-10)).toBeInstanceOf(RedBlackTreeNode);
expect(tree.getNode(-20)).toBeInstanceOf(RedBlackTreeNode);
expect(rbTree.getNode(-10)).toBeInstanceOf(RedBlackTreeNode);
expect(rbTree.getNode(-20)).toBeInstanceOf(RedBlackTreeNode);
});
});
describe('deleteNode', () => {
it('should delete a node from the tree', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.delete(20);
it('should delete a node from the rbTree', () => {
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.delete(20);
expect(tree.getNode(20)).toBe(undefined);
expect(rbTree.getNode(20)).toBe(undefined);
});
it('should handle deleting a non-existent node', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.delete(15);
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.delete(15);
expect(tree.getNode(15)).toBe(undefined);
expect(rbTree.getNode(15)).toBe(undefined);
});
});
describe('minimum', () => {
it('should find the minimum node in the tree', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.add(15);
tree.add(3);
it('should find the minimum node in the rbTree', () => {
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.add(15);
rbTree.add(3);
const minNode = tree.getLeftMost(tree.root);
const minNode = rbTree.getLeftMost(rbTree.root);
expect(minNode?.key).toBe(3);
});
it('should handle an empty tree', () => {
const minNode = tree.getLeftMost(tree.root);
expect(minNode).toBe(tree.SENTINEL);
it('should handle an empty rbTree', () => {
const minNode = rbTree.getLeftMost(rbTree.root);
expect(minNode).toBe(rbTree.SENTINEL);
});
});
describe('getRightMost', () => {
it('should find the getRightMost node in the tree', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.add(15);
tree.add(25);
it('should find the getRightMost node in the rbTree', () => {
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.add(15);
rbTree.add(25);
const maxNode = tree.getRightMost(tree.root);
const maxNode = rbTree.getRightMost(rbTree.root);
expect(maxNode?.key).toBe(25);
});
it('should handle an empty tree', () => {
const maxNode = tree.getRightMost(tree.root);
expect(maxNode).toBe(tree.SENTINEL);
it('should handle an empty rbTree', () => {
const maxNode = rbTree.getRightMost(rbTree.root);
expect(maxNode).toBe(rbTree.SENTINEL);
});
});
describe('getSuccessor', () => {
it('should find the getSuccessor of a node', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.add(15);
tree.add(25);
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.add(15);
rbTree.add(25);
const node = tree.getNode(15);
const successorNode = tree.getSuccessor(node!);
const node = rbTree.getNode(15);
const successorNode = rbTree.getSuccessor(node!);
expect(successorNode?.key).toBe(20);
});
it('should handle a node with no getSuccessor', () => {
tree.add(10);
tree.add(5);
rbTree.add(10);
rbTree.add(5);
const node = tree.getNode(10);
const successorNode = tree.getSuccessor(node!);
// TODO not sure if it should be undefined or tree.SENTINEL
const node = rbTree.getNode(10);
const successorNode = rbTree.getSuccessor(node!);
// TODO not sure if it should be undefined or rbTree.SENTINEL
expect(successorNode).toBe(undefined);
});
});
describe('getPredecessor', () => {
it('should find the getPredecessor of a node', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.add(15);
tree.add(25);
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.add(15);
rbTree.add(25);
const node = tree.getNode(20);
const predecessorNode = tree.getPredecessor(node!);
const node = rbTree.getNode(20);
const predecessorNode = rbTree.getPredecessor(node!);
expect(predecessorNode?.key).toBe(15);
});
it('should handle a node with no getPredecessor', () => {
tree.add(10);
tree.add(20);
rbTree.add(10);
rbTree.add(20);
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(20));
const node = rbTree.getNode(20);
const predecessorNode = rbTree.getPredecessor(node!);
// TODO not sure if it should be rbTree.SENTINEL or something else.
expect(predecessorNode).toBe(rbTree.getNode(20));
});
});
@ -180,83 +180,83 @@ describe('RedBlackTree 1', () => {
});
describe('RedBlackTree 2', () => {
let tree: RedBlackTree<number>;
let rbTree: RedBlackTree<number>;
beforeEach(() => {
tree = new RedBlackTree<number>();
rbTree = new RedBlackTree<number>();
});
it('should add nodes into the tree', () => {
tree.add(10);
expect(tree.getNode(10)).toBeDefined();
tree.add(20);
expect(tree.getNode(20)).toBeDefined();
tree.add(5);
expect(tree.getNode(5)).toBeDefined();
it('should add nodes into the rbTree', () => {
rbTree.add(10);
expect(rbTree.getNode(10)).toBeDefined();
rbTree.add(20);
expect(rbTree.getNode(20)).toBeDefined();
rbTree.add(5);
expect(rbTree.getNode(5)).toBeDefined();
});
it('should delete nodes from the tree', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.delete(20);
expect(tree.getNode(20)).toBe(undefined);
it('should delete nodes from the rbTree', () => {
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.delete(20);
expect(rbTree.getNode(20)).toBe(undefined);
});
it('should get the successor of a node', () => {
tree.add(10);
tree.add(20);
const node = tree.getNode(10);
const successor = tree.getSuccessor(node!);
rbTree.add(10);
rbTree.add(20);
const node = rbTree.getNode(10);
const successor = rbTree.getSuccessor(node!);
expect(successor?.key).toBe(20);
});
it('should get the predecessor of a node', () => {
tree.add(10);
tree.add(20);
const node = tree.getNode(20);
const predecessor = tree.getPredecessor(node!);
rbTree.add(10);
rbTree.add(20);
const node = rbTree.getNode(20);
const predecessor = rbTree.getPredecessor(node!);
expect(predecessor?.key).toBe(20);
});
it('should rotate nodes to the left', () => {
tree.add(10);
tree.add(20);
tree.add(5);
const node = tree.getNode(10);
tree.add(15);
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
const node = rbTree.getNode(10);
rbTree.add(15);
// Verify that rotation has occurred
expect(node?.left?.key).toBe(5);
expect(node?.right?.key).toBe(20);
});
it('should rotate nodes to the right', () => {
tree.add(10);
tree.add(20);
tree.add(5);
const node = tree.getNode(20);
tree.add(25);
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
const node = rbTree.getNode(20);
rbTree.add(25);
// Verify that rotation has occurred
expect(node?.left?.key).toBeNaN();
expect(node?.right?.key).toBe(25);
});
it('should all node attributes fully conform to the red-black tree standards.', () => {
tree.add(10);
tree.add(20);
tree.add(5);
tree.add(15);
tree.add(21);
tree.add(6);
tree.add(2);
it('should all node attributes fully conform to the red-black rbTree standards.', () => {
rbTree.add(10);
rbTree.add(20);
rbTree.add(5);
rbTree.add(15);
rbTree.add(21);
rbTree.add(6);
rbTree.add(2);
let node10F = tree.getNode(10);
let node20F = tree.getNode(20);
let node5F = tree.getNode(5);
let node15F = tree.getNode(15);
let node21F = tree.getNode(21);
let node6F = tree.getNode(6);
let node2F = tree.getNode(2);
let node10F = rbTree.getNode(10);
let node20F = rbTree.getNode(20);
let node5F = rbTree.getNode(5);
let node15F = rbTree.getNode(15);
let node21F = rbTree.getNode(21);
let node6F = rbTree.getNode(6);
let node2F = rbTree.getNode(2);
expect(node10F?.key).toBe(10);
expect(node10F?.color).toBe(RBTNColor.BLACK);
expect(node10F?.left).toBe(node5F);
@ -274,37 +274,37 @@ 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(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node21F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node6F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node2F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.SENTINEL);
expect(node15F?.parent).toBe(node20F);
tree.delete(5);
node10F = tree.getNode(10);
node20F = tree.getNode(20);
node5F = tree.getNode(5);
node15F = tree.getNode(15);
node21F = tree.getNode(21);
node6F = tree.getNode(6);
node2F = tree.getNode(2);
rbTree.delete(5);
node10F = rbTree.getNode(10);
node20F = rbTree.getNode(20);
node5F = rbTree.getNode(5);
node15F = rbTree.getNode(15);
node21F = rbTree.getNode(21);
node6F = rbTree.getNode(6);
node2F = rbTree.getNode(2);
expect(node10F?.key).toBe(10);
expect(node10F?.color).toBe(RBTNColor.BLACK);
expect(node10F?.left).toBe(node6F);
@ -318,37 +318,37 @@ 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(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node21F?.right).toBe(rbTree.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(rbTree.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(rbTree.SENTINEL);
expect(node2F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.SENTINEL);
expect(node15F?.parent).toBe(node20F);
tree.delete(20);
node10F = tree.getNode(10);
node20F = tree.getNode(20);
node5F = tree.getNode(5);
node15F = tree.getNode(15);
node21F = tree.getNode(21);
node6F = tree.getNode(6);
node2F = tree.getNode(2);
rbTree.delete(20);
node10F = rbTree.getNode(10);
node20F = rbTree.getNode(20);
node5F = rbTree.getNode(5);
node15F = rbTree.getNode(15);
node21F = rbTree.getNode(21);
node6F = rbTree.getNode(6);
node2F = rbTree.getNode(2);
expect(node10F?.key).toBe(10);
expect(node10F?.color).toBe(RBTNColor.BLACK);
expect(node10F?.left).toBe(node6F);
@ -358,167 +358,167 @@ 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(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.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(rbTree.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(rbTree.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(rbTree.SENTINEL);
expect(node2F?.right).toBe(rbTree.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(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.SENTINEL);
expect(node15F?.parent).toBe(node21F);
});
it('should fix the tree after insertion', () => {
tree.add(1);
tree.add(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?.parent).toBe(tree.getNode(5));
it('should fix the rbTree after insertion', () => {
rbTree.add(1);
rbTree.add(2);
rbTree.add(5);
rbTree.add(15);
const node15F = rbTree.getNode(15);
expect(node15F?.left).toBe(rbTree.SENTINEL);
expect(node15F?.right).toBe(rbTree.SENTINEL);
expect(node15F?.parent).toBe(rbTree.getNode(5));
tree.add(25);
tree.add(10);
tree.add(8);
tree.add(28);
tree.add(111);
tree.add(12);
tree.delete(2);
tree.add(22);
tree.add(50);
tree.add(155);
tree.add(225);
const node225F = tree.getNode(225);
expect(node225F?.left).toBe(tree.SENTINEL);
expect(node225F?.right).toBe(tree.SENTINEL);
rbTree.add(25);
rbTree.add(10);
rbTree.add(8);
rbTree.add(28);
rbTree.add(111);
rbTree.add(12);
rbTree.delete(2);
rbTree.add(22);
rbTree.add(50);
rbTree.add(155);
rbTree.add(225);
const node225F = rbTree.getNode(225);
expect(node225F?.left).toBe(rbTree.SENTINEL);
expect(node225F?.right).toBe(rbTree.SENTINEL);
expect(node225F?.parent?.key).toBe(155);
tree.add(7);
isDebug && tree.print();
rbTree.add(7);
isDebug && rbTree.print();
const node15S = tree.getNode(15);
const node15S = rbTree.getNode(15);
expect(node15S?.left?.key).toBe(10);
expect(node15S?.right?.key).toBe(25);
expect(tree.root).toBe(tree.getNode(8));
expect(rbTree.root).toBe(rbTree.getNode(8));
expect(node15S?.parent?.key).toBe(28);
tree.delete(15);
expect(tree.root?.key).toBe(8);
expect(tree.root?.parent).toBe(undefined);
rbTree.delete(15);
expect(rbTree.root?.key).toBe(8);
expect(rbTree.root?.parent).toBe(undefined);
const node15T = tree.getNode(15);
const node15T = rbTree.getNode(15);
expect(node15T).toBe(undefined);
tree.add(23);
tree.add(33);
tree.add(15);
rbTree.add(23);
rbTree.add(33);
rbTree.add(15);
const nodeLM = tree.getLeftMost();
const nodeLM = rbTree.getLeftMost();
expect(nodeLM?.key).toBe(1);
const node50 = tree.getNode(50);
const node50 = rbTree.getNode(50);
expect(node50?.key).toBe(50);
expect(node50?.left?.key).toBe(33);
expect(node50?.right).toBe(tree.SENTINEL);
const node15Fo = tree.getNode(15);
expect(node50?.right).toBe(rbTree.SENTINEL);
const node15Fo = rbTree.getNode(15);
expect(node15Fo?.key).toBe(15);
expect(node15Fo?.left).toBe(tree.SENTINEL);
const node225S = tree.getNode(225);
expect(node225S?.left).toBe(tree.SENTINEL);
expect(node225S?.right).toBe(tree.SENTINEL);
expect(node15Fo?.left).toBe(rbTree.SENTINEL);
const node225S = rbTree.getNode(225);
expect(node225S?.left).toBe(rbTree.SENTINEL);
expect(node225S?.right).toBe(rbTree.SENTINEL);
expect(node225S?.parent?.key).toBe(155);
// TODO
// expect(tree.getNode(0)).toBe(undefined);
tree.add(2);
tree.add(3);
tree.add(4);
tree.add(6);
tree.add(9);
tree.add(11);
tree.add(13);
tree.add(14);
tree.add(16);
tree.add(17);
tree.add(18);
tree.add(19);
tree.add(110);
// expect(rbTree.getNode(0)).toBe(undefined);
rbTree.add(2);
rbTree.add(3);
rbTree.add(4);
rbTree.add(6);
rbTree.add(9);
rbTree.add(11);
rbTree.add(13);
rbTree.add(14);
rbTree.add(16);
rbTree.add(17);
rbTree.add(18);
rbTree.add(19);
rbTree.add(110);
isDebug && tree.print();
isDebug && rbTree.print();
expect(tree.dfs()).toEqual([
expect(rbTree.dfs()).toEqual([
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 25, 28, 33, 50, 110, 111, 155, 225
]);
expect(tree.isBST()).toBe(true);
expect(rbTree.isBST()).toBe(true);
});
it('should fix the tree after insertion and deletion', () => {
it('should fix the rbTree after insertion and deletion', () => {
for (let i = 0; i < 100; i++) {
tree.add(i);
rbTree.add(i);
}
for (let i = 0; i < 49; i++) {
tree.delete(i);
rbTree.delete(i);
}
expect(tree.size).toBe(51);
expect(tree.isBST()).toBe(true);
expect(tree.isBST(tree.root, IterationType.RECURSIVE)).toBe(true);
expect(rbTree.size).toBe(51);
expect(rbTree.isBST()).toBe(true);
expect(rbTree.isBST(rbTree.root, IterationType.RECURSIVE)).toBe(true);
expect(tree.dfs(n => n.key, 'in', tree.root, IterationType.ITERATIVE)).toEqual([
expect(rbTree.dfs(n => n.key, 'in', rbTree.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
]);
expect(tree.dfs(n => n.key, 'in', tree.root, IterationType.RECURSIVE)).toEqual([
expect(rbTree.dfs(n => n.key, 'in', rbTree.root, IterationType.RECURSIVE)).toEqual([
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99
]);
});
it('should fix the tree after large scale insertion and deletion', () => {
it('should fix the rbTree after large scale insertion and deletion', () => {
for (let i = 0; i < 10000; i++) {
tree.add(i);
rbTree.add(i);
}
for (let i = 0; i < 10000; i++) {
tree.delete(i);
rbTree.delete(i);
}
expect(tree.size).toBe(0);
expect(tree.isBST()).toBe(true);
expect(tree.dfs(n => n.key, 'in', tree.root, IterationType.ITERATIVE)).toEqual([]);
expect(rbTree.size).toBe(0);
expect(rbTree.isBST()).toBe(true);
expect(rbTree.dfs(n => n.key, 'in', rbTree.root, IterationType.ITERATIVE)).toEqual([]);
tree.clear();
rbTree.clear();
for (let i = 0; i < 1000; i++) {
tree.add(getRandomInt(-100, 1000));
tree.delete(getRandomInt(-100, 1000));
rbTree.add(getRandomInt(-100, 1000));
rbTree.delete(getRandomInt(-100, 1000));
}
// TODO there is a bug when dfs the tree with SENTINEL node
// expect(tree.isBST()).toBe(true);
// TODO there is a bug when dfs the rbTree with SENTINEL node
// expect(rbTree.isBST()).toBe(true);
});
const { HUNDRED_THOUSAND } = magnitude;
const arr = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND, true);
const competitor = new OrderedMap<number, number>();
it('should fix the tree after large scale insertion and deletion', () => {
tree.clear();
it('should fix the rbTree after large scale insertion and deletion', () => {
rbTree.clear();
const tS = performance.now();
for (let i = 0; i < arr.length; i++) {
tree.add(arr[i]);
rbTree.add(arr[i]);
}
isDebug && console.log(performance.now() - tS);
@ -531,20 +531,20 @@ describe('RedBlackTree 2', () => {
});
it('duplicates', () => {
tree.addMany([9, 8, 7, 8, 8, 8, 2, 3, 6, 5, 5, 4]);
isDebug && tree.print();
rbTree.addMany([9, 8, 7, 8, 8, 8, 2, 3, 6, 5, 5, 4]);
isDebug && rbTree.print();
expect(tree.size).toBe(8);
expect(tree.isBST()).toBe(true);
expect(tree.isAVLBalanced()).toBe(true);
tree.addMany([10, 5, 2, 11]);
expect(tree.size).toBe(10);
expect(tree.isBST()).toBe(true);
expect(tree.isAVLBalanced()).toBe(true);
expect(rbTree.size).toBe(8);
expect(rbTree.isBST()).toBe(true);
expect(rbTree.isAVLBalanced()).toBe(true);
rbTree.addMany([10, 5, 2, 11]);
expect(rbTree.size).toBe(10);
expect(rbTree.isBST()).toBe(true);
expect(rbTree.isAVLBalanced()).toBe(true);
tree.clear();
tree.addMany([10, 20, 30, 40, 50, 60]);
expect(tree.isAVLBalanced()).toBe(false);
rbTree.clear();
rbTree.addMany([10, 20, 30, 40, 50, 60]);
expect(rbTree.isAVLBalanced()).toBe(false);
});
describe('RedBlackTree delete test', function () {
@ -598,7 +598,7 @@ describe('RedBlackTree 2', () => {
if (rbTree.root) dfs(rbTree.root);
expect(rbTree.size).toBe(0);
expect(rbTree.getHeight()).toBe(0);
expect(rbTree.getHeight()).toBe(-1);
expect(nanCount).toBeLessThanOrEqual(inputSize);
isDebug && rbTree.print();
@ -633,7 +633,7 @@ describe('RedBlackTree 2', () => {
expect(mockCallback.mock.calls[2]).toEqual(['c', 3]);
});
test('filter should return a new tree with filtered elements', () => {
test('filter should return a new rbTree with filtered elements', () => {
const filteredTree = rbTree.filter((value, key) => key > 1);
expect(filteredTree.size).toBe(2);
expect([...filteredTree]).toEqual([
@ -642,7 +642,7 @@ describe('RedBlackTree 2', () => {
]);
});
test('map should return a new tree with modified elements', () => {
test('map should return a new rbTree with modified elements', () => {
const mappedTree = rbTree.map((value, key) => (key * 2).toString());
expect(mappedTree.size).toBe(3);
expect([...mappedTree]).toEqual([

View file

@ -14,12 +14,13 @@ const isDebug = isDebugTest;
// const isDebug = true;
describe('TreeMultiMap count', () => {
let tm: TreeMultiMap<number>;
let tmm: TreeMultiMap<number>;
beforeEach(() => {
tm = new TreeMultiMap<number>();
tmm = new TreeMultiMap<number>();
});
it('Should added isolated node count ', () => {
tm.addMany([
it('Should added node count ', () => {
tmm.addMany([
[1, 1],
[2, 2],
[3, 3],
@ -27,35 +28,45 @@ describe('TreeMultiMap count', () => {
[5, 5]
]);
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);
tmm.add(newNode);
expect(tmm.count).toBe(15);
expect(tmm.getMutableCount()).toBe(15);
expect(tmm.getNode(3)?.count).toBe(11);
});
it('Should count', () => {
tm.addMany([
tmm.addMany([
[1, 1],
[2, 2],
[3, 3]
]);
tm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 1);
expect(tm.getMutableCount()).toBe(7);
expect(tm.count).toBe(3);
tmm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 1);
expect(tmm.getMutableCount()).toBe(7);
expect(tmm.count).toBe(3);
});
});
describe('TreeMultiMap operations test1', () => {
it('should height ', () => {
const tmm = new TreeMultiMap();
expect(tmm.getHeight()).toBe(-1);
expect(tmm.getMinHeight()).toBe(-1);
tmm.addMany([1, 6, 7, 2, 3, 4, 9, 11, 8, 5, 10, 12, 16, 14, 13, 15]);
expect(tmm.getHeight()).toBe(5);
expect(tmm.getMinHeight()).toBe(2);
});
it('should size and count', () => {
const treeMultiMap = new TreeMultiMap();
const tmm = new TreeMultiMap();
expect(treeMultiMap instanceof TreeMultiMap);
expect(tmm 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);
tmm.add([11, 11]);
tmm.add([3, 3]);
expect(tmm.count).toBe(2);
expect(tmm.getMutableCount()).toBe(2);
expect(tmm.size).toBe(2);
const keyValuePairs: [number, number][] = [
[11, 11],
@ -76,25 +87,25 @@ describe('TreeMultiMap operations test1', () => {
[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);
tmm.addMany(keyValuePairs);
expect(tmm.size).toBe(16);
expect(tmm.count).toBe(18);
expect(tmm.getMutableCount()).toBe(18);
tmm.delete(11);
expect(tmm.count).toBe(17);
expect(tmm.getMutableCount()).toBe(17);
tmm.delete(3, undefined, true);
expect(tmm.count).toBe(15);
expect(tmm.getMutableCount()).toBe(15);
});
it('should perform various operations on a Binary Search Tree with numeric values1', () => {
const treeMultiMap = new TreeMultiMap();
const tmm = new TreeMultiMap();
expect(treeMultiMap instanceof TreeMultiMap);
expect(tmm instanceof TreeMultiMap);
treeMultiMap.add([11, 11]);
treeMultiMap.add([3, 3]);
tmm.add([11, 11]);
tmm.add([3, 3]);
const idAndValues: [number, number][] = [
[11, 11],
[3, 3],
@ -113,201 +124,200 @@ describe('TreeMultiMap operations test1', () => {
[10, 10],
[5, 5]
];
treeMultiMap.addMany(idAndValues);
expect(treeMultiMap.root instanceof TreeMultiMapNode);
tmm.addMany(idAndValues);
expect(tmm.root instanceof TreeMultiMapNode);
if (treeMultiMap.root) expect(treeMultiMap.root.key == 11);
if (tmm.root) expect(tmm.root.key == 11);
expect(treeMultiMap.size).toBe(16);
expect(treeMultiMap.count).toBe(18);
expect(treeMultiMap.getMutableCount()).toBe(18);
expect(tmm.size).toBe(16);
expect(tmm.count).toBe(18);
expect(tmm.getMutableCount()).toBe(18);
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(tmm.has(6));
isDebug && tmm.print();
expect(tmm.getHeight(6)).toBe(1);
expect(tmm.getDepth(6)).toBe(3);
const nodeId10 = tmm.getNode(10);
expect(nodeId10?.key).toBe(10);
const nodeVal9 = treeMultiMap.getNode(9, node => node.value);
const nodeVal9 = tmm.getNode(9, node => node.value);
expect(nodeVal9?.key).toBe(9);
const nodesByCount1 = treeMultiMap.getNodes(1, node => node.count);
const nodesByCount1 = tmm.getNodes(1, node => node.count);
expect(nodesByCount1.length).toBe(14);
const nodesByCount2 = treeMultiMap.getNodes(2, node => node.count);
const nodesByCount2 = tmm.getNodes(2, node => node.count);
expect(nodesByCount2.length).toBe(2);
const leftMost = treeMultiMap.getLeftMost();
const leftMost = tmm.getLeftMost();
expect(leftMost?.key).toBe(1);
const node15 = treeMultiMap.getNode(15);
const minNodeBySpecificNode = node15 && treeMultiMap.getLeftMost(node15);
const node15 = tmm.getNode(15);
const minNodeBySpecificNode = node15 && tmm.getLeftMost(node15);
expect(minNodeBySpecificNode?.key).toBe(14);
let subTreeSum = 0;
node15 && treeMultiMap.dfs(node => (subTreeSum += node.key), 'pre', 15);
node15 && tmm.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);
tmm.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 = tmm.dfs(node => (node.count += 1), 'pre', 15);
expect(subTreeAdd);
}
const node11 = treeMultiMap.getNode(11);
const node11 = tmm.getNode(11);
expect(node11 instanceof TreeMultiMapNode);
if (node11 instanceof TreeMultiMapNode) {
const allGreaterNodesAdded = treeMultiMap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
const allGreaterNodesAdded = tmm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
expect(allGreaterNodesAdded);
}
const dfsInorderNodes = treeMultiMap.dfs(node => node, 'in');
const dfsInorderNodes = tmm.dfs(node => node, 'in');
expect(dfsInorderNodes[0].key).toBe(1);
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
expect(tmm.isPerfectlyBalanced()).toBe(false);
tmm.perfectlyBalance();
expect(tmm.isPerfectlyBalanced()).toBe(false);
expect(treeMultiMap.isPerfectlyBalanced()).toBe(true);
treeMultiMap.perfectlyBalance();
expect(treeMultiMap.isPerfectlyBalanced()).toBe(false);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.isAVLBalanced()).toBe(false);
const bfsNodesAfterBalanced = treeMultiMap.bfs(node => node);
const bfsNodesAfterBalanced = tmm.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 = tmm.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(false);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight(15)).toBe(1);
expect(tmm.getHeight(15)).toBe(1);
const removed1 = treeMultiMap.delete(1, undefined, true);
const removed1 = tmm.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(false);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight()).toBe(5);
expect(tmm.getHeight()).toBe(5);
const removed4 = treeMultiMap.delete(4, undefined, true);
const removed4 = tmm.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(false);
expect(treeMultiMap.getHeight()).toBe(5);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(5);
const removed10 = treeMultiMap.delete(10, undefined, true);
const removed10 = tmm.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(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight()).toBe(4);
expect(tmm.getHeight()).toBe(4);
const removed15 = treeMultiMap.delete(15, undefined, true);
const removed15 = tmm.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(false);
expect(treeMultiMap.getHeight()).toBe(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed5 = treeMultiMap.delete(5, undefined, true);
const removed5 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(3);
const removed13 = treeMultiMap.delete(13, undefined, true);
const removed13 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(3);
const removed3 = treeMultiMap.delete(3, undefined, true);
const removed3 = tmm.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(false);
expect(treeMultiMap.getHeight()).toBe(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed8 = treeMultiMap.delete(8, undefined, true);
const removed8 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed6 = treeMultiMap.delete(6, undefined, true);
const removed6 = tmm.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(tmm.delete(6, undefined, true).length).toBe(0);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight()).toBe(3);
expect(tmm.getHeight()).toBe(3);
const removed7 = treeMultiMap.delete(7, undefined, true);
const removed7 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed9 = treeMultiMap.delete(9, undefined, true);
const removed9 = tmm.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(2);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(2);
const removed14 = treeMultiMap.delete(14, undefined, true);
const removed14 = tmm.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(1);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(1);
expect(treeMultiMap.isAVLBalanced()).toBe(true);
expect(tmm.isAVLBalanced()).toBe(true);
const bfsIDs = treeMultiMap.bfs(node => node.key);
const bfsIDs = tmm.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 = tmm.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(6);
expect(treeMultiMap.getMutableCount()).toBe(8);
expect(tmm.count).toBe(6);
expect(tmm.getMutableCount()).toBe(8);
});
it('should perform various operations on a Binary Search Tree with object values', () => {
@ -347,11 +357,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 tmm = new TreeMultiMap<number>([], { iterationType: IterationType.RECURSIVE });
expect(treeMultiMap instanceof TreeMultiMap);
treeMultiMap.add([11, 11]);
treeMultiMap.add([3, 3]);
expect(tmm instanceof TreeMultiMap);
tmm.add([11, 11]);
tmm.add([3, 3]);
const idAndValues: [number, number][] = [
[11, 11],
[3, 3],
@ -370,43 +380,43 @@ describe('TreeMultiMap operations test recursively1', () => {
[10, 10],
[5, 5]
];
treeMultiMap.addMany(idAndValues);
expect(treeMultiMap.root).toBeInstanceOf(TreeMultiMapNode);
tmm.addMany(idAndValues);
expect(tmm.root).toBeInstanceOf(TreeMultiMapNode);
if (treeMultiMap.root) expect(treeMultiMap.root.key).toBe(5);
if (tmm.root) expect(tmm.root.key).toBe(5);
expect(treeMultiMap.size).toBe(16);
expect(treeMultiMap.count).toBe(18);
expect(treeMultiMap.getMutableCount()).toBe(18);
expect(tmm.size).toBe(16);
expect(tmm.count).toBe(18);
expect(tmm.getMutableCount()).toBe(18);
expect(treeMultiMap.has(6));
expect(tmm.has(6));
expect(treeMultiMap.getHeight(6)).toBe(1);
expect(treeMultiMap.getDepth(6)).toBe(3);
const nodeId10 = treeMultiMap.getNode(10);
expect(tmm.getHeight(6)).toBe(1);
expect(tmm.getDepth(6)).toBe(3);
const nodeId10 = tmm.getNode(10);
expect(nodeId10?.key).toBe(10);
const nodeVal9 = treeMultiMap.getNode(9, node => node.value);
const nodeVal9 = tmm.getNode(9, node => node.value);
expect(nodeVal9?.key).toBe(9);
const nodesByCount1 = treeMultiMap.getNodes(1, node => node.count);
const nodesByCount1 = tmm.getNodes(1, node => node.count);
expect(nodesByCount1.length).toBe(14);
const nodesByCount2 = treeMultiMap.getNodes(2, node => node.count);
const nodesByCount2 = tmm.getNodes(2, node => node.count);
expect(nodesByCount2.length).toBe(2);
const leftMost = treeMultiMap.getLeftMost();
const leftMost = tmm.getLeftMost();
expect(leftMost?.key).toBe(1);
const node15 = treeMultiMap.getNode(15);
const minNodeBySpecificNode = node15 && treeMultiMap.getLeftMost(node15);
const node15 = tmm.getNode(15);
const minNodeBySpecificNode = node15 && tmm.getLeftMost(node15);
expect(minNodeBySpecificNode?.key).toBe(14);
let subTreeSum = 0;
node15 && treeMultiMap.dfs(node => (subTreeSum += node.key), 'pre', 15);
node15 && tmm.dfs(node => (subTreeSum += node.key), 'pre', 15);
expect(subTreeSum).toBe(45);
let lesserSum = 0;
expect(treeMultiMap.has(9)).toBe(true);
treeMultiMap.lesserOrGreaterTraverse(
expect(tmm.has(9)).toBe(true);
tmm.lesserOrGreaterTraverse(
node => {
lesserSum += node.key;
return node.key;
@ -418,161 +428,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 = tmm.dfs(node => (node.count += 1), 'pre', 15);
expect(subTreeAdd);
}
const node11 = treeMultiMap.getNode(11);
const node11 = tmm.getNode(11);
expect(node11 instanceof TreeMultiMapNode);
if (node11 instanceof TreeMultiMapNode) {
const allGreaterNodesAdded = treeMultiMap.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
const allGreaterNodesAdded = tmm.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11);
expect(allGreaterNodesAdded);
}
const dfsInorderNodes = treeMultiMap.dfs(node => node, 'in');
const dfsInorderNodes = tmm.dfs(node => node, 'in');
expect(dfsInorderNodes[0].key).toBe(1);
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
expect(treeMultiMap.isPerfectlyBalanced()).toBe(true);
expect(tmm.isPerfectlyBalanced()).toBe(false);
treeMultiMap.perfectlyBalance();
tmm.perfectlyBalance();
expect(treeMultiMap.isPerfectlyBalanced()).toBe(false);
expect(treeMultiMap.isAVLBalanced()).toBe(false);
expect(tmm.isPerfectlyBalanced()).toBe(false);
expect(tmm.isAVLBalanced()).toBe(false);
const bfsNodesAfterBalanced = treeMultiMap.bfs(node => node);
const bfsNodesAfterBalanced = tmm.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 = tmm.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(false);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight(15)).toBe(1);
expect(tmm.getHeight(15)).toBe(1);
const removed1 = treeMultiMap.delete(1, undefined, true);
const removed1 = tmm.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(false);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight()).toBe(5);
expect(tmm.getHeight()).toBe(5);
const removed4 = treeMultiMap.delete(4, undefined, true);
const removed4 = tmm.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(false);
expect(treeMultiMap.getHeight()).toBe(5);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(5);
const removed10 = treeMultiMap.delete(10, undefined, true);
const removed10 = tmm.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(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight()).toBe(4);
expect(tmm.getHeight()).toBe(4);
const removed15 = treeMultiMap.delete(15, undefined, true);
const removed15 = tmm.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(false);
expect(treeMultiMap.getHeight()).toBe(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed5 = treeMultiMap.delete(5, undefined, true);
const removed5 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(3);
const removed13 = treeMultiMap.delete(13, undefined, true);
const removed13 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(3);
const removed3 = treeMultiMap.delete(3, undefined, true);
const removed3 = tmm.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(false);
expect(treeMultiMap.getHeight()).toBe(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed8 = treeMultiMap.delete(8, undefined, true);
const removed8 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed6 = treeMultiMap.delete(6, undefined, true);
const removed6 = tmm.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(tmm.delete(6, undefined, true).length).toBe(0);
expect(tmm.isAVLBalanced()).toBe(false);
expect(treeMultiMap.getHeight()).toBe(3);
expect(tmm.getHeight()).toBe(3);
const removed7 = treeMultiMap.delete(7, undefined, true);
const removed7 = tmm.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(3);
expect(tmm.isAVLBalanced()).toBe(false);
expect(tmm.getHeight()).toBe(3);
const removed9 = treeMultiMap.delete(9, undefined, true);
const removed9 = tmm.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(2);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(2);
const removed14 = treeMultiMap.delete(14, undefined, true);
const removed14 = tmm.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(1);
expect(tmm.isAVLBalanced()).toBe(true);
expect(tmm.getHeight()).toBe(1);
expect(treeMultiMap.isAVLBalanced()).toBe(true);
expect(tmm.isAVLBalanced()).toBe(true);
const bfsIDs = treeMultiMap.bfs(node => node.key);
const bfsIDs = tmm.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 = tmm.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(6);
expect(treeMultiMap.getMutableCount()).toBe(8);
expect(tmm.count).toBe(6);
expect(tmm.getMutableCount()).toBe(8);
});
it('should perform various operations on a Binary Search Tree with object values', () => {
@ -611,79 +621,81 @@ describe('TreeMultiMap operations test recursively1', () => {
});
describe('TreeMultiMap delete test', function () {
const treeMS = new TreeMultiMap<number, number>();
const inputSize = 100; // Adjust input sizes as needed
const tmm = new TreeMultiMap<number, number>();
const inputSize = 1000; // Adjust input sizes as needed
beforeEach(() => {
treeMS.clear();
tmm.clear();
});
it(`Observe the time consumption of TreeMultiMap.dfs be good`, function () {
const startDFS = performance.now();
const dfs = treeMS.dfs(node => node);
const dfs = tmm.dfs(node => node);
isDebug && console.log('---bfs', performance.now() - startDFS, dfs.length);
});
it('The structure remains normal after random deletion', function () {
for (let i = 0; i < inputSize; i++) {
treeMS.add(i);
tmm.add(i);
}
expect(tmm.size).toBe(inputSize);
for (let i = 0; i < inputSize; i++) {
const num = getRandomInt(0, inputSize - 1);
treeMS.delete(num);
tmm.delete(num);
}
let nanCount = 0;
let nilCount = 0;
const dfs = (cur: TreeMultiMapNode<number>) => {
if (isNaN(cur.key)) nanCount++;
if (isNaN(cur.key)) nilCount++;
if (cur.left) dfs(cur.left);
if (cur.right) dfs(cur.right);
};
if (treeMS.root) dfs(treeMS.root);
if (tmm.root) dfs(tmm.root);
expect(treeMS.size).toBeLessThanOrEqual(inputSize);
expect(treeMS.getHeight()).toBeGreaterThan(Math.log2(inputSize));
expect(treeMS.getHeight()).toBeLessThan(Math.log2(inputSize) * 2);
expect(tmm.size).toBeLessThanOrEqual(inputSize);
expect(tmm.getHeight()).toBeGreaterThan(Math.log2(inputSize) - 1);
expect(tmm.getHeight()).toBeLessThan(Math.log2(inputSize) * 2);
expect(nanCount).toBeLessThanOrEqual(inputSize);
expect(nilCount).toBe(tmm.size + 1);
});
it(`Random additions, complete deletions of structures are normal`, function () {
for (let i = 0; i < inputSize; i++) {
const num = getRandomInt(0, inputSize - 1);
if (i === 0 && isDebug) console.log(`first:`, num);
treeMS.add(num);
tmm.add(num);
}
for (let i = 0; i < inputSize; i++) {
treeMS.delete(i, undefined, true);
tmm.delete(i, undefined, true);
}
let nanCount = 0;
let nilCount = 0;
const dfs = (cur: TreeMultiMapNode<number>) => {
if (isNaN(cur.key)) nanCount++;
if (isNaN(cur.key)) nilCount++;
if (cur.left) dfs(cur.left);
if (cur.right) dfs(cur.right);
};
if (treeMS.root) dfs(treeMS.root);
if (tmm.root) dfs(tmm.root);
expect(treeMS.size).toBe(0);
expect(treeMS.getHeight()).toBe(0);
expect(nanCount).toBeLessThanOrEqual(inputSize);
expect(tmm.size).toBe(0);
expect(tmm.getHeight()).toBe(-1);
expect(nilCount).toBe(tmm.size + 1);
isDebug && treeMS.print();
isDebug && tmm.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);
tmm.add(num);
}
for (let i = 0; i < inputSize; i++) {
treeMS.delete(i);
tmm.delete(i);
}
let nanCount = 0;
@ -692,49 +704,49 @@ describe('TreeMultiMap delete test', function () {
if (cur.left) dfs(cur.left);
if (cur.right) dfs(cur.right);
};
if (treeMS.root) dfs(treeMS.root);
if (tmm.root) dfs(tmm.root);
expect(treeMS.size).toBeGreaterThanOrEqual(0);
expect(treeMS.getHeight()).toBeGreaterThanOrEqual(0);
expect(tmm.size).toBeGreaterThanOrEqual(0);
expect(tmm.getHeight()).toBeGreaterThanOrEqual(0);
expect(nanCount).toBeLessThanOrEqual(inputSize);
isDebug && treeMS.print();
isDebug && tmm.print();
});
it('should the clone method', () => {
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');
function checkTreeStructure(tmm: TreeMultiMap<string, number>) {
expect(tmm.size).toBe(4);
expect(tmm.root?.key).toBe('2');
expect(tmm.root?.left?.key).toBe('1');
expect(tmm.root?.left?.left?.key).toBe(NaN);
expect(tmm.root?.left?.right?.key).toBe(NaN);
expect(tmm.root?.right?.key).toBe('4');
expect(tmm.root?.right?.left?.key).toBe(NaN);
expect(tmm.root?.right?.right?.key).toBe('5');
}
const treeMultiMap = new TreeMultiMap<string, number>();
treeMultiMap.addMany([
const tmm = new TreeMultiMap<string, number>();
tmm.addMany([
['2', 2],
['4', 4],
['5', 5],
['3', 3],
['1', 1]
]);
expect(treeMultiMap.size).toBe(5);
expect(treeMultiMap.root?.key).toBe('2');
expect(treeMultiMap.root?.left?.key).toBe('1');
expect(treeMultiMap.root?.left?.left?.key).toBe(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();
expect(tmm.size).toBe(5);
expect(tmm.root?.key).toBe('2');
expect(tmm.root?.left?.key).toBe('1');
expect(tmm.root?.left?.left?.key).toBe(NaN);
expect(tmm.root?.left?.right?.key).toBe(NaN);
expect(tmm.root?.right?.key).toBe('4');
expect(tmm.root?.right?.left?.key).toBe(`3`);
expect(tmm.root?.right?.right?.key).toBe('5');
tmm.delete('3');
checkTreeStructure(tmm);
const cloned = tmm.clone();
checkTreeStructure(cloned);
cloned.delete('1');
expect(treeMultiMap.size).toBe(4);
expect(tmm.size).toBe(4);
expect(cloned.size).toBe(3);
});
});