After extracting the count member variable to TreeMultiset, the initial test has passed successfully.

This commit is contained in:
Revone 2023-08-29 17:29:57 +08:00
parent 87b7e420b5
commit 1a893cfd20
15 changed files with 1127 additions and 232 deletions

View file

@ -31,10 +31,9 @@ export abstract class AbstractBinaryTreeNode<T = any, FAMILY extends AbstractBin
* @param {number} [count] - The `count` parameter is an optional parameter that represents the number of times the
* value `val` appears in the binary tree node. If the `count` parameter is not provided, it defaults to 1.
*/
constructor(id: BinaryTreeNodeId, val?: T, count?: number) {
constructor(id: BinaryTreeNodeId, val?: T) {
this._id = id;
this._val = val;
this._count = count ?? 1;
}
private _id: BinaryTreeNodeId;
@ -94,16 +93,6 @@ export abstract class AbstractBinaryTreeNode<T = any, FAMILY extends AbstractBin
this._parent = v;
}
private _count = 1;
get count(): number {
return this._count;
}
set count(v: number) {
this._count = v;
}
private _height = 0;
get height(): number {
@ -227,18 +216,13 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return this._size;
}
private _count = 0;
get count(): number {
return this._count;
}
abstract createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N | null ;
abstract createNode(id: BinaryTreeNodeId, val?: N['val']): N | null ;
swapLocation(srcNode: N, destNode: N): N {
const {val, count, height, id} = destNode;
const tempNode = this.createNode(id, val, count);
const {val, height, id} = destNode;
const tempNode = this.createNode(id, val);
if (tempNode) {
tempNode.height = height;
@ -248,12 +232,10 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
destNode.id = srcNode.id;
destNode.val = srcNode.val;
destNode.count = srcNode.count;
destNode.height = srcNode.height;
srcNode.id = tempNode.id;
srcNode.val = tempNode.val;
srcNode.count = tempNode.count;
srcNode.height = tempNode.height;
}
}
@ -267,7 +249,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
clear() {
this._setRoot(null);
this._setSize(0);
this._setCount(0);
this._setMaxId(-1);
}
@ -308,24 +289,21 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
};
let inserted: N | null | undefined;
const needInsert = val !== null ? this.createNode(id, val, count) : null;
const needInsert = val !== null ? this.createNode(id, val) : null;
const existNode = val !== null ? this.get(id, 'id') : null;
if (this.root) {
if (existNode) {
existNode.count += count;
existNode.val = val ?? id;
if (needInsert !== null) {
this._setCount(this.count + count);
inserted = existNode;
}
} else {
inserted = _bfs(this.root, needInsert);
}
} else {
this._setRoot(val !== null ? this.createNode(id, val, count) : null);
this._setRoot(val !== null ? this.createNode(id, val) : null);
if (needInsert !== null) {
this._setSize(1);
this._setCount(count);
}
inserted = this.root;
}
@ -350,7 +328,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
parent.left = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count ?? 0)
}
return parent.left;
@ -361,7 +338,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
parent.right = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count ?? 0);
}
return parent.right;
} else {
@ -391,7 +367,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
for (const nodeOrId of data) {
if (nodeOrId instanceof AbstractBinaryTreeNode) {
inserted.push(this.add(nodeOrId.id, nodeOrId.val, nodeOrId.count));
inserted.push(this.add(nodeOrId.id, nodeOrId.val));
continue;
}
@ -467,37 +443,33 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
const parent: N | null = curr?.parent ? curr.parent : null;
let needBalanced: N | null = null, orgCurrent = curr;
if (curr.count > 1 && !ignoreCount) {
curr.count--;
this._setCount(this.count - 1);
} else {
if (!curr.left) {
if (!parent) {
if (curr.right !== undefined) this._setRoot(curr.right);
} else {
const {familyPosition: fp} = curr;
if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) {
parent.left = curr.right;
} else if (fp === FamilyPosition.RIGHT || fp === FamilyPosition.ROOT_RIGHT) {
parent.right = curr.right;
}
needBalanced = parent;
}
if (!curr.left) {
if (!parent) {
if (curr.right !== undefined) this._setRoot(curr.right);
} else {
const leftSubTreeRightMost = curr.left ? this.getRightMost(curr.left) : null;
if (leftSubTreeRightMost) {
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
orgCurrent = this.swapLocation(curr, leftSubTreeRightMost);
if (parentOfLeftSubTreeMax) {
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
needBalanced = parentOfLeftSubTreeMax;
}
const {familyPosition: fp} = curr;
if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) {
parent.left = curr.right;
} else if (fp === FamilyPosition.RIGHT || fp === FamilyPosition.ROOT_RIGHT) {
parent.right = curr.right;
}
needBalanced = parent;
}
} else {
const leftSubTreeRightMost = curr.left ? this.getRightMost(curr.left) : null;
if (leftSubTreeRightMost) {
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
orgCurrent = this.swapLocation(curr, leftSubTreeRightMost);
if (parentOfLeftSubTreeMax) {
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
needBalanced = parentOfLeftSubTreeMax;
}
}
this._setSize(this.size - 1);
this._setCount(this.count - orgCurrent.count);
}
this._setSize(this.size - 1);
bstDeletedResult.push({deleted: orgCurrent, needBalanced});
return bstDeletedResult;
@ -543,30 +515,28 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return _getMaxHeight(beginRoot);
} else {
const stack: N[] = [];
let node: N | null | undefined = beginRoot, last: N | null = null;
const depths: Map<N, number> = new Map();
while (stack.length > 0 || node) {
if (node) {
stack.push(node);
node = node.left;
} else {
node = stack[stack.length - 1]
if (!node.right || last === node.right) {
node = stack.pop();
if (node) {
const leftHeight = node.left ? depths.get(node.left) ?? -1 : -1;
const rightHeight = node.right ? depths.get(node.right) ?? -1 : -1;
depths.set(node, 1 + Math.max(leftHeight, rightHeight));
last = node;
node = null;
}
} else node = node.right
}
if (!beginRoot) {
return -1;
}
return depths.get(beginRoot) ?? -1;
const stack: { node: N; depth: number }[] = [{node: beginRoot, depth: 0}];
let maxHeight = 0;
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});
}
maxHeight = Math.max(maxHeight, depth);
}
return maxHeight;
}
}
@ -626,7 +596,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
* of type `N | null`, which means it can either be a `BinaryTreeNode` object or `null`.
* @returns The method is returning a boolean value.
*/
isBalanced(beginRoot?: N | null): boolean {
isPerfectlyBalanced(beginRoot?: N | null): boolean {
return (this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot));
}
@ -648,7 +618,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
const result: N[] = [];
if (this._loopType === LoopType.RECURSIVE) {
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return;
if (!cur.left && !cur.right) return;
@ -682,6 +652,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
* @returns a boolean value.
*/
has(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName): boolean {
propertyName = propertyName ?? 'id';
return this.getNodes(nodeProperty, propertyName).length > 0;
}
@ -843,32 +814,30 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
* @returns The function `getSubTreeSizeAndCount` returns an array `[number, number]`. The first element of the array
* represents the size of the subtree, and the second element represents the count of the nodes in the subtree.
*/
getSubTreeSizeAndCount(subTreeRoot: N | null | undefined) {
const res: [number, number] = [0, 0];
if (!subTreeRoot) return res;
getSubTreeSize(subTreeRoot: N | null | undefined) {
let size = 0;
if (!subTreeRoot) return size;
if (this._loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
res[0]++;
res[1] += cur.count;
size++;
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
}
_traverse(subTreeRoot);
return res;
return size;
} else {
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
res[0]++;
res[1] += cur.count;
size++;
cur.right && stack.push(cur.right);
cur.left && stack.push(cur.left);
}
return res;
return size;
}
}
@ -898,9 +867,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
case 'id':
needSum = cur.id;
break;
case 'count':
needSum = cur.count;
break;
case 'val':
needSum = typeof cur.val === 'number' ? cur.val : 0;
break;
@ -956,10 +922,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
case 'id':
cur.id += delta;
break;
case 'count':
cur.count += delta;
this._setCount(this.count + delta);
break;
default:
cur.id += delta;
break;
@ -996,8 +958,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
BFS(nodeOrPropertyName: 'node'): N[];
BFS(nodeOrPropertyName: 'count'): number[];
/**
* The BFS function performs a breadth-first search on a binary tree and returns the results based on a specified node
* or property name.
@ -1032,8 +992,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): N[];
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[];
/**
* The DFS function performs a depth-first search traversal on a binary tree and returns the results based on the
* specified pattern and node or property name.
@ -1082,8 +1040,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): N[];
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[];
/**
* Time complexity is O(n)
* Space complexity of Iterative DFS equals to recursive DFS which is O(n) because of the stack
@ -1141,8 +1097,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
levelIterative(node: N | null, nodeOrPropertyName?: 'node'): N[];
levelIterative(node: N | null, nodeOrPropertyName?: 'count'): number[];
/**
* The `levelIterative` function performs a level-order traversal on a binary tree and returns the values of the nodes
* in an array, based on a specified property name.
@ -1187,8 +1141,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
listLevels(node: N | null, nodeOrPropertyName?: 'node'): N[][];
listLevels(node: N | null, nodeOrPropertyName?: 'count'): number[][];
/**
* The `listLevels` function collects nodes from a binary tree by a specified property and organizes them into levels.
* @param {N | null} node - The `node` parameter is a BinaryTreeNode object or null. It represents the
@ -1216,16 +1168,13 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
case 'node':
levelsNodes[level].push(node);
break;
case 'count':
levelsNodes[level].push(node.count);
break;
default:
levelsNodes[level].push(node.id);
break;
}
}
if (this._loopType === LoopType.RECURSIVE) {
if (this.loopType === LoopType.RECURSIVE) {
const _recursive = (node: N, level: number) => {
if (!levelsNodes[level]) levelsNodes[level] = [];
collectByProperty(node, level);
@ -1278,8 +1227,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): N[];
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[];
/**
* The `morris` function performs an in-order, pre-order, or post-order traversal on a binary tree using the Morris
* traversal algorithm and returns the results based on the specified property name.
@ -1425,10 +1372,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
this._size = v;
}
protected _setCount(v: number) {
this._count = v;
}
/**
* The function resets the values of several arrays used for tracking visited nodes and their properties.
*/
@ -1436,7 +1379,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
this._visitedId = [];
this._visitedVal = [];
this._visitedNode = [];
this._visitedCount = [];
this._visitedLeftSum = [];
}
@ -1464,12 +1406,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return !!onlyOne;
}
break;
case 'count':
if (cur.count === nodeProperty) {
result.push(cur);
return !!onlyOne;
}
break;
case 'val':
if (cur.val === nodeProperty) {
result.push(cur);
@ -1506,9 +1442,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
case 'node':
this._visitedNode.push(node);
break;
case 'count':
this._visitedCount.push(node.count);
break;
default:
this._visitedId.push(node.id);
break;
@ -1532,8 +1465,6 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return this._visitedVal;
case 'node':
return this._visitedNode;
case 'count':
return this._visitedCount;
default:
return this._visitedId;
}

View file

@ -17,8 +17,8 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
super(options);
}
override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N {
return new AVLTreeNode<N['val'], N>(id, val, count) as N;
override createNode(id: BinaryTreeNodeId, val?: N['val']): N {
return new AVLTreeNode<N['val'], N>(id, val) as N;
}
/**
@ -33,8 +33,8 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
* to `1`, indicating that the value should be inserted once.
* @returns The method is returning either an N object or null.
*/
override add(id: BinaryTreeNodeId, val?: N['val'], count?: number): N | null {
const inserted = super.add(id, val, count);
override add(id: BinaryTreeNodeId, val?: N['val']): N | null | undefined {
const inserted = super.add(id, val);
if (inserted) this.balancePath(inserted);
return inserted;
}

View file

@ -38,8 +38,8 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* of occurrences of the value in the binary tree node. If not provided, the default value is `undefined`.
* @returns a BinaryTreeNode object if the value is not null, otherwise it returns null.
*/
createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N {
return new BinaryTreeNode<N['val'], N>(id, val, count) as N;
createNode(id: BinaryTreeNodeId, val?: N['val']): N {
return new BinaryTreeNode<N['val'], N>(id, val) as N;
}
}

View file

@ -38,8 +38,8 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* of a particular value in the binary search tree node.
* @returns a new instance of the BSTNode class, casted as type N.
*/
override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N {
return new BSTNode<N['val'], N>(id, val, count) as N;
override createNode(id: BinaryTreeNodeId, val?: N['val']): N {
return new BSTNode<N['val'], N>(id, val) as N;
}
/**
@ -54,13 +54,12 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* inserted once.
* @returns The method `add` returns a `N` object or `null`.
*/
override add(id: BinaryTreeNodeId, val?: N['val'], count: number = 1): N | null {
override add(id: BinaryTreeNodeId, val?: N['val']): N | null | undefined {
let inserted: N | null = null;
const newNode = this.createNode(id, val, count);
const newNode = this.createNode(id, val);
if (this.root === null) {
this._setRoot(newNode);
this._setSize(this.size + 1);
this._setCount(this.count + count);
inserted = (this.root);
} else {
let cur = this.root;
@ -69,8 +68,6 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
if (cur !== null && newNode !== null) {
if (this._compare(cur.id, id) === CP.eq) {
if (newNode) {
cur.count += newNode.count;
this._setCount(this.count + newNode.count);
cur.val = newNode.val;
}
//Duplicates are not accepted.
@ -85,7 +82,6 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
//Add to the left of the current node
cur.left = newNode;
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count);
traversing = false;
inserted = cur.left;
} else {
@ -101,7 +97,6 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
//Add to the right of the current node
cur.right = newNode;
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count);
traversing = false;
inserted = (cur.right);
} else {
@ -220,9 +215,6 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
case 'id':
needSum = cur.id;
break;
case 'count':
needSum = cur.count;
break;
default:
needSum = cur.id;
break;
@ -299,23 +291,19 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
case 'id':
cur.id += delta;
break;
case 'count':
cur.count += delta;
break;
default:
cur.id += delta;
break;
}
}
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.id, id);
_sumByPropertyName(cur);
if (compared === CP.gt) _sumByPropertyName(cur);
if (!cur.left && !cur.right) return;
if (cur.left && compared === CP.gt) _traverse(cur.left);
else if (cur.right && compared === CP.gt) _traverse(cur.right);
if (cur.left && this._compare(cur.left.id, id) === CP.gt) _traverse(cur.left);
if (cur.right && this._compare(cur.right.id, id) === CP.gt) _traverse(cur.right);
};
_traverse(this.root);
@ -325,23 +313,34 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
const compared = this._compare(cur.id, node.id);
_sumByPropertyName(cur);
const compared = this._compare(cur.id, id);
if (compared === CP.gt) _sumByPropertyName(cur);
if (cur.left && compared === CP.gt) queue.push(cur.left);
else if (cur.right && compared === CP.gt) queue.push(cur.right);
if (cur.left && this._compare(cur.left.id, id) === CP.gt) queue.push(cur.left);
if (cur.right && this._compare(cur.right.id, id) === CP.gt) queue.push(cur.right);
}
}
return true;
}
}
/**
* Balancing Adjustment:
* Perfectly Balanced Binary Tree: Since the balance of a perfectly balanced binary tree is already fixed, no additional balancing adjustment is needed. Any insertion or deletion operation will disrupt the perfect balance, often requiring a complete reconstruction of the tree.
* AVL Tree: After insertion or deletion operations, an AVL tree performs rotation adjustments based on the balance factor of nodes to restore the tree's balance. These rotations can be left rotations, right rotations, left-right rotations, or right-left rotations, performed as needed.
*
* Use Cases and Efficiency:
* Perfectly Balanced Binary Tree: Perfectly balanced binary trees are typically used in specific scenarios such as complete binary heaps in heap sort or certain types of Huffman trees. However, they are not suitable for dynamic operations requiring frequent insertions and deletions, as these operations often necessitate full tree reconstruction.
* AVL Tree: AVL trees are well-suited for scenarios involving frequent searching, insertion, and deletion operations. Through rotation adjustments, AVL trees maintain their balance, ensuring average and worst-case time complexity of O(log n).
*/
/**
* The `balance` function takes a sorted array of nodes and builds a balanced binary search tree using either a
* recursive or iterative approach.
* @returns The `balance()` function returns a boolean value.
*/
balance(): boolean {
perfectlyBalance(): boolean {
const sorted = this.DFS('in', 'node'), n = sorted.length;
this.clear();
@ -351,7 +350,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
if (l > r) return;
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add(midNode.id, midNode.val, midNode.count);
this.add(midNode.id, midNode.val);
buildBalanceBST(l, m - 1);
buildBalanceBST(m + 1, r);
};
@ -367,7 +366,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
if (l <= r) {
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add(midNode.id, midNode.val, midNode.count);
this.add(midNode.id, midNode.val);
stack.push([m + 1, r]);
stack.push([l, m - 1]);
}

View file

@ -4,8 +4,9 @@ import {BST, BSTNode} from './bst';
export class RBTreeNode<T = any, FAMILY extends RBTreeNode<T, FAMILY> = RBTreeNodeNested<T>> extends BSTNode<T, FAMILY> implements IRBTreeNode<T, FAMILY> {
constructor(id: BinaryTreeNodeId, val?: T, count?: number) {
super(id, val, count);
constructor(id: BinaryTreeNodeId, color: RBColor, val?: T) {
super(id, val);
this._color = color;
}
private _color: RBColor = RBColor.RED;
@ -58,8 +59,8 @@ export class RBTree<N extends RBTreeNode<N['val'], N> = RBTreeNode> extends BST<
super(options);
}
override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N {
return new RBTreeNode(id, val, count) as N;
override createNode(id: BinaryTreeNodeId, val?: N['val']): N {
return new RBTreeNode(id, RBColor.RED, val) as N;
}
// private override _root: BinaryTreeNode<N> | null = null;

View file

@ -6,10 +6,26 @@
* @license MIT License
*/
import type {BinaryTreeNodeId, TreeMultisetNodeNested, TreeMultisetOptions} from '../types';
import {BinaryTreeDeletedResult, CP, DFSOrderPattern, FamilyPosition, LoopType, NodeOrPropertyName} from '../types';
import {ITreeMultiset, ITreeMultisetNode} from '../interfaces';
import {AVLTree, AVLTreeNode} from './avl-tree';
import {ObjectWithNumberId} from '../../utils';
export class TreeMultisetNode<T = any, FAMILY extends TreeMultisetNode<T, FAMILY> = TreeMultisetNodeNested<T>> extends AVLTreeNode<T, FAMILY> implements ITreeMultisetNode<T, FAMILY> {
constructor(id: BinaryTreeNodeId, val?: T, count: number = 1) {
super(id, val);
this._count = count;
}
private _count = 1;
get count(): number {
return this._count;
}
set count(v: number) {
this._count = v;
}
}
/**
@ -20,6 +36,12 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
super({...options, isMergeDuplicatedVal: true});
}
private _count = 0;
get count(): number {
return this._count;
}
/**
* The function creates a new BSTNode with the given id, value, and count.
* @param {BinaryTreeNodeId} id - The id parameter is the unique identifier for the binary tree node. It is used to
@ -32,4 +54,586 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N {
return new TreeMultisetNode(id, val, count) as N;
}
override swapLocation(srcNode: N, destNode: N): N {
const {val, count, height, id} = destNode;
const tempNode = this.createNode(id, val, count);
if (tempNode) {
tempNode.height = height;
if (tempNode instanceof TreeMultisetNode) {
// TODO should we consider the left, right children?
destNode.id = srcNode.id;
destNode.val = srcNode.val;
destNode.count = srcNode.count;
destNode.height = srcNode.height;
srcNode.id = tempNode.id;
srcNode.val = tempNode.val;
srcNode.count = tempNode.count;
srcNode.height = tempNode.height;
}
}
return destNode;
}
/**
* The `add` function inserts a new node with a given ID and value into a binary tree, updating the count if the node
* already exists.
* @param {BinaryTreeNodeId} id - The id parameter is the identifier of the binary tree node. It is used to uniquely
* identify each node in the binary tree.
* @param {N} val - The value to be inserted into the binary tree.
* @param {number} [count] - The `count` parameter is an optional parameter that specifies the number of times the
* value should be inserted into the binary tree. If not provided, it defaults to 1.
* @returns The function `add` returns a `N` object if a new node is inserted, or `null` if no new node
* is inserted, or `undefined` if the insertion fails.
*/
// override add(id: BinaryTreeNodeId, val?: N['val'], count?: number): N | null | undefined {
// count = count ?? 1;
//
// const _bfs = (root: N, newNode: N | null): N | undefined | null => {
// const queue: Array<N | null> = [root];
// while (queue.length > 0) {
// const cur = queue.shift();
// if (cur) {
// const inserted = this.addTo(newNode, cur);
// if (inserted !== undefined) return inserted;
// if (cur.left) queue.push(cur.left);
// if (cur.right) queue.push(cur.right);
// } else return;
// }
// return;
// };
//
// let inserted: N | null | undefined;
// const needInsert = val !== null ? this.createNode(id, val, count) : null;
// const existNode = val !== null ? this.get(id, 'id') : null;
// if (this.root) {
// if (existNode) {
// existNode.count += count;
// existNode.val = val ?? id;
// if (needInsert !== null) {
// this._setCount(this.count + count);
// inserted = existNode;
// }
// } else {
// inserted = _bfs(this.root, needInsert);
// }
// } else {
// this._setRoot(val !== null ? this.createNode(id, val, count) : null);
// if (needInsert !== null) {
// this._setSize(1);
// this._setCount(count);
// }
// inserted = this.root;
// }
// return inserted;
// }
override add(id: BinaryTreeNodeId, val?: N['val'], count?: number): N | null | undefined {
count = count ?? 1;
let inserted: N | null = null;
const newNode = this.createNode(id, val, count);
if (this.root === null) {
this._setRoot(newNode);
this._setSize(this.size + 1);
this._setCount(this.count + count);
inserted = (this.root);
} else {
let cur = this.root;
let traversing = true;
while (traversing) {
if (cur !== null && newNode !== null) {
if (this._compare(cur.id, id) === CP.eq) {
if (newNode) {
cur.val = newNode.val;
cur.count += count;
this._setCount(this.count + newNode.count);
}
//Duplicates are not accepted.
traversing = false;
inserted = cur;
} else if (this._compare(cur.id, id) === CP.gt) {
// Traverse left of the node
if (cur.left === undefined) {
if (newNode) {
newNode.parent = cur;
}
//Add to the left of the current node
cur.left = newNode;
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count);
traversing = false;
inserted = cur.left;
} else {
//Traverse the left of the current node
if (cur.left) cur = cur.left;
}
} else if (this._compare(cur.id, id) === CP.lt) {
// Traverse right of the node
if (cur.right === undefined) {
if (newNode) {
newNode.parent = cur;
}
//Add to the right of the current node
cur.right = newNode;
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count);
traversing = false;
inserted = (cur.right);
} else {
//Traverse the left of the current node
if (cur.right) cur = cur.right;
}
}
} else {
traversing = false;
}
}
}
if (inserted) this.balancePath(inserted);
return inserted;
}
/**
* The function adds a new node to a binary tree as the left or right child of a given parent node.
* @param {N | null} newNode - The `newNode` parameter represents the node that you want to add to the tree. It can be
* either a node object (`N`) or `null`.
* @param {N} parent - The `parent` parameter represents the parent node to which the new node will be added as a
* child.
* @returns either the left or right child node that was added to the parent node. It can also return `null` or
* `undefined` in certain cases.
*/
override addTo(newNode: N | null, parent: N): N | null | undefined {
if (parent) {
if (parent.left === undefined) {
if (newNode) {
newNode.parent = parent;
}
parent.left = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count ?? 0)
}
return parent.left;
} else if (parent.right === undefined) {
if (newNode) {
newNode.parent = parent;
}
parent.right = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._setCount(this.count + newNode.count ?? 0);
}
return parent.right;
} else {
return;
}
} else {
return;
}
}
/**
* The `addMany` function inserts multiple items into a binary tree and returns an array of the inserted nodes or
* null/undefined values.
* @param {N[] | N[]} data - The `data` parameter can be either an array of elements of type `N` or an
* array of `N` objects.
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
override addMany(data: N[] | Array<N['val']>): (N | null | undefined)[] {
// TODO not sure addMany not be run multi times
const inserted: (N | null | undefined)[] = [];
const map: Map<N | N['val'], number> = new Map();
if (this.isMergeDuplicatedVal) {
for (const nodeOrId of data) map.set(nodeOrId, (map.get(nodeOrId) ?? 0) + 1);
}
for (const nodeOrId of data) {
if (nodeOrId instanceof TreeMultisetNode) {
inserted.push(this.add(nodeOrId.id, nodeOrId.val, nodeOrId.count));
continue;
}
if (nodeOrId === null) {
inserted.push(this.add(NaN, null, 0));
continue;
}
// TODO will this cause an issue?
const count = this.isMergeDuplicatedVal ? map.get(nodeOrId) : 1;
let newId: BinaryTreeNodeId;
if (typeof nodeOrId === 'number') {
newId = this.autoIncrementId ? this.maxId + 1 : nodeOrId;
} else if (nodeOrId instanceof Object) {
if (this.autoIncrementId) {
newId = this.maxId + 1;
} else {
if (Object.keys(nodeOrId).includes('id')) {
newId = (nodeOrId as ObjectWithNumberId).id;
} else {
console.warn(nodeOrId, 'Object value must has an id property when the autoIncrementId is false');
continue;
}
}
} else {
console.warn(nodeOrId, ` is not added`);
continue;
}
if (this.isMergeDuplicatedVal) {
if (map.has(nodeOrId)) {
inserted.push(this.add(newId, nodeOrId, count));
map.delete(nodeOrId);
}
} else {
inserted.push(this.add(newId, nodeOrId, 1));
}
this._setMaxId(newId);
}
return inserted;
}
/**
* The `remove` function removes a node from a binary search tree and returns the deleted node along with the parent
* node that needs to be balanced.
* @param {N | BinaryTreeNodeId | null} nodeOrId - The `nodeOrId` parameter can be one of the following:
* @param {boolean} [ignoreCount] - The `ignoreCount` parameter is an optional boolean parameter that determines
* whether to ignore the count of the node being removed. If `ignoreCount` is set to `true`, the count of the node will
* not be taken into account when removing it. If `ignoreCount` is set to `false
* @returns The function `remove` returns an array of `BinaryTreeDeletedResult<N>` objects.
*/
override remove(nodeOrId: N | BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeletedResult<N>[] {
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
if (!this.root) return bstDeletedResult;
const curr: N | null = (typeof nodeOrId === 'number') ? this.get(nodeOrId) : nodeOrId;
if (!curr) return bstDeletedResult;
const parent: N | null = curr?.parent ? curr.parent : null;
let needBalanced: N | null = null, orgCurrent = curr;
if (curr.count > 1 && !ignoreCount) {
curr.count--;
this._setCount(this.count - 1);
} else {
if (!curr.left) {
if (!parent) {
if (curr.right !== undefined) this._setRoot(curr.right);
} else {
const {familyPosition: fp} = curr;
if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) {
parent.left = curr.right;
} else if (fp === FamilyPosition.RIGHT || fp === FamilyPosition.ROOT_RIGHT) {
parent.right = curr.right;
}
needBalanced = parent;
}
} else {
const leftSubTreeRightMost = curr.left ? this.getRightMost(curr.left) : null;
if (leftSubTreeRightMost) {
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
orgCurrent = this.swapLocation(curr, leftSubTreeRightMost);
if (parentOfLeftSubTreeMax) {
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
needBalanced = parentOfLeftSubTreeMax;
}
}
}
this._setSize(this.size - 1);
this._setCount(this.count - orgCurrent.count);
}
bstDeletedResult.push({deleted: orgCurrent, needBalanced});
if (needBalanced) {
this.balancePath(needBalanced);
}
return bstDeletedResult;
}
/**
* The function calculates the size and count of a subtree in a binary tree using either recursive or iterative
* traversal.
* @param {N | null | undefined} subTreeRoot - The `subTreeRoot` parameter is the root node of a binary
* tree.
* @returns The function `getSubTreeSizeAndCount` returns an array `[number, number]`. The first element of the array
* represents the size of the subtree, and the second element represents the count of the nodes in the subtree.
*/
getSubTreeCount(subTreeRoot: N | null | undefined) {
const res: [number, number] = [0, 0];
if (!subTreeRoot) return res;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
res[0]++;
res[1] += cur.count;
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
}
_traverse(subTreeRoot);
return res;
} else {
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
res[0]++;
res[1] += cur.count;
cur.right && stack.push(cur.right);
cur.left && stack.push(cur.left);
}
return res;
}
}
subTreeSumCount(subTreeRoot: N | BinaryTreeNodeId | null): number {
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'id');
if (!subTreeRoot) return 0;
let sum = 0;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N): void => {
sum += cur.count;
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
}
_traverse(subTreeRoot);
} else {
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
sum += cur.count;
cur.right && stack.push(cur.right);
cur.left && stack.push(cur.left);
}
}
return sum;
}
/**
* The function `subTreeAddCount` recursively or iteratively traverses a binary tree and adds a given delta value to
* the `count` property of each node.
* @param {N | BinaryTreeNodeId | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree
* in a binary tree. It can be either a `BinaryTreeNodeId` (a unique identifier for a node in the binary tree), a
* `BinaryTreeNode` object, or `null` if the subtree is empty.
* @param {number} delta - The delta parameter is a number that represents the amount by which the count of each node
* in the subtree should be increased or decreased.
* @returns a boolean value.
*/
subTreeAddCount(subTreeRoot: N | BinaryTreeNodeId | null, delta: number): boolean {
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'id');
if (!subTreeRoot) return false;
const _addByProperty = (cur: N) => {
cur.count += delta;
this._setCount(this.count + delta);
}
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
_addByProperty(cur);
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
};
_traverse(subTreeRoot);
} else {
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
_addByProperty(cur);
cur.right && stack.push(cur.right);
cur.left && stack.push(cur.left);
}
}
return true;
}
getNodesByCount(nodeProperty: BinaryTreeNodeId | N, onlyOne ?: boolean): N[] {
if (!this.root) return [];
const result: N[] = [];
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
if (cur.count === nodeProperty) {
result.push(cur);
if (onlyOne) return;
}
if (!cur.left && !cur.right) return;
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
}
_traverse(this.root);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
if (cur.count === nodeProperty) {
result.push(cur);
if (onlyOne) return result;
}
cur.left && queue.push(cur.left);
cur.right && queue.push(cur.right);
}
}
}
return result;
}
BFSCount(): number[] {
const nodes = super.BFS('node');
return nodes.map(node => node.count);
}
listLevelsCount(node: N | null): number[][] {
const levels = super.listLevels(node, 'node');
return levels.map(level => level.map(node => node.count));
}
morrisCount(pattern?: 'in' | 'pre' | 'post'): number[] {
pattern = pattern || 'in';
const nodes = super.morris(pattern, 'node');
return nodes.map(node => node.count);
}
DFSIterativeCount(pattern ?: 'in' | 'pre' | 'post', nodeOrPropertyName ?: NodeOrPropertyName): number[] {
pattern = pattern ?? 'in';
const nodes = super.DFSIterative(pattern, 'node');
return nodes.map(node => node.count);
}
DFSCount(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[] {
pattern = pattern ?? 'in';
const nodes = super.DFS(pattern, 'node');
return nodes.map(node => node.count);
}
lesserSumCount(beginNode: N | BinaryTreeNodeId | null): number {
if (typeof beginNode === 'number') beginNode = this.get(beginNode, 'id');
if (!beginNode) return 0;
if (!this.root) return 0;
const id = beginNode.id;
let sum = 0;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N): void => {
const compared = this._compare(cur.id, id);
if (compared === CP.eq) {
if (cur.right) sum += this.subTreeSumCount(cur.right);
return;
} else if (compared === CP.lt) {
if (cur.left) sum += this.subTreeSumCount(cur.left);
sum += cur.count;
if (cur.right) _traverse(cur.right);
else return;
} else {
if (cur.left) _traverse(cur.left);
else return;
}
};
_traverse(this.root);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
const compared = this._compare(cur.id, id);
if (compared === CP.eq) {
if (cur.right) sum += this.subTreeSumCount(cur.right);
return sum;
} else if (compared === CP.lt) { // todo maybe a bug
if (cur.left) sum += this.subTreeSumCount(cur.left);
sum += cur.count;
if (cur.right) queue.push(cur.right);
else return sum;
} else {
if (cur.left) queue.push(cur.left);
else return sum;
}
}
}
}
return sum;
}
allGreaterNodesAddCount(node: N | BinaryTreeNodeId | null, delta: number): boolean {
if (typeof node === 'number') node = this.get(node, 'id');
if (!node) return false;
const id = node.id;
if (!this.root) return false;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.id, id);
if (compared === CP.gt) cur.count += delta;
if (!cur.left && !cur.right) return;
if (cur.left && this._compare(cur.left.id, id) === CP.gt) _traverse(cur.left);
if (cur.right && this._compare(cur.right.id, id) === CP.gt) _traverse(cur.right);
};
_traverse(this.root);
return true;
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
const compared = this._compare(cur.id, id);
if (compared === CP.gt) cur.count += delta
if (cur.left && this._compare(cur.left.id, id) === CP.gt) queue.push(cur.left);
if (cur.right && this._compare(cur.right.id, id) === CP.gt) queue.push(cur.right);
}
}
return true;
}
}
override clear() {
this._setRoot(null);
this._setSize(0);
this._setCount(0);
this._setMaxId(-1);
}
protected _setCount(v: number) {
this._count = v;
}
}

