Standardize methods for all BinaryTrees, enabling support for both TreeNode parameters and TreeNodeId as arguments.

This commit is contained in:
Revone 2023-08-27 21:14:18 +08:00
parent d29ff07f40
commit 8ac4e0e7c4
5 changed files with 74 additions and 56 deletions

View file

@ -461,21 +461,19 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
}
/**
* The function removes a node from a binary tree and returns information about the deleted node.
* @param {BinaryTreeNodeId} id - The `id` parameter is the identifier of the binary tree node that you want to remove.
* It is of type `BinaryTreeNodeId`.
* 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 decremented and the overall count of the binary tree will not be updated. If `
* @returns An array of objects is being returned. Each object in the array has two properties: "deleted" and
* "needBalanced". The "deleted" property contains the deleted node or undefined if no node was deleted. The
* "needBalanced" property is always null.
* 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.
*/
remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeletedResult<N>[] {
remove(nodeOrId: N | BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeletedResult<N>[] {
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
if (!this.root) return bstDeletedResult;
const curr: N | null = this.get(id);
const curr: N | null = (typeof nodeOrId === 'number') ? this.get(nodeOrId) : nodeOrId;
if (!curr) return bstDeletedResult;
const parent: N | null = curr?.parent ? curr.parent : null;
@ -518,16 +516,17 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
}
/**
* The function calculates the depth of a binary tree node by traversing its parent nodes.
* @param node - N - This is the node for which we want to calculate the depth. It is a generic type,
* meaning it can represent any type of data that we want to store in the node.
* @returns The depth of the given binary tree node.
* The function calculates the depth of a node in a binary tree.
* @param {N | BinaryTreeNodeId | null} beginRoot - The `beginRoot` parameter can be one of the following:
* @returns the depth of the given node or binary tree.
*/
getDepth(node: N): number {
getDepth(beginRoot: N | BinaryTreeNodeId | null): number {
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot, 'id');
let depth = 0;
while (node.parent) {
while (beginRoot?.parent) {
depth++;
node = node.parent;
beginRoot = beginRoot.parent;
}
return depth;
}
@ -540,8 +539,10 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
* If no value is provided for `beginRoot`, the function will use the `root` property of the class instance as
* @returns the height of the binary tree.
*/
getHeight(beginRoot?: N | null): number {
getHeight(beginRoot?: N | BinaryTreeNodeId | null): number {
beginRoot = beginRoot ?? this.root;
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot, 'id');
if (!beginRoot) return -1;
if (this._loopType === LoopType.RECURSIVE) {
@ -731,17 +732,22 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
getLeftMost(node: N): N;
/**
* The `getLeftMost` function returns the leftmost node in a binary tree, either recursively or iteratively using tail
* recursion optimization.
* @param {N | null} [node] - The `node` parameter is an optional parameter of type `N
* | null`. It represents the starting node from which to find the leftmost node in a binary tree. If no node is
* provided, the function will use the root node of the binary tree.
* @returns The `getLeftMost` function returns the leftmost node in a binary tree.
* The `getLeftMost` function returns the leftmost node in a binary tree, starting from a specified node or the root if
* no node is specified.
* @param {N | BinaryTreeNodeId | null} [beginRoot] - The `beginRoot` parameter is optional and can be of type `N` (a
* generic type representing a node in a binary tree), `BinaryTreeNodeId` (a type representing the ID of a binary tree
* node), or `null`.
* @returns The function `getLeftMost` returns the leftmost node in a binary tree. If the `beginRoot` parameter is
* provided, it starts the traversal from that node. If `beginRoot` is not provided or is `null`, it starts the
* traversal from the root of the binary tree. If there are no nodes in the binary tree, it returns `null`.
*/
getLeftMost(node?: N | null): N | null {
node = node ?? this.root;
if (!node) return node;
getLeftMost(beginRoot?: N | BinaryTreeNodeId | null): N | null {
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot, 'id');
beginRoot = beginRoot ?? this.root;
if (!beginRoot) return beginRoot;
if (this._loopType === LoopType.RECURSIVE) {
@ -750,7 +756,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return _traverse(cur.left);
}
return _traverse(node);
return _traverse(beginRoot);
} else {
// Indirect implementation of iteration using tail recursion optimization
const _traverse = trampoline((cur: N) => {
@ -758,7 +764,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return _traverse.cont(cur.left);
});
return _traverse(node);
return _traverse(beginRoot);
}
}
@ -890,8 +896,10 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
* provided, it defaults to `'val'`.
* @returns a number, which is the sum of the values of the nodes in the subtree rooted at `subTreeRoot`.
*/
subTreeSum(subTreeRoot: N, propertyName ?: BinaryTreeNodePropertyName): number {
subTreeSum(subTreeRoot: N | BinaryTreeNodeId | null, propertyName ?: BinaryTreeNodePropertyName): number {
propertyName = propertyName ?? 'id';
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'id');
if (!subTreeRoot) return 0;
let sum = 0;
@ -937,17 +945,22 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return sum;
}
/**
* The function `subTreeAdd` adds a specified delta value to a property of each node in a binary tree.
* @param subTreeRoot - The `subTreeRoot` parameter is the root node of the subtree where the values will be modified.
* The function `subTreeAdd` adds a delta value to a specified property of each node in a subtree.
* @param {N | BinaryTreeNodeId | 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 root.
* @param {number} delta - The `delta` parameter is a number that represents the amount by which the property value of
* each node in the subtree should be increased or decreased.
* each node in the subtree should be incremented or decremented.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property of the `BinaryTreeNode` that should be modified. It defaults to `'id'` if not provided.
* @returns a boolean value, which is `true`.
* specifies the property of the binary tree node that should be modified. It can be either 'id' or 'count'. If no
* value is provided for `propertyName`, it defaults to 'id'.
* @returns a boolean value.
*/
subTreeAdd(subTreeRoot: N, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
subTreeAdd(subTreeRoot: N | BinaryTreeNodeId | null, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
propertyName = propertyName ?? 'id';
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'id');
if (!subTreeRoot) return false;
const _addByProperty = (cur: N) => {

View file

@ -213,19 +213,20 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
// --- start additional functions
/**
* The `lesserSum` function calculates the sum of a specified property in all nodes with an ID less than a given ID in
* a binary search tree.
* @param {BinaryTreeNodeId} id - The `id` parameter is the identifier of the binary tree node for which you want to
* calculate the lesser sum.
* The `lesserSum` function calculates the sum of property values in a binary tree for nodes that have a lesser value
* than a given node.
* @param {N | BinaryTreeNodeId | 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 of the binary tree node to use for calculating the sum. If not provided, it defaults to 'id'.
* @returns The function `lesserSum` returns a number, which represents the sum of the values of the nodes in the
* binary search tree that have a property value lesser than the given `id`.
* specifies the property name to use for calculating the sum. If not provided, it defaults to `'id'`.
* @returns The function `lesserSum` returns a number, which represents the sum of the values of the nodes in a binary
* tree that have a lesser value than the specified `beginNode` based on the specified `propertyName`.
*/
lesserSum(id: BinaryTreeNodeId, propertyName ?: BinaryTreeNodePropertyName): number {
lesserSum(beginNode: N | BinaryTreeNodeId | null, propertyName ?: BinaryTreeNodePropertyName): number {
propertyName = propertyName ?? 'id';
if (typeof beginNode === 'number') beginNode = this.get(beginNode, 'id');
if (!beginNode) return 0;
if (!this.root) return 0;
const id = beginNode.id;
const getSumByPropertyName = (cur: N) => {
let needSum: number;
switch (propertyName) {
@ -299,8 +300,11 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* defaults to 'id'.
* @returns a boolean value.
*/
allGreaterNodesAdd(node: N, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
allGreaterNodesAdd(node: N | BinaryTreeNodeId | null, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
propertyName = propertyName ?? 'id';
if (typeof node === 'number') node = this.get(node, 'id');
if (!node) return false;
const id = node.id;
if (!this.root) return false;
const _sumByPropertyName = (cur: N) => {
@ -319,7 +323,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.id, node.id);
const compared = this._compare(cur.id, id);
_sumByPropertyName(cur);
if (!cur.left && !cur.right) return;

View file

@ -4,7 +4,8 @@ import {
BinaryTreeDeletedResult,
BinaryTreeNodeId,
BinaryTreeNodePropertyName,
DFSOrderPattern, FamilyPosition,
DFSOrderPattern,
FamilyPosition,
LoopType,
NodeOrPropertyName
} from '../types';

View file

@ -16,10 +16,10 @@ describe('BST operations test', () => {
expect(bst.has(6)).toBe(true);
const node6 = bst.get(6);
expect(node6 && bst.getHeight(node6)).toBe(2);
expect(node6 && bst.getDepth(node6)).toBe(3);
expect(node6 && bst.getHeight(6)).toBe(2);
expect(node6 && bst.getDepth(6)).toBe(3);
const nodeId10 = bst.get(10, 'id');
const nodeId10 = bst.get(10);
expect(nodeId10?.id).toBe(10);
const nodeVal9 = bst.get(9, 'val');
@ -35,7 +35,7 @@ describe('BST operations test', () => {
const minNodeBySpecificNode = node15 && bst.getLeftMost(node15);
expect(minNodeBySpecificNode?.id).toBe(12);
const subTreeSum = node15 && bst.subTreeSum(node15);
const subTreeSum = node15 && bst.subTreeSum(15);
expect(subTreeSum).toBe(70);
const lesserSum = bst.lesserSum(10);
@ -43,14 +43,14 @@ describe('BST operations test', () => {
expect(node15).toBeInstanceOf(BSTNode);
if (node15 instanceof BSTNode) {
const subTreeAdd = bst.subTreeAdd(node15, 1, 'count');
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(node11, 2, 'count');
const allGreaterNodesAdded = bst.allGreaterNodesAdd(11, 2, 'count');
expect(allGreaterNodesAdded).toBeDefined();
}
@ -74,7 +74,7 @@ describe('BST operations test', () => {
expect(bst.isAVLBalanced()).toBe(true);
expect(node15 && bst.getHeight(node15)).toBe(2);
expect(bst.getHeight(15)).toBe(1);
const removed1 = bst.remove(1, true);
expect(removed1).toBeInstanceOf(Array);
@ -189,7 +189,7 @@ describe('BST operations test', () => {
expect(bfsNodes[0].id).toBe(2);
expect(bfsNodes[1].id).toBe(12);
expect(bfsNodes[2].id).toBe(16);
expect(bst.count).toBe(5);
});

View file

@ -1,4 +1,4 @@
const orderReducedBy = 2; // reduction of magnitude's order compared to the baseline magnitude
const orderReducedBy = 3; // reduction of magnitude's order compared to the baseline magnitude
export const magnitude = {
CONSTANT: Math.floor(Number.MAX_SAFE_INTEGER / Math.pow(10, orderReducedBy)),