[binary-tree, graph] Replace all code that uses Arrays as makeshift Queues with actual Queues to improve efficiency. [binary-tree] Remove most methods for calculating sums and modifying properties, adopting a generic callback-based design. [bst] Enable default batch addition in BST using sorted binary insertion to maintain tree balance as much as possible.

This commit is contained in:
Revone 2023-10-21 23:56:14 +08:00
parent 284e85e76b
commit 3a86a5c37d
10 changed files with 185 additions and 422 deletions

View file

@ -23,6 +23,7 @@ import {
} from '../../types';
import {IBinaryTree} from '../../interfaces';
import {trampoline} from '../../utils';
import {Queue} from '../queue';
export class BinaryTreeNode<V = any, FAMILY extends BinaryTreeNode<V, FAMILY> = BinaryTreeNodeNested<V>> {
/**
@ -210,8 +211,8 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
*/
add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined {
const _bfs = (root: N, newNode: N | null): N | undefined | null => {
const queue: Array<N | null> = [root];
while (queue.length > 0) {
const queue = new Queue<N | null>([root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (newNode && cur.key === newNode.key) return;
@ -509,8 +510,8 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
_traverse(this.root);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const queue = new Queue<N>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return result;
@ -762,65 +763,11 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
}
}
/**
* The function `subTreeSum` calculates the sum of a specified property in a binary tree or subtree.
* @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a binary
* tree or the ID of a binary tree node. It can also be `null` if there is no subtree.
* @param {BinaryTreeNodePropertyName} [propertyName] - propertyName is an optional parameter that specifies the
* property of the binary tree node to use for calculating the sum. It can be either 'key' or 'val'. If propertyName is
* not provided, it defaults to 'key'.
* @returns a number, which is the sum of the values of the specified property in the subtree rooted at `subTreeRoot`.
*/
subTreeSum(subTreeRoot: N | BinaryTreeNodeKey | null, propertyName: BinaryTreeNodePropertyName = 'key'): number {
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'key');
if (!subTreeRoot) return 0;
let sum = 0;
const _sumByProperty = (cur: N) => {
let needSum: number;
switch (propertyName) {
case 'key':
needSum = cur.key;
break;
case 'val':
needSum = typeof cur.val === 'number' ? cur.val : 0;
break;
default:
needSum = cur.key;
break;
}
return needSum;
};
if (this._loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N): void => {
sum += _sumByProperty(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()!;
sum += _sumByProperty(cur);
cur.right && stack.push(cur.right);
cur.left && stack.push(cur.left);
}
}
return sum;
}
/**
* The function `subTreeForeach` adds a delta value to a specified property of each node in a subtree.
* @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a binary
* tree or the ID of a node in the binary tree. It can also be `null` if there is no subtree to add to.
* @param callBack - The `callBack` parameter is a function that takes a node as a parameter and returns a number.
* @param callback - The `callback` parameter is a function that takes a node as a parameter and returns a value.
* specifies the property of the binary tree node that should be modified. If not provided, it defaults to 'key'.
* @returns a boolean value.
*/
@ -887,10 +834,9 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
*/
bfs(nodeOrPropertyName: NodeOrPropertyName = 'key'): BinaryTreeNodeProperties<N> {
this._clearResults();
const queue: Array<N | null | undefined> = [this.root];
const queue = new Queue<N | null | undefined>([this.root]);
while (queue.length !== 0) {
// TODO Array.shift is not efficient, consider using Deque
while (queue.size !== 0) {
const cur = queue.shift();
if (cur) {
this._accumulatedByPropertyName(cur, nodeOrPropertyName);

View file

@ -15,6 +15,7 @@ import type {
import {CP, LoopType} from '../../types';
import {BinaryTree, BinaryTreeNode} from './binary-tree';
import {IBinaryTree} from '../../interfaces';
import {Queue} from '../queue';
export class BSTNode<V = any, FAMILY extends BSTNode<V, FAMILY> = BSTNodeNested<V>> extends BinaryTreeNode<V, FAMILY> {
constructor(key: BinaryTreeNodeKey, val?: V) {
@ -59,7 +60,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* @returns The function `add` returns the inserted node (`inserted`) which can be of type `N`, `null`, or `undefined`.
*/
override add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined {
// TODO support node as a param
// TODO support node as a parameter
let inserted: N | null = null;
let newNode: N | null = null;
if (keyOrNode instanceof BSTNode) {
@ -137,8 +138,9 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
override addMany(
keysOrNodes: (BinaryTreeNodeKey | null)[] | (N | null)[],
data?: N['val'][],
isBalanceAdd = false
isBalanceAdd = true
): (N | null | undefined)[] {
// TODO this addMany function is inefficient, it should be optimized
function hasNoNull(arr: (BinaryTreeNodeKey | null)[] | (N | null)[]): arr is BinaryTreeNodeKey[] | N[] {
return arr.indexOf(null) === -1;
}
@ -265,8 +267,8 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
_traverse(this.root);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const queue = new Queue<N>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return result;
@ -285,133 +287,47 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
// --- start additional functions
/**
* The `lesserSum` function calculates the sum of property values in a binary tree for nodes that have a property value
* less than a given node.
* @param {N | BinaryTreeNodeKey | null} beginNode - The `beginNode` parameter can be one of the following:
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property name to use for calculating the sum. If not provided, it defaults to `'key'`.
* @returns The function `lesserSum` returns a number, which represents the sum of the values of the nodes in the
* binary tree that have a lesser value than the specified `beginNode` based on the `propertyName`.
*/
lesserSum(beginNode: N | BinaryTreeNodeKey | null, propertyName: BinaryTreeNodePropertyName = 'key'): number {
if (typeof beginNode === 'number') beginNode = this.get(beginNode, 'key');
if (!beginNode) return 0;
if (!this.root) return 0;
const key = beginNode.key;
const getSumByPropertyName = (cur: N) => {
let needSum: number;
switch (propertyName) {
case 'key':
needSum = cur.key;
break;
default:
needSum = cur.key;
break;
}
return needSum;
};
let sum = 0;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N): void => {
const compared = this._compare(cur.key, key);
if (compared === CP.eq) {
if (cur.right) sum += this.subTreeSum(cur.right, propertyName);
return;
} else if (compared === CP.lt) {
if (cur.left) sum += this.subTreeSum(cur.left, propertyName);
sum += getSumByPropertyName(cur);
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.key, key);
if (compared === CP.eq) {
if (cur.right) sum += this.subTreeSum(cur.right, propertyName);
return sum;
} else if (compared === CP.lt) {
// todo maybe a bug
if (cur.left) sum += this.subTreeSum(cur.left, propertyName);
sum += getSumByPropertyName(cur);
if (cur.right) queue.push(cur.right);
else return sum;
} else {
if (cur.left) queue.push(cur.left);
else return sum;
}
}
}
}
return sum;
}
/**
* The `allGreaterNodesAdd` function adds a delta value to the specified property of all nodes in a binary tree that
* The `lesserOrGreaterForeach` function adds a delta value to the specified property of all nodes in a binary tree that
* have a greater value than a given node.
* @param {N | BinaryTreeNodeKey | null} node - The `node` parameter can be either of type `N` (a generic type),
* `BinaryTreeNodeKey`, or `null`. It represents the node in the binary tree to which the delta value will be added.
* @param {number} delta - The `delta` parameter is a number that represents the amount by which the property value of
* each greater node should be increased.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property name of the nodes in the binary tree that you want to update. If not provided, it defaults to
* 'key'.
* @returns a boolean value.
* @param {N | BinaryTreeNodeKey | null} node - The `node` parameter can be either of type `N` (a generic type), `BinaryTreeNodeKey`, or `null`. It
* represents the node in the binary tree to which the delta value will be added.
* @param lesserOrGreater - The `lesserOrGreater` parameter is an optional parameter that specifies whether the delta
* @param callback - The `callback` parameter is a function that takes a node as a parameter and returns a boolean
*/
allGreaterNodesAdd(
lesserOrGreaterForeach(
node: N | BinaryTreeNodeKey | null,
delta: number,
propertyName: BinaryTreeNodePropertyName = 'key'
lesserOrGreater: CP = CP.lt,
callback: (node: N) => void
): boolean {
if (typeof node === 'number') node = this.get(node, 'key');
if (!node) return false;
const key = node.key;
if (!this.root) return false;
const _sumByPropertyName = (cur: N) => {
switch (propertyName) {
case 'key':
cur.key += delta;
break;
default:
cur.key += delta;
break;
}
};
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.key, key);
if (compared === CP.gt) _sumByPropertyName(cur);
if (compared === lesserOrGreater) callback(cur);
if (!cur.left && !cur.right) return;
if (cur.left && this._compare(cur.left.key, key) === CP.gt) _traverse(cur.left);
if (cur.right && this._compare(cur.right.key, key) === CP.gt) _traverse(cur.right);
if (cur.left && this._compare(cur.left.key, key) === lesserOrGreater) _traverse(cur.left);
if (cur.right && this._compare(cur.right.key, key) === lesserOrGreater) _traverse(cur.right);
};
_traverse(this.root);
return true;
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const queue = new Queue<N>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
const compared = this._compare(cur.key, key);
if (compared === CP.gt) _sumByPropertyName(cur);
if (compared === lesserOrGreater) callback(cur);
if (cur.left && this._compare(cur.left.key, key) === CP.gt) queue.push(cur.left);
if (cur.right && this._compare(cur.right.key, key) === CP.gt) queue.push(cur.right);
if (cur.left && this._compare(cur.left.key, key) === lesserOrGreater) queue.push(cur.left);
if (cur.right && this._compare(cur.right.key, key) === lesserOrGreater) queue.push(cur.right);
}
}
return true;

View file

@ -9,6 +9,7 @@ import type {BinaryTreeNodeKey, TreeMultisetNodeNested, TreeMultisetOptions} fro
import {BinaryTreeDeletedResult, CP, DFSOrderPattern, FamilyPosition, LoopType} from '../../types';
import {IBinaryTree} from '../../interfaces';
import {AVLTree, AVLTreeNode} from './avl-tree';
import {Queue} from '../queue';
export class TreeMultisetNode<
V = any,
@ -349,121 +350,6 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
return bstDeletedResult;
}
/**
* The function `getSubTreeCount` calculates the number of nodes and the sum of their counts in a subtree, using either
* recursive or iterative traversal.
* @param {N | null | undefined} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree in a
* binary tree.
* @returns The function `getSubTreeCount` returns an array `[number, number]`.
*/
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;
}
}
/**
* The function `subTreeSumCount` calculates the sum of the `count` property of each node in a subtree, either
* recursively or iteratively.
* @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree
* in a binary tree. It can be either a `BinaryTreeNodeKey` (a unique identifier for a node in the binary tree) or
* `null` if the subtree is empty.
* @returns the sum of the count values of all nodes in the subtree rooted at `subTreeRoot`.
*/
subTreeSumCount(subTreeRoot: N | BinaryTreeNodeKey | null): number {
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'key');
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 | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree
* in a binary tree. It can be either a `BinaryTreeNodeKey` (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 | BinaryTreeNodeKey | null, delta: number): boolean {
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'key');
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;
}
/**
* The function `getNodesByCount` returns an array of nodes that have a specific count property, either recursively or
* using a queue.
@ -492,8 +378,8 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
_traverse(this.root);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const queue = new Queue<N>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (cur.count === nodeProperty) {
@ -545,120 +431,6 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
return nodes.map(node => node.count);
}
/**
* The function dfsCountIterative performs an iterative depth-first search and returns an array of node counts based on
* the specified traversal pattern.
* @param {'in' | 'pre' | 'post'} [pattern] - The pattern parameter is a string that specifies the traversal order for
* the Depth-First Search (dfs) algorithm. It can have three possible values: 'in', 'pre', or 'post'.
* @param loopType - The loopType parameter is a string that specifies the type of loop to use when traversing the
* @returns The dfsCountIterative function returns an array of numbers, which represents the count property of each node
* in the dfs traversal.
*/
dfsCount(pattern: DFSOrderPattern = 'in', loopType: LoopType = LoopType.ITERATIVE): number[] {
const nodes = super.dfs(pattern, 'node', loopType);
return nodes.map(node => node.count);
}
/**
* The `lesserSumCount` function calculates the sum of the counts of all nodes in a binary tree that have a lesser
* value than a given node.
* @param {N | BinaryTreeNodeKey | null} beginNode - The `beginNode` parameter can be one of the following:
* @returns the sum of the counts of nodes in the binary tree that have a lesser value than the given beginNode.
*/
lesserSumCount(beginNode: N | BinaryTreeNodeKey | null): number {
if (typeof beginNode === 'number') beginNode = this.get(beginNode, 'key');
if (!beginNode) return 0;
if (!this.root) return 0;
const key = beginNode.key;
let sum = 0;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N): void => {
const compared = this._compare(cur.key, key);
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.key, key);
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;
}
/**
* The function `allGreaterNodesAddCount` updates the count property of all nodes in a binary tree that have an ID
* greater than a given ID by a specified delta value.
* @param {N | BinaryTreeNodeKey | null} node - The `node` parameter can be one of the following:
* @param {number} delta - The `delta` parameter is a number that represents the amount by which the `count` property
* of each node should be increased.
* @returns a boolean value.
*/
allGreaterNodesAddCount(node: N | BinaryTreeNodeKey | null, delta: number): boolean {
if (typeof node === 'number') node = this.get(node, 'key');
if (!node) return false;
const key = node.key;
if (!this.root) return false;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.key, key);
if (compared === CP.gt) cur.count += delta;
if (!cur.left && !cur.right) return;
if (cur.left && this._compare(cur.left.key, key) === CP.gt) _traverse(cur.left);
if (cur.right && this._compare(cur.right.key, key) === 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.key, key);
if (compared === CP.gt) cur.count += delta;
if (cur.left && this._compare(cur.left.key, key) === CP.gt) queue.push(cur.left);
if (cur.right && this._compare(cur.right.key, key) === CP.gt) queue.push(cur.right);
}
}
return true;
}
}
/**
* The clear() function clears the data and sets the count to 0.
*/

View file

@ -9,6 +9,7 @@ import {arrayRemove, uuidV4} from '../../utils';
import {PriorityQueue} from '../priority-queue';
import type {DijkstraResult, VertexKey} from '../../types';
import {IGraph} from '../../interfaces';
import {Queue} from '../queue';
export abstract class AbstractVertex<V = any> {
/**
@ -342,11 +343,11 @@ export abstract class AbstractGraph<
}
const visited: Map<V, boolean> = new Map();
const queue: V[] = [vertex1];
const queue = new Queue<V>([vertex1]);
visited.set(vertex1, true);
let cost = 0;
while (queue.length > 0) {
for (let i = 0; i < queue.length; i++) {
while (queue.size > 0) {
for (let i = 0; i < queue.size; i++) {
const cur = queue.shift();
if (cur === vertex2) {
return cost;

View file

@ -1,4 +1,4 @@
import {AVLTree} from '../../../../src';
import {AVLTree, CP} from '../../../../src';
describe('AVL Tree Test', () => {
it('should perform various operations on a AVL Tree', () => {
@ -22,10 +22,12 @@ describe('AVL Tree Test', () => {
const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node15);
expect(getMinNodeBySpecificNode?.key).toBe(12);
const subTreeSum = node15 && tree.subTreeSum(node15);
let subTreeSum = 0;
node15 && tree.subTreeForeach(node15, node => (subTreeSum += node.key));
expect(subTreeSum).toBe(70);
const lesserSum = tree.lesserSum(10);
let lesserSum = 0;
tree.lesserOrGreaterForeach(10, CP.lt, node => (lesserSum += node.key));
expect(lesserSum).toBe(45);
// 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.

View file

@ -1,4 +1,6 @@
import {BST, BSTNode} from '../../../../src';
import {BST, BSTNode, CP} from '../../../../src';
const isDebug = false;
describe('BST operations test', () => {
it('should perform various operations on a Binary Search Tree with numeric values', () => {
@ -7,7 +9,7 @@ describe('BST operations test', () => {
bst.add(11, 11);
bst.add(3, 3);
const idsAndValues = [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
bst.addMany(idsAndValues, idsAndValues);
bst.addMany(idsAndValues, idsAndValues, false);
expect(bst.root).toBeInstanceOf(BSTNode);
if (bst.root) expect(bst.root.key).toBe(11);
@ -33,10 +35,12 @@ describe('BST operations test', () => {
const minNodeBySpecificNode = node15 && bst.getLeftMost(node15);
expect(minNodeBySpecificNode?.key).toBe(12);
const subTreeSum = node15 && bst.subTreeSum(15);
let subTreeSum = 0;
node15 && bst.subTreeForeach(15, node => (subTreeSum += node.key));
expect(subTreeSum).toBe(70);
const lesserSum = bst.lesserSum(10);
let lesserSum = 0;
bst.lesserOrGreaterForeach(10, CP.lt, node => (lesserSum += node.key));
expect(lesserSum).toBe(45);
expect(node15).toBeInstanceOf(BSTNode);
@ -204,7 +208,8 @@ describe('BST operations test', () => {
objBST.addMany(
values.map(item => item.key),
values
values,
false
);
expect(objBST.root).toBeInstanceOf(BSTNode);
@ -231,10 +236,12 @@ describe('BST operations test', () => {
const minNodeBySpecificNode = node15 && objBST.getLeftMost(node15);
expect(minNodeBySpecificNode?.key).toBe(12);
const subTreeSum = node15 && objBST.subTreeSum(node15);
let subTreeSum = 0;
node15 && objBST.subTreeForeach(node15, node => (subTreeSum += node.key));
expect(subTreeSum).toBe(70);
const lesserSum = objBST.lesserSum(10);
let lesserSum = 0;
objBST.lesserOrGreaterForeach(10, CP.lt, node => (lesserSum += node.key));
expect(lesserSum).toBe(45);
expect(node15).toBeInstanceOf(BSTNode);
@ -378,3 +385,33 @@ describe('BST operations test', () => {
expect(bfsNodes[2].key).toBe(16);
});
});
describe('BST Performance test', function () {
const bst = new BST<BSTNode<number>>();
const inputSize = 10000; // Adjust input sizes as needed
beforeEach(() => {
bst.clear();
});
it(`Observe the time consumption of BST.dfs be good`, function () {
const startDFS = performance.now();
const dfs = bst.dfs();
isDebug && console.log('---bfs', performance.now() - startDFS, dfs.length);
});
it('Should the time consumption of lesserOrGreaterForeach fitting O(n log n)', function () {
const nodes: number[] = [];
for (let i = 0; i < inputSize; i++) {
nodes.push(i);
}
const start = performance.now();
bst.addMany(nodes);
isDebug && console.log('---add', performance.now() - start);
const startL = performance.now();
bst.lesserOrGreaterForeach(inputSize / 2, CP.lt, node => {
return node.key - 1;
});
isDebug && console.log('---lesserOrGreaterForeach', performance.now() - startL);
});
});

View file

@ -5,7 +5,7 @@ describe('Overall BinaryTree Test', () => {
const bst = new BST();
bst.add(11);
bst.add(3);
bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5], undefined, false);
bst.size === 16; // true
expect(bst.size).toBe(16); // true
bst.has(6); // true

View file

@ -1,5 +1,6 @@
import {TreeMultiset, TreeMultisetNode} from '../../../../src';
import {CP, TreeMultiset, TreeMultisetNode} from '../../../../src';
const isDebug = false;
describe('TreeMultiset operations test', () => {
it('should perform various operations on a Binary Search Tree with numeric values', () => {
const treeMultiset = new TreeMultiset();
@ -39,20 +40,26 @@ describe('TreeMultiset operations test', () => {
const minNodeBySpecificNode = node15 && treeMultiset.getLeftMost(node15);
expect(minNodeBySpecificNode?.key).toBe(12);
const subTreeSum = node15 && treeMultiset.subTreeSum(15);
let subTreeSum = 0;
node15 && treeMultiset.subTreeForeach(15, (node: TreeMultisetNode<number>) => (subTreeSum += node.key));
expect(subTreeSum).toBe(70);
const lesserSum = treeMultiset.lesserSum(10);
let lesserSum = 0;
treeMultiset.lesserOrGreaterForeach(10, CP.lt, (node: TreeMultisetNode<number>) => (lesserSum += node.key));
expect(lesserSum).toBe(45);
expect(node15 instanceof TreeMultisetNode);
if (node15 instanceof TreeMultisetNode) {
const subTreeAdd = treeMultiset.subTreeAddCount(15, 1);
const subTreeAdd = treeMultiset.subTreeForeach(15, (node: TreeMultisetNode<number>) => (node.count += 1));
expect(subTreeAdd);
}
const node11 = treeMultiset.get(11);
expect(node11 instanceof TreeMultisetNode);
if (node11 instanceof TreeMultisetNode) {
const allGreaterNodesAdded = treeMultiset.allGreaterNodesAddCount(11, 2);
const allGreaterNodesAdded = treeMultiset.lesserOrGreaterForeach(
11,
CP.gt,
(node: TreeMultisetNode<number>) => (node.count += 2)
);
expect(allGreaterNodesAdded);
}
@ -419,13 +426,19 @@ describe('TreeMultiset operations test', () => {
describe('TreeMultiset Performance test', function () {
// const treeMS = new TreeMultiset<TreeMultisetNode<number>>();
// const inputSizes = [100]; // Adjust input sizes as needed
// const inputSize = [100]; // Adjust input sizes as needed
//
// // Define a function to calculate the expected O(n log n) time
// function expectedTime(n: number): number {
// return n * Math.log(n);
// }
const treeMS = new TreeMultiset<TreeMultisetNode<number>>();
const inputSize = 100000; // Adjust input sizes as needed
beforeEach(() => {
treeMS.clear();
});
it(`Observe the time consumption of TreeMultiset.add fitting O(n log n)`, function () {
// // Create a benchmark suite
// const suite = new Benchmark.Suite();
@ -437,9 +450,9 @@ describe('TreeMultiset Performance test', function () {
// }
// return arr;
// }
// const inputArray = generateRandomArray(inputSizes[0]);
// const inputArray = generateRandomArray(inputSize[0]);
//
// suite.add(`TreeMultiset addMany (n=${inputSizes[0]})`, () => {
// suite.add(`TreeMultiset addMany (n=${inputSize[0]})`, () => {
// treeMS.addMany([...inputArray]);
// });
//
@ -453,9 +466,26 @@ describe('TreeMultiset Performance test', function () {
// console.log(`Input size (n): ${n}, Observed time: ${observedTime.toFixed(2)}ms, Expected time: ${expected.toFixed(2)}ms`);
// })
// .on('complete', () => {
// console.log(`Benchmark (n=${inputSizes[0]}) completed.`);
// console.log(`Benchmark (n=${inputSize[0]}) completed.`);
// done(); // Call done to indicate the test is complete
// })
// .run({async: true});
});
it(`Observe the time consumption of TreeMultiset.dfs be good`, function () {
const startDFS = performance.now();
const dfs = treeMS.dfs();
isDebug && console.log('---bfs', performance.now() - startDFS, dfs.length);
});
it('Should the time consumption of lesserOrGreaterForeach fitting O(n log n)', function () {
const start = performance.now();
for (let i = 0; i < inputSize; i++) {
treeMS.add(i);
}
isDebug && console.log('---add', performance.now() - start);
const startL = performance.now();
treeMS.lesserOrGreaterForeach(inputSize / 2, CP.lt, (node: TreeMultisetNode<number>) => (node.count += 1));
isDebug && console.log('---lesserOrGreaterForeach', performance.now() - startL);
});
});

View file

@ -1,4 +1,5 @@
import {Deque, ArrayDeque, ObjectDeque} from '../../../../src';
import {bigO} from '../../../utils';
describe('Deque Tests', () => {
// Test cases for the Deque class (DoublyLinkedList-based)
@ -128,3 +129,19 @@ describe('Deque Tests', () => {
// Add more test cases as needed
});
});
describe('Deque Performance Test', () => {
const dataSize = 10000;
it('should numeric queue be efficient', function () {
const startTime = performance.now();
const queue = new Deque<number>();
for (let i = 0; i < dataSize; i++) {
queue.unshift(i);
}
for (let i = 0; i < dataSize; i++) {
queue.pop();
}
console.log(`Queue Deque Test: ${performance.now() - startTime} ms`);
expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100);
});
});

View file

@ -197,3 +197,45 @@ describe('LinkedListQueue', () => {
// Add more test cases for other methods of LinkedListQueue.
});
describe('Queue Performance Test', () => {
const dataSize = 10000;
it('should numeric queue be efficient', function () {
const startTime = performance.now();
const queue = new Queue<number>();
for (let i = 0; i < dataSize; i++) {
queue.enqueue(i);
}
for (let i = 0; i < dataSize; i++) {
queue.dequeue();
}
console.log(`Queue Performance Test: ${performance.now() - startTime} ms`);
expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100);
});
it('should numeric Array be more efficient than Queue when the data size is 10000', function () {
const startTime2 = performance.now();
const queue2: number[] = [];
for (let i = 0; i < dataSize; i++) {
queue2.push(i);
}
for (let i = 0; i < dataSize; i++) {
queue2.shift();
}
console.log(`Array Performance Test: ${performance.now() - startTime2} ms`);
expect(performance.now() - startTime2).toBeLessThan(bigO.CUBED * 100);
});
it('should numeric LinkedListQueue be efficient', function () {
const startTime = performance.now();
const queue = new LinkedListQueue<number>();
for (let i = 0; i < dataSize; i++) {
queue.enqueue(i);
}
for (let i = 0; i < dataSize; i++) {
queue.dequeue();
}
console.log(`LinkedListQueue Performance Test: ${performance.now() - startTime} ms`);
expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100);
});
});