View file

@ -35,10 +35,6 @@ export interface IAbstractBinaryTreeNode<T, FAMILY extends IAbstractBinaryTreeNo
get familyPosition(): FamilyPosition
get count(): number
set count(v: number)
get height(): number
set height(v: number)
@ -69,8 +65,6 @@ export interface IAbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val'],
get size(): number
get count(): number
swapLocation(srcNode: N, destNode: N): N
clear(): void
@ -93,7 +87,7 @@ export interface IAbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val'],
getMinHeight(beginRoot?: N | null): number
isBalanced(beginRoot?: N | null): boolean
isPerfectlyBalanced(beginRoot?: N | null): boolean
getNodes(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean): N[]
@ -119,7 +113,7 @@ export interface IAbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val'],
isBST(node?: N | null): boolean
getSubTreeSizeAndCount(subTreeRoot: N | null | undefined): [number, number]
getSubTreeSize(subTreeRoot: N | null | undefined): number
// --- start additional methods ---

View file

@ -8,7 +8,7 @@ export interface IAVLTreeNode<T, FAMILY extends IAVLTreeNode<T, FAMILY>> extends
export interface IAVLTree<N extends AVLTreeNode<N['val'], N>> extends IBST<N> {
add(id: BinaryTreeNodeId, val?: N['val'] | null, count?: number): N | null
add(id: BinaryTreeNodeId, val?: N['val'] | null): N | null | undefined
remove(id: BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): BinaryTreeDeletedResult<N>[]

View file

@ -8,7 +8,7 @@ export interface IBSTNode<T, FAMILY extends IBSTNode<T, FAMILY>> extends IBinary
export interface IBST<N extends BSTNode<N['val'], N>> extends IBinaryTree<N> {
createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N
add(id: BinaryTreeNodeId, val?: N['val'] | null, count?: number): N | null
add(id: BinaryTreeNodeId, val?: N['val'] | null, count?: number): N | null | undefined
get(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName): N | null
@ -24,7 +24,7 @@ export interface IBST<N extends BSTNode<N['val'], N>> extends IBinaryTree<N> {
allGreaterNodesAdd(node: N, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean
balance(): boolean
perfectlyBalance(): boolean
isAVLBalanced(): boolean

View file

@ -19,11 +19,12 @@ export enum FamilyPosition {
MAL_NODE = 'MAL_NODE'
}
export type BinaryTreeNodePropertyName = 'id' | 'val' | 'count';
export type BinaryTreeNodePropertyName = 'id' | 'val';
export type NodeOrPropertyName = 'node' | BinaryTreeNodePropertyName;
export type DFSOrderPattern = 'in' | 'pre' | 'post';
export type BinaryTreeNodeId = number;
export type BinaryTreeDeletedResult<N> = { deleted: N | null | undefined, needBalanced: N | null };
export type AVLTreeDeletedResult<N> = { deleted: N | null | undefined };
export type AbstractBinaryTreeNodeProperty<N extends AbstractBinaryTreeNode<N['val'], N>> =
N['val']

View file

@ -16,6 +16,10 @@ export type ObjectWithNumberId = {
id: number;
}
export type RestrictValById = NonNumberNonObjectButDefined | ObjectWithoutId | ObjectWithNonNumberId | ObjectWithNumberId;
export type RestrictValById =
NonNumberNonObjectButDefined
| ObjectWithoutId
| ObjectWithNonNumberId
| ObjectWithNumberId;
export type DummyAny = string | number | boolean | null | undefined | object | symbol | void | Function | never;

View file

@ -1,4 +1,3 @@
import {z} from 'zod'
/**
* data-structure-typed
*
@ -6,16 +5,7 @@ import {z} from 'zod'
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type {
NonNumberNonObjectButDefined,
ObjectWithNonNumberId,
ObjectWithNumberId,
ObjectWithoutId,
Thunk,
ToThunkFn,
TrlAsyncFn,
TrlFn
} from './types';
import type {Thunk, ToThunkFn, TrlAsyncFn, TrlFn} from './types';
export const uuidV4 = function () {
return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c) {

View file

@ -16,8 +16,6 @@ describe('AVL Tree Test', () => {
const getNodeById = tree.get(10, 'id');
expect(getNodeById?.id).toBe(10);
const getNodesByCount = tree.getNodes(1, 'count');
expect(getNodesByCount.length).toBe(16);
const getMinNodeByRoot = tree.getLeftMost();
expect(getMinNodeByRoot?.id).toBe(1);
@ -32,26 +30,20 @@ describe('AVL Tree Test', () => {
const lesserSum = tree.lesserSum(10);
expect(lesserSum).toBe(45);
if (node15) {
const subTreeAdd = tree.subTreeAdd(node15, 1, 'count');
expect(subTreeAdd).toBe(true);
}
// node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
expect(node15?.val).toBe(15);
const node11 = tree.get(11);
if (node11) {
const allGreaterNodesAdd = tree.allGreaterNodesAdd(node11, 2, 'count');
expect(allGreaterNodesAdd).toBe(true);
}
const dfs = tree.DFS('in', 'node');
expect(dfs[0].id).toBe(1);
expect(dfs[dfs.length - 1].id).toBe(16);
tree.balance();
tree.perfectlyBalance();
const bfs = tree.BFS('node');
expect(tree.isBalanced()).toBe(true);
expect(tree.isPerfectlyBalanced()).toBe(true);
expect(bfs[0].id).toBe(8);
expect(bfs[bfs.length - 1].id).toBe(16);

View file

@ -12,7 +12,6 @@ describe('BST operations test', () => {
if (bst.root) expect(bst.root.id).toBe(11);
expect(bst.size).toBe(16);
expect(bst.count).toBe(16);
expect(bst.has(6)).toBe(true);
@ -26,8 +25,6 @@ describe('BST operations test', () => {
const nodeVal9 = bst.get(9, 'val');
expect(nodeVal9?.id).toBe(9);
const nodesByCount1 = bst.getNodes(1, 'count');
expect(nodesByCount1.length).toBe(16);
const leftMost = bst.getLeftMost();
expect(leftMost?.id).toBe(1);
@ -43,24 +40,16 @@ describe('BST operations test', () => {
expect(lesserSum).toBe(45);
expect(node15).toBeInstanceOf(BSTNode);
if (node15 instanceof BSTNode) {
const subTreeAdd = bst.subTreeAdd(15, 1, 'count');
expect(subTreeAdd).toBeDefined();
}
const node11 = bst.get(11);
expect(node11).toBeInstanceOf(BSTNode);
if (node11 instanceof BSTNode) {
const allGreaterNodesAdded = bst.allGreaterNodesAdd(11, 2, 'count');
expect(allGreaterNodesAdded).toBeDefined();
}
const dfsInorderNodes = bst.DFS('in', 'node');
expect(dfsInorderNodes[0].id).toBe(1);
expect(dfsInorderNodes[dfsInorderNodes.length - 1].id).toBe(16);
bst.balance();
expect(bst.isBalanced()).toBe(true);
bst.perfectlyBalance();
expect(bst.isPerfectlyBalanced()).toBe(true);
const bfsNodesAfterBalanced = bst.BFS('node');
expect(bfsNodesAfterBalanced[0].id).toBe(8);
@ -191,7 +180,6 @@ describe('BST operations test', () => {
expect(bfsNodes[1].id).toBe(12);
expect(bfsNodes[2].id).toBe(16);
expect(bst.count).toBe(5);
});
it('should perform various operations on a Binary Search Tree with object values', () => {
@ -211,8 +199,6 @@ describe('BST operations test', () => {
if (objBST.root) expect(objBST.root.id).toBe(11);
expect(objBST.count).toBe(16);
expect(objBST.has(6)).toBe(true);
const node6 = objBST.get(6);
@ -225,9 +211,6 @@ describe('BST operations test', () => {
const nodeVal9 = objBST.get(9, 'id');
expect(nodeVal9?.id).toBe(9);
const nodesByCount1 = objBST.getNodes(1, 'count');
expect(nodesByCount1.length).toBe(16);
const leftMost = objBST.getLeftMost();
expect(leftMost?.id).toBe(1);
@ -243,24 +226,16 @@ describe('BST operations test', () => {
expect(lesserSum).toBe(45);
expect(node15).toBeInstanceOf(BSTNode);
if (node15 instanceof BSTNode) {
const subTreeAdd = objBST.subTreeAdd(node15, 1, 'count');
expect(subTreeAdd).toBeDefined();
}
const node11 = objBST.get(11);
expect(node11).toBeInstanceOf(BSTNode);
if (node11 instanceof BSTNode) {
const allGreaterNodesAdded = objBST.allGreaterNodesAdd(node11, 2, 'count');
expect(allGreaterNodesAdded).toBeDefined();
}
const dfsInorderNodes = objBST.DFS('in', 'node');
expect(dfsInorderNodes[0].id).toBe(1);
expect(dfsInorderNodes[dfsInorderNodes.length - 1].id).toBe(16);
objBST.balance();
expect(objBST.isBalanced()).toBe(true);
objBST.perfectlyBalance();
expect(objBST.isPerfectlyBalanced()).toBe(true);
const bfsNodesAfterBalanced = objBST.BFS('node');
expect(bfsNodesAfterBalanced[0].id).toBe(8);
@ -391,6 +366,5 @@ describe('BST operations test', () => {
expect(bfsNodes[1].id).toBe(12);
expect(bfsNodes[2].id).toBe(16);
expect(objBST.count).toBe(5);
});
});

View file

@ -0,0 +1,405 @@
import {TreeMultiset, TreeMultisetNode} from '../../../../src';
describe('TreeMultiset operations test', () => {
it('should perform various operations on a Binary Search Tree with numeric values', () => {
const treeMultiset = new TreeMultiset();
expect(treeMultiset instanceof TreeMultiset);
treeMultiset.add(11);
treeMultiset.add(3);
treeMultiset.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
expect(treeMultiset.root instanceof TreeMultisetNode);
if (treeMultiset.root) expect(treeMultiset.root.id == 11);
expect(treeMultiset.size === 16);
expect(treeMultiset.count === 18);
expect(treeMultiset.BFS('id'))
expect(treeMultiset.has(6));
expect(treeMultiset.getHeight(6) === 3);
expect(treeMultiset.getDepth(6) === 1);
const nodeId10 = treeMultiset.get(10);
expect(nodeId10?.id === 10);
const nodeVal9 = treeMultiset.get(9, 'val');
expect(nodeVal9?.id === 9);
const nodesByCount1 = treeMultiset.getNodesByCount(1);
expect(nodesByCount1.length === 14);
const nodesByCount2 = treeMultiset.getNodesByCount(2);
expect(nodesByCount2.length === 2);
const leftMost = treeMultiset.getLeftMost();
expect(leftMost?.id === 1);
const node15 = treeMultiset.get(15);
const minNodeBySpecificNode = node15 && treeMultiset.getLeftMost(node15);
expect(minNodeBySpecificNode?.id === 12);
const subTreeSum = node15 && treeMultiset.subTreeSum(15);
expect(subTreeSum === 70);
const lesserSum = treeMultiset.lesserSum(10);
expect(lesserSum === 45);
expect(node15 instanceof TreeMultisetNode);
if (node15 instanceof TreeMultisetNode) {
const subTreeAdd = treeMultiset.subTreeAddCount(15, 1);
expect(subTreeAdd);
}
const node11 = treeMultiset.get(11);
expect(node11 instanceof TreeMultisetNode);
if (node11 instanceof TreeMultisetNode) {
const allGreaterNodesAdded = treeMultiset.allGreaterNodesAddCount(11, 2);
expect(allGreaterNodesAdded);
}
const dfsInorderNodes = treeMultiset.DFS('in', 'node');
expect(dfsInorderNodes[0].id === 1);
expect(dfsInorderNodes[dfsInorderNodes.length - 1].id === 16);
expect(treeMultiset.isPerfectlyBalanced() === false);
treeMultiset.perfectlyBalance();
expect(treeMultiset.isPerfectlyBalanced() === true);
expect(treeMultiset.isAVLBalanced() === true);
const bfsNodesAfterBalanced = treeMultiset.BFS('node');
expect(bfsNodesAfterBalanced[0].id === 8);
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].id === 16);
const removed11 = treeMultiset.remove(11, true);
expect(removed11 instanceof Array);
expect(removed11[0]);
expect(removed11[0].deleted);
if (removed11[0].deleted) expect(removed11[0].deleted.id === 11);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight(15) === 1);
const removed1 = treeMultiset.remove(1, true);
expect(removed1 instanceof Array);
expect(removed1[0]);
expect(removed1[0].deleted);
if (removed1[0].deleted) expect(removed1[0].deleted.id === 1);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 4);
const removed4 = treeMultiset.remove(4, true);
expect(removed4 instanceof Array);
expect(removed4[0]);
expect(removed4[0].deleted);
if (removed4[0].deleted) expect(removed4[0].deleted.id === 4);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 4);
const removed10 = treeMultiset.remove(10, true);
expect(removed10 instanceof Array);
expect(removed10[0]);
expect(removed10[0].deleted);
if (removed10[0].deleted) expect(removed10[0].deleted.id === 10);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 3);
const removed15 = treeMultiset.remove(15, true);
expect(removed15 instanceof Array);
expect(removed15[0]);
expect(removed15[0].deleted);
if (removed15[0].deleted) expect(removed15[0].deleted.id === 15);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 3);
const removed5 = treeMultiset.remove(5, true);
expect(removed5 instanceof Array);
expect(removed5[0]);
expect(removed5[0].deleted);
if (removed5[0].deleted) expect(removed5[0].deleted.id === 5);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 3);
const removed13 = treeMultiset.remove(13, true);
expect(removed13 instanceof Array);
expect(removed13[0]);
expect(removed13[0].deleted);
if (removed13[0].deleted) expect(removed13[0].deleted.id === 13);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 3);
const removed3 = treeMultiset.remove(3, true);
expect(removed3 instanceof Array);
expect(removed3[0]);
expect(removed3[0].deleted);
if (removed3[0].deleted) expect(removed3[0].deleted.id === 3);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 3);
const removed8 = treeMultiset.remove(8, true);
expect(removed8 instanceof Array);
expect(removed8[0]);
expect(removed8[0].deleted);
if (removed8[0].deleted) expect(removed8[0].deleted.id === 8);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 3);
const removed6 = treeMultiset.remove(6, true);
expect(removed6 instanceof Array);
expect(removed6[0]);
expect(removed6[0].deleted);
if (removed6[0].deleted) expect(removed6[0].deleted.id === 6);
expect(treeMultiset.remove(6, true).length === 0);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 2);
const removed7 = treeMultiset.remove(7, true);
expect(removed7 instanceof Array);
expect(removed7[0]);
expect(removed7[0].deleted);
if (removed7[0].deleted) expect(removed7[0].deleted.id === 7);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 2);
const removed9 = treeMultiset.remove(9, true);
expect(removed9 instanceof Array);
expect(removed9[0]);
expect(removed9[0].deleted);
if (removed9[0].deleted) expect(removed9[0].deleted.id === 9);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 2);
const removed14 = treeMultiset.remove(14, true);
expect(removed14 instanceof Array);
expect(removed14[0]);
expect(removed14[0].deleted);
if (removed14[0].deleted) expect(removed14[0].deleted.id === 14);
expect(treeMultiset.isAVLBalanced() === true);
expect(treeMultiset.getHeight() === 1);
expect(treeMultiset.isAVLBalanced() === true);
const bfsIDs = treeMultiset.BFS();
expect(bfsIDs[0] === 12);
expect(bfsIDs[1] === 2);
expect(bfsIDs[2] === 16);
const bfsNodes = treeMultiset.BFS('node');
expect(bfsNodes[0].id === 12);
expect(bfsNodes[1].id === 2);
expect(bfsNodes[2].id === 16);
expect(treeMultiset.count === 3);
});
it('should perform various operations on a Binary Search Tree with object values', () => {
const objTreeMultiset = new TreeMultiset<TreeMultisetNode<{ id: number, keyA: number }>>();
expect(objTreeMultiset).toBeInstanceOf(TreeMultiset);
objTreeMultiset.add(11, {id: 11, keyA: 11});
objTreeMultiset.add(3, {id: 3, keyA: 3});
const values = [{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8},
{id: 13, keyA: 13}, {id: 16, keyA: 16}, {id: 2, keyA: 2},
{id: 6, keyA: 6}, {id: 9, keyA: 9}, {id: 12, keyA: 12},
{id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7},
{id: 10, keyA: 10}, {id: 5, keyA: 5}];
objTreeMultiset.addMany(values);
expect(objTreeMultiset.root).toBeInstanceOf(TreeMultisetNode);
if (objTreeMultiset.root) expect(objTreeMultiset.root.id).toBe(11);
expect(objTreeMultiset.count).toBe(16);
expect(objTreeMultiset.has(6)).toBe(true);
const node6 = objTreeMultiset.get(6);
// expect(node6 && objTreeMultiset.getHeight(node6)).toBe(2);
// expect(node6 && objTreeMultiset.getDepth(node6)).toBe(3);
//
// const nodeId10 = objTreeMultiset.get(10, 'id');
// expect(nodeId10?.id).toBe(10);
//
// const nodeVal9 = objTreeMultiset.get(9, 'id');
// expect(nodeVal9?.id).toBe(9);
//
// const nodesByCount1 = objTreeMultiset.getNodesByCount(1);
// expect(nodesByCount1.length).toBe(16);
//
// const leftMost = objTreeMultiset.getLeftMost();
// expect(leftMost?.id).toBe(1);
//
// const node15 = objTreeMultiset.get(15);
// expect(node15?.val).toEqual({id: 15, keyA: 15});
// const minNodeBySpecificNode = node15 && objTreeMultiset.getLeftMost(node15);
// expect(minNodeBySpecificNode?.id).toBe(12);
//
// const subTreeSum = node15 && objTreeMultiset.subTreeSum(node15);
// expect(subTreeSum).toBe(70);
//
// const lesserSum = objTreeMultiset.lesserSum(10);
// expect(lesserSum).toBe(45);
//
// expect(node15).toBeInstanceOf(TreeMultisetNode);
// if (node15 instanceof TreeMultisetNode) {
// const subTreeAdd = objTreeMultiset.subTreeAddCount(node15, 1);
// expect(subTreeAdd).toBeDefined();
// }
//
// const node11 = objTreeMultiset.get(11);
// expect(node11).toBeInstanceOf(TreeMultisetNode);
// if (node11 instanceof TreeMultisetNode) {
// const allGreaterNodesAdded = objTreeMultiset.allGreaterNodesAddCount(node11, 2);
// expect(allGreaterNodesAdded).toBeDefined();
// }
//
// const dfsInorderNodes = objTreeMultiset.DFS('in', 'node');
// expect(dfsInorderNodes[0].id).toBe(1);
// expect(dfsInorderNodes[dfsInorderNodes.length - 1].id).toBe(16);
//
// objTreeMultiset.perfectlyBalance();
// expect(objTreeMultiset.isPerfectlyBalanced()).toBe(true);
//
// const bfsNodesAfterBalanced = objTreeMultiset.BFS('node');
// expect(bfsNodesAfterBalanced[0].id).toBe(8);
// expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].id).toBe(16);
//
// const removed11 = objTreeMultiset.remove(11, true);
// expect(removed11).toBeInstanceOf(Array);
// expect(removed11[0]).toBeDefined();
// expect(removed11[0].deleted).toBeDefined();
//
// if (removed11[0].deleted) expect(removed11[0].deleted.id).toBe(11);
//
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
//
// expect(node15 && objTreeMultiset.getHeight(node15)).toBe(2);
//
// const removed1 = objTreeMultiset.remove(1, true);
// expect(removed1).toBeInstanceOf(Array);
// expect(removed1[0]).toBeDefined();
// expect(removed1[0].deleted).toBeDefined();
// if (removed1[0].deleted) expect(removed1[0].deleted.id).toBe(1);
//
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
//
// expect(objTreeMultiset.getHeight()).toBe(4);
//
// const removed4 = objTreeMultiset.remove(4, true);
// expect(removed4).toBeInstanceOf(Array);
// expect(removed4[0]).toBeDefined();
// expect(removed4[0].deleted).toBeDefined();
// if (removed4[0].deleted) expect(removed4[0].deleted.id).toBe(4);
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
// expect(objTreeMultiset.getHeight()).toBe(4);
//
// const removed10 = objTreeMultiset.remove(10, true);
// expect(removed10).toBeInstanceOf(Array);
// expect(removed10[0]).toBeDefined();
// expect(removed10[0].deleted).toBeDefined();
// if (removed10[0].deleted) expect(removed10[0].deleted.id).toBe(10);
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
// expect(objTreeMultiset.getHeight()).toBe(4);
//
// const removed15 = objTreeMultiset.remove(15, true);
// expect(removed15).toBeInstanceOf(Array);
// expect(removed15[0]).toBeDefined();
// expect(removed15[0].deleted).toBeDefined();
// if (removed15[0].deleted) expect(removed15[0].deleted.id).toBe(15);
//
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed5 = objTreeMultiset.remove(5, true);
// expect(removed5).toBeInstanceOf(Array);
// expect(removed5[0]).toBeDefined();
// expect(removed5[0].deleted).toBeDefined();
// if (removed5[0].deleted) expect(removed5[0].deleted.id).toBe(5);
//
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed13 = objTreeMultiset.remove(13, true);
// expect(removed13).toBeInstanceOf(Array);
// expect(removed13[0]).toBeDefined();
// expect(removed13[0].deleted).toBeDefined();
// if (removed13[0].deleted) expect(removed13[0].deleted.id).toBe(13);
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed3 = objTreeMultiset.remove(3, true);
// expect(removed3).toBeInstanceOf(Array);
// expect(removed3[0]).toBeDefined();
// expect(removed3[0].deleted).toBeDefined();
// if (removed3[0].deleted) expect(removed3[0].deleted.id).toBe(3);
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed8 = objTreeMultiset.remove(8, true);
// expect(removed8).toBeInstanceOf(Array);
// expect(removed8[0]).toBeDefined();
// expect(removed8[0].deleted).toBeDefined();
// if (removed8[0].deleted) expect(removed8[0].deleted.id).toBe(8);
// expect(objTreeMultiset.isAVLBalanced()).toBe(true);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed6 = objTreeMultiset.remove(6, true);
// expect(removed6).toBeInstanceOf(Array);
// expect(removed6[0]).toBeDefined();
// expect(removed6[0].deleted).toBeDefined();
// if (removed6[0].deleted) expect(removed6[0].deleted.id).toBe(6);
// expect(objTreeMultiset.remove(6, true).length).toBe(0);
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed7 = objTreeMultiset.remove(7, true);
// expect(removed7).toBeInstanceOf(Array);
// expect(removed7[0]).toBeDefined();
// expect(removed7[0].deleted).toBeDefined();
// if (removed7[0].deleted) expect(removed7[0].deleted.id).toBe(7);
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed9 = objTreeMultiset.remove(9, true);
// expect(removed9).toBeInstanceOf(Array);
// expect(removed9[0]).toBeDefined();
// expect(removed9[0].deleted).toBeDefined();
// if (removed9[0].deleted) expect(removed9[0].deleted.id).toBe(9);
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
// expect(objTreeMultiset.getHeight()).toBe(3);
//
// const removed14 = objTreeMultiset.remove(14, true);
// expect(removed14).toBeInstanceOf(Array);
// expect(removed14[0]).toBeDefined();
// expect(removed14[0].deleted).toBeDefined();
// if (removed14[0].deleted) expect(removed14[0].deleted.id).toBe(14);
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
// expect(objTreeMultiset.getHeight()).toBe(2);
//
//
// expect(objTreeMultiset.isAVLBalanced()).toBe(false);
//
// const bfsIDs = objTreeMultiset.BFS();
// expect(bfsIDs[0]).toBe(2);
// expect(bfsIDs[1]).toBe(12);
// expect(bfsIDs[2]).toBe(16);
//
// const bfsNodes = objTreeMultiset.BFS('node');
// expect(bfsNodes[0].id).toBe(2);
// expect(bfsNodes[1].id).toBe(12);
// expect(bfsNodes[2].id).toBe(16);
//
// expect(objTreeMultiset.count).toBe(5);
});
});