mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
[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:
parent
284e85e76b
commit
3a86a5c37d
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue