Merge pull request #23 from zrwusa/optimization

Optimization
This commit is contained in:
zrwusa 2023-10-24 00:06:23 +08:00 committed by GitHub
commit 44c29d89fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 191 additions and 98 deletions

View file

@ -72,27 +72,25 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
/**
* The function overrides the add method of a binary tree node and balances the tree after inserting a new node.
* @param {BinaryTreeNodeKey} key - The `key` parameter is the identifier of the binary tree node that we want to add.
* @param keyOrNode - The `keyOrNode` parameter is either a key or a node that needs to be added to the binary tree.
* @param [val] - The `val` parameter is an optional value that can be assigned to the node being added. It is of type
* `N['val']`, which means it should be of the same type as the `val` property of the nodes in the binary tree.
* @returns The method is returning the inserted node, or null or undefined if the insertion was not successful.
*/
override add(key: BinaryTreeNodeKey, val?: N['val']): N | null | undefined {
override add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined {
// TODO support node as a param
const inserted = super.add(key, val);
const inserted = super.add(keyOrNode, val);
if (inserted) this._balancePath(inserted);
return inserted;
}
/**
* The function overrides the delete method of a binary tree and performs additional operations to balance the tree after
* deletion.
* @param {BinaryTreeNodeKey} key - The `key` parameter represents the identifier of the binary tree node that needs to be
* removed.
* The function overrides the delete method of a binary tree and performs additional operations to balance the tree after deletion.
* @returns The method is returning an array of `BinaryTreeDeletedResult<N>` objects.
* @param nodeOrKey - The `nodeOrKey` parameter is either a node or a key that needs to be deleted from the binary tree.
*/
override delete(key: BinaryTreeNodeKey): BinaryTreeDeletedResult<N>[] {
const deletedResults = super.delete(key);
override delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult<N>[] {
const deletedResults = super.delete(nodeOrKey);
for (const {needBalanced} of deletedResults) {
if (needBalanced) {
this._balancePath(needBalanced);

View file

@ -15,7 +15,7 @@ import type {
MapCallback,
MapCallbackReturn
} from '../../types';
import {BinaryTreeDeletedResult, DFSOrderPattern, FamilyPosition, LoopType} from '../../types';
import {BinaryTreeDeletedResult, DFSOrderPattern, FamilyPosition, IterationType} from '../../types';
import {IBinaryTree} from '../../interfaces';
import {trampoline} from '../../utils';
import {Queue} from '../queue';
@ -106,8 +106,8 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
*/
constructor(options?: BinaryTreeOptions) {
if (options !== undefined) {
const {loopType = LoopType.ITERATIVE} = options;
this._loopType = loopType;
const {iterationType = IterationType.ITERATIVE} = options;
this._loopType = iterationType;
}
}
@ -136,13 +136,13 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
return this._size;
}
private _loopType: LoopType = LoopType.ITERATIVE;
private _loopType: IterationType = IterationType.ITERATIVE;
get loopType(): LoopType {
get iterationType(): IterationType {
return this._loopType;
}
set loopType(v: LoopType) {
set iterationType(v: IterationType) {
this._loopType = v;
}
@ -367,13 +367,14 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param {N | BinaryTreeNodeKey | null} [beginRoot] - The `beginRoot` parameter is optional and can be of type `N` (a
* generic type representing a node in a binary tree), `BinaryTreeNodeKey` (a type representing the ID of a binary tree
* node), or `null`.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of
* @returns the height of the binary tree.
*/
getHeight(beginRoot: N | BinaryTreeNodeKey | null = this.root): number {
getHeight(beginRoot: N | BinaryTreeNodeKey | null = this.root, iterationType = this.iterationType): number {
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot);
if (!beginRoot) return -1;
if (this._loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _getMaxHeight = (cur: N | null | undefined): number => {
if (!cur) return -1;
const leftHeight = _getMaxHeight(cur.left);
@ -416,12 +417,13 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param {N | null} [beginRoot] - The `beginRoot` parameter is an optional parameter of type `N` or `null`. It
* represents the starting node from which to calculate the minimum height of a binary tree. If no value is provided
* for `beginRoot`, the `this.root` property is used as the default value.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop
* @returns The function `getMinHeight` returns the minimum height of the binary tree.
*/
getMinHeight(beginRoot: N | null = this.root): number {
getMinHeight(beginRoot: N | null = this.root, iterationType = this.iterationType): number {
if (!beginRoot) return -1;
if (this._loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _getMinHeight = (cur: N | null | undefined): number => {
if (!cur) return 0;
if (!cur.left && !cur.right) return 0;
@ -481,19 +483,21 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* return only one node that matches the given `nodeProperty` or `propertyName`. If `onlyOne` is set to `true`, the
* function will stop traversing the tree and return the first matching node. If `only
* @param beginRoot
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop
* @returns an array of nodes (type N).
*/
getNodes(
nodeProperty: BinaryTreeNodeKey | N,
callback: MapCallback<N> = this._defaultCallbackByKey,
onlyOne = false,
beginRoot: N | null = this.root
beginRoot: N | null = this.root,
iterationType = this.iterationType
): N[] {
if (!beginRoot) return [];
const ans: N[] = [];
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
if (callback(cur) === nodeProperty) {
ans.push(cur);
@ -529,11 +533,18 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeKey` or `N`.
* It represents the property of the binary tree node that you want to check.
* specifies the name of the property to be checked in the nodes. If not provided, it defaults to 'key'.
* @param beginRoot - The `beginRoot` parameter is an optional parameter of type `N` or `null`. It represents the root node of a tree or null if the tree is empty.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop
* @returns a boolean value.
*/
has(nodeProperty: BinaryTreeNodeKey | N, callback: MapCallback<N> = this._defaultCallbackByKey): boolean {
has(
nodeProperty: BinaryTreeNodeKey | N,
callback: MapCallback<N> = this._defaultCallbackByKey,
beginRoot = this.root,
iterationType = this.iterationType
): boolean {
// TODO may support finding node by value equal
return this.getNodes(nodeProperty, callback, true).length > 0;
return this.getNodes(nodeProperty, callback, true, beginRoot, iterationType).length > 0;
}
/**
@ -544,12 +555,19 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* It represents the property of the binary tree node that you want to search for.
* specifies the property name to be used for searching the binary tree nodes. If this parameter is not provided, the
* default value is set to `'key'`.
* @param beginRoot - The `beginRoot` parameter is an optional parameter of type `N` or `null`. It represents the root node of a tree or null if the tree is empty.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop used to traverse the binary tree.
* @returns either the value of the specified property of the node, or the node itself if no property name is provided.
* If no matching node is found, it returns null.
*/
get(nodeProperty: BinaryTreeNodeKey | N, callback: MapCallback<N> = this._defaultCallbackByKey): N | null {
get(
nodeProperty: BinaryTreeNodeKey | N,
callback: MapCallback<N> = this._defaultCallbackByKey,
beginRoot = this.root,
iterationType = this.iterationType
): N | null {
// TODO may support finding node by value equal
return this.getNodes(nodeProperty, callback, true)[0] ?? null;
return this.getNodes(nodeProperty, callback, true, beginRoot, iterationType)[0] ?? null;
}
/**
@ -581,17 +599,18 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param {N | BinaryTreeNodeKey | null} [beginRoot] - The `beginRoot` parameter is optional and can be of type `N` (a
* generic type representing a node in a binary tree), `BinaryTreeNodeKey` (a type representing the ID of a binary tree
* node), or `null`.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop used to traverse the binary tree.
* @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. The function returns the leftmost node found during the traversal. If no leftmost
* node is found (
*/
getLeftMost(beginRoot: N | BinaryTreeNodeKey | null = this.root): N | null {
getLeftMost(beginRoot: N | BinaryTreeNodeKey | null = this.root, iterationType = this.iterationType): N | null {
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot);
if (!beginRoot) return beginRoot;
if (this._loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N): N => {
if (!cur.left) return cur;
return _traverse(cur.left);
@ -615,15 +634,16 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param {N | null} [beginRoot] - The `node` parameter is an optional parameter of type `N` or `null`. It represents the
* starting node from which we want to find the rightmost node. If no node is provided, the function will default to
* using the root node of the data structure.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop
* @returns The `getRightMost` function returns the rightmost node in a binary tree. If the `node` parameter is provided,
* it returns the rightmost node starting from that node. If the `node` parameter is not provided, it returns the
* rightmost node starting from the root of the binary tree.
*/
getRightMost(beginRoot: N | null = this.root): N | null {
getRightMost(beginRoot: N | null = this.root, iterationType = this.iterationType): N | null {
// TODO support get right most by passing key in
if (!beginRoot) return beginRoot;
if (this._loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N): N => {
if (!cur.right) return cur;
return _traverse(cur.right);
@ -643,25 +663,26 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
/**
* The function checks if a binary search tree is valid by traversing it either recursively or iteratively.
* @param {N | null} subTreeRoot - The `node` parameter represents the root node of a binary search tree (BST).
* @param {N | null} beginRoot - The `node` parameter represents the root node of a binary search tree (BST).
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop
* @returns a boolean value.
*/
isSubtreeBST(subTreeRoot: N | null): boolean {
isSubtreeBST(beginRoot: N, iterationType = this.iterationType): boolean {
// TODO there is a bug
if (!subTreeRoot) return true;
if (!beginRoot) return true;
if (this._loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const dfs = (cur: N | null | undefined, min: BinaryTreeNodeKey, max: BinaryTreeNodeKey): boolean => {
if (!cur) return true;
if (cur.key <= min || cur.key >= max) return false;
return dfs(cur.left, min, cur.key) && dfs(cur.right, cur.key, max);
};
return dfs(subTreeRoot, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
return dfs(beginRoot, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
} else {
const stack = [];
let prev = Number.MIN_SAFE_INTEGER,
curr: N | null | undefined = subTreeRoot;
curr: N | null | undefined = beginRoot;
while (curr || stack.length > 0) {
while (curr) {
stack.push(curr);
@ -680,37 +701,40 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* The function isBST checks if the binary tree is valid binary search tree.
* @returns The `isBST()` function is returning a boolean value.
*/
isBST(): boolean {
return this.isSubtreeBST(this.root);
isBST(iterationType = this.iterationType): boolean {
if (this.root === null) return true;
return this.isSubtreeBST(this.root, iterationType);
}
/**
* The function `subTreeTraverse` 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
* @param {N | BinaryTreeNodeKey | null} beginRoot - The `beginRoot` 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 value.
* specifies the property of the binary tree node that should be modified. If not provided, it defaults to 'key'.
* @param iterationType - The `iterationType` parameter is an optional parameter of type `IterationType`. It represents the type of loop
* @returns a boolean value.
*/
subTreeTraverse(
callback: MapCallback<N> = this._defaultCallbackByKey,
subTreeRoot: N | BinaryTreeNodeKey | null = this.root
beginRoot: N | BinaryTreeNodeKey | null = this.root,
iterationType = this.iterationType
): MapCallbackReturn<N>[] {
if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot);
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot);
const ans: MapCallbackReturn<N>[] = [];
if (!subTreeRoot) return ans;
if (!beginRoot) return ans;
if (this._loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
ans.push(callback(cur));
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
};
_traverse(subTreeRoot);
_traverse(beginRoot);
} else {
const stack: N[] = [subTreeRoot];
const stack: N[] = [beginRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
@ -729,18 +753,18 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param callback
* @param beginRoot - The `beginRoot` parameter is an optional parameter of type `N` or `null`. It represents the
* @param {'in' | 'pre' | 'post'} [pattern] - The traversal pattern: 'in' (in-order), 'pre' (pre-order), or 'post' (post-order).
* @param loopType - The type of loop to use for the depth-first search traversal. The default value is `LoopType.ITERATIVE`.
* @param iterationType - The type of loop to use for the depth-first search traversal. The default value is `IterationType.ITERATIVE`.
* @returns an instance of the BinaryTreeNodeProperties class, which contains the accumulated properties of the binary tree nodes based on the specified pattern and node or property name.
*/
dfs(
callback: MapCallback<N> = this._defaultCallbackByKey,
pattern: DFSOrderPattern = 'in',
beginRoot: N | null = this.root,
loopType: LoopType = LoopType.ITERATIVE
iterationType: IterationType = IterationType.ITERATIVE
): MapCallbackReturn<N>[] {
if (!beginRoot) return [];
const ans: MapCallbackReturn<N>[] = [];
if (loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (node: N) => {
switch (pattern) {
case 'in':
@ -807,30 +831,31 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
/**
* 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 root node of a binary tree. If it is null, the function will use the root node of the current binary tree instance.
* @param callback - The `callback` parameter is a function that takes a node and a level as parameters and returns a value.
* @param withLevel - The `withLevel` parameter is a boolean flag that determines whether to include the level of each node in the result. If `withLevel` is set to `true`, the function will include the level of each node in the result. If `withLevel` is set to `false` or not provided, the function will not include the level of each node in the result.
* @param beginRoot - The `beginRoot` parameter is an optional parameter of type `N` or `null`. It represents the root node of a tree or null if the tree is empty.
* @param iterationType
*/
bfs(
callback: BFSCallback<N> = this._defaultCallbackByKey,
withLevel: boolean = false,
node?: N | null
beginRoot: N | null = this.root,
iterationType = this.iterationType
): BFSCallbackReturn<N>[] {
if (!node) node = this.root;
if (!node) return [];
if (!beginRoot) return [];
const ans: BFSCallbackReturn<N>[] = [];
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _recursive = (node: N, level: number) => {
callback && ans.push(callback(node, withLevel ? level : undefined));
if (node.left) _recursive(node.left, level + 1);
if (node.right) _recursive(node.right, level + 1);
};
_recursive(node, 0);
_recursive(beginRoot, 0);
} else {
const stack: [N, number][] = [[node, 0]];
const stack: [N, number][] = [[beginRoot, 0]];
while (stack.length > 0) {
const head = stack.pop()!;
@ -870,18 +895,23 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
/**
* The `morris` function performs an in-order, pre-order, or post-order traversal on a binary tree using the Morris traversal algorithm.
* The Morris algorithm only modifies the tree's structure during traversal; once the traversal is complete,
* the tree's structure should be restored to its original state to maintain the tree's integrity.
* This is because the purpose of the Morris algorithm is to save space rather than permanently alter the tree's shape.
* @param {'in' | 'pre' | 'post'} [pattern] - The traversal pattern: 'in' (in-order), 'pre' (pre-order), or 'post' (post-order).
* @param callback - The `callback` parameter is a function that takes a node as a parameter and returns a value.
* @param beginRoot - The `beginRoot` parameter is an optional parameter of type `N` or `null`. It represents the
* @returns An array of BinaryTreeNodeProperties<N> objects.
*/
morris(
callback: MapCallback<N> = this._defaultCallbackByKey,
pattern: DFSOrderPattern = 'in'
pattern: DFSOrderPattern = 'in',
beginRoot: N | null = this.root
): MapCallbackReturn<N>[] {
if (this.root === null) return [];
if (beginRoot === null) return [];
const ans: MapCallbackReturn<N>[] = [];
let cur: N | null | undefined = this.root;
let cur: N | null | undefined = beginRoot;
const _reverseEdge = (node: N | null | undefined) => {
let pre: N | null | undefined = null;
let next: N | null | undefined = null;
@ -952,7 +982,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
}
cur = cur.right;
}
_printEdge(this.root);
_printEdge(beginRoot);
break;
}
return ans;

View file

@ -13,7 +13,7 @@ import type {
MapCallback,
MapCallbackReturn
} from '../../types';
import {CP, LoopType} from '../../types';
import {CP, IterationType} from '../../types';
import {BinaryTree, BinaryTreeNode} from './binary-tree';
import {IBinaryTree} from '../../interfaces';
import {Queue} from '../queue';
@ -134,12 +134,14 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* to the binary search tree.
* @param {N['val'][]} data - The values of tree nodes
* @param {boolean} isBalanceAdd - If true the nodes will be balance inserted in binary search method.
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies whether to use a
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
override addMany(
keysOrNodes: (BinaryTreeNodeKey | null)[] | (N | null)[],
data?: N['val'][],
isBalanceAdd = true
isBalanceAdd = true,
iterationType = this.iterationType
): (N | null | undefined)[] {
// TODO this addMany function is inefficient, it should be optimized
function hasNoNull(arr: (BinaryTreeNodeKey | null)[] | (N | null)[]): arr is BinaryTreeNodeKey[] | N[] {
@ -199,7 +201,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
}
};
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
recursive(sortedKeysOrNodes, sortedData);
} else {
iterative();
@ -221,16 +223,15 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
/**
* The function returns the key of the rightmost node if the comparison between two values is less than, the key of the
* leftmost node if the comparison is greater than, and the key of the rightmost node otherwise.
* @returns The method `lastKey()` returns the key of the rightmost node in the binary tree if the comparison between
* the values at index 0 and 1 is less than, otherwise it returns the key of the leftmost node. If the comparison is
* equal, it returns the key of the rightmost node. If there are no nodes in the tree, it returns 0.
* lastKey returns the last key in a binary tree. If the binary tree is empty, it returns 0.
* @param beginRoot - The `beginRoot` parameter is an optional parameter that specifies the root node from which to begin
* the search for the last key.
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies whether to use a recursive or iterative approach to search for the last key.
*/
lastKey(): BinaryTreeNodeKey {
if (this._compare(0, 1) === CP.lt) return this.getRightMost()?.key ?? 0;
else if (this._compare(0, 1) === CP.gt) return this.getLeftMost()?.key ?? 0;
else return this.getRightMost()?.key ?? 0;
lastKey(beginRoot: N | null = this.root, iterationType = this.iterationType): BinaryTreeNodeKey {
if (this._compare(0, 1) === CP.lt) return this.getRightMost(beginRoot, iterationType)?.key ?? 0;
else if (this._compare(0, 1) === CP.gt) return this.getLeftMost(beginRoot, iterationType)?.key ?? 0;
else return this.getRightMost(beginRoot, iterationType)?.key ?? 0;
}
/**
@ -243,18 +244,20 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* return only one node that matches the given `nodeProperty` or all nodes that match the `nodeProperty`. If `onlyOne`
* is set to `true`, the function will return an array with only one node (if
* @param beginRoot - The `beginRoot` parameter is an optional parameter that specifies the root node from which to
* @param iterationType
* @returns an array of nodes (type N).
*/
override getNodes(
nodeProperty: BinaryTreeNodeKey | N,
callback: MapCallback<N> = this._defaultCallbackByKey,
onlyOne = false,
beginRoot: N | null = this.root
beginRoot: N | null = this.root,
iterationType = this.iterationType
): N[] {
if (!beginRoot) return [];
const ans: N[] = [];
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
const callbackResult = callback(cur);
if (callbackResult === nodeProperty) {
@ -305,43 +308,45 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* The `lesserOrGreaterTraverse` 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 callback - The `callback` parameter is a function that takes a node as a parameter and returns a 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 targetNode - The `targetNode` parameter is an optional parameter that specifies the node in the binary tree
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies whether to use a
*/
lesserOrGreaterTraverse(
callback: MapCallback<N> = this._defaultCallbackByKey,
lesserOrGreater: CP = CP.lt,
node: N | BinaryTreeNodeKey | null
targetNode: N | BinaryTreeNodeKey | null = this.root,
iterationType = this.iterationType
): MapCallbackReturn<N> {
if (typeof node === 'number') node = this.get(node);
if (typeof targetNode === 'number') targetNode = this.get(targetNode);
const ans: MapCallbackReturn<N>[] = [];
if (!node) return [];
const key = node.key;
if (!this.root) return false;
if (!targetNode) return ans;
const targetKey = targetNode.key;
if (!this.root) return ans;
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.key, key);
const compared = this._compare(cur.key, targetKey);
if (compared === lesserOrGreater) ans.push(callback(cur));
if (!cur.left && !cur.right) return;
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);
if (cur.left && this._compare(cur.left.key, targetKey) === lesserOrGreater) _traverse(cur.left);
if (cur.right && this._compare(cur.right.key, targetKey) === lesserOrGreater) _traverse(cur.right);
};
_traverse(this.root);
return true;
return ans;
} else {
const queue = new Queue<N>([this.root]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
const compared = this._compare(cur.key, key);
const compared = this._compare(cur.key, targetKey);
if (compared === lesserOrGreater) ans.push(callback(cur));
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);
if (cur.left && this._compare(cur.left.key, targetKey) === lesserOrGreater) queue.push(cur.left);
if (cur.right && this._compare(cur.right.key, targetKey) === lesserOrGreater) queue.push(cur.right);
}
}
return ans;
@ -363,13 +368,13 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* constructs a balanced binary search tree using either a recursive or iterative approach.
* @returns The function `perfectlyBalance()` returns a boolean value.
*/
perfectlyBalance(): boolean {
perfectlyBalance(iterationType = this.iterationType): boolean {
const sorted = this.dfs(node => node, 'in'),
n = sorted.length;
this.clear();
if (sorted.length < 1) return false;
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const buildBalanceBST = (l: number, r: number) => {
if (l > r) return;
const m = l + Math.floor((r - l) / 2);
@ -404,12 +409,12 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* The function `isAVLBalanced` checks if a binary tree is balanced according to the AVL tree property.
* @returns a boolean value.
*/
isAVLBalanced(): boolean {
isAVLBalanced(iterationType = this.iterationType): boolean {
if (!this.root) return true;
let balanced = true;
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _height = (cur: N | null | undefined): number => {
if (!cur) return 0;
const leftHeight = _height(cur.left),

View file

@ -6,7 +6,7 @@
* @license MIT License
*/
import type {BinaryTreeNodeKey, TreeMultisetNodeNested, TreeMultisetOptions} from '../../types';
import {BinaryTreeDeletedResult, CP, FamilyPosition, LoopType} from '../../types';
import {BinaryTreeDeletedResult, CP, FamilyPosition, IterationType} from '../../types';
import {IBinaryTree} from '../../interfaces';
import {AVLTree, AVLTreeNode} from './avl-tree';
@ -246,14 +246,14 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
* constructs a balanced binary search tree using either a recursive or iterative approach.
* @returns The function `perfectlyBalance()` returns a boolean value.
*/
override perfectlyBalance(): boolean {
override perfectlyBalance(iterationType = this.iterationType): boolean {
const sorted = this.dfs(node => node, 'in'),
n = sorted.length;
if (sorted.length < 1) return false;
this.clear();
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const buildBalanceBST = (l: number, r: number) => {
if (l > r) return;
const m = l + Math.floor((r - l) / 2);

View file

@ -7,7 +7,7 @@ import {BinaryTreeNode} from '../../data-structures/binary-tree';
* - `recursive`: Indicates the recursive loop type (with loops that call themselves).
*/
export enum LoopType {
export enum IterationType {
ITERATIVE = 'ITERATIVE',
RECURSIVE = 'RECURSIVE'
}
@ -44,4 +44,4 @@ export type BinaryTreeNodeProperties<N extends BinaryTreeNode<N['val'], N>> =
export type BinaryTreeNodeNested<T> = BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeOptions = { loopType?: LoopType }
export type BinaryTreeOptions = { iterationType?: IterationType }

1
test/config.ts Normal file
View file

@ -0,0 +1 @@
export const isDebugTest = true;

View file

@ -144,3 +144,53 @@ describe('BinaryTree', () => {
expect(binaryTree.root).toBeNull();
});
});
describe('BinaryTree Morris Traversal', () => {
// Create a binary tree
const tree = new BinaryTree<BinaryTreeNode<number>>();
tree.add(1);
tree.add(2);
tree.add(3);
tree.add(4);
tree.add(5);
it('should perform in-order Morris traversal correctly as dfs traversal', () => {
// Perform in-order Morris traversal
const result = tree.morris(node => node.key, 'in');
// Expected in-order traversal result
const expected = [4, 2, 5, 1, 3];
expect(result).toEqual(expected);
expect(tree.dfs(node => node.key, 'in')).toEqual(expected);
});
it('should perform pre-order Morris traversal correctly as dfs traversal', () => {
// Perform pre-order Morris traversal
const result = tree.morris(node => node.key, 'pre');
// Expected pre-order traversal result
const expected = [1, 2, 4, 5, 3];
expect(result).toEqual(expected);
expect(tree.dfs(node => node.key, 'pre')).toEqual(expected);
});
it('should perform post-order Morris traversal correctly as dfs traversal', () => {
// Perform post-order Morris traversal
const result = tree.morris(node => node.key, 'post');
// Expected post-order traversal result
const expected = [4, 5, 2, 3, 1];
expect(result).toEqual([4, 5, 2, 3, 1]);
expect(tree.dfs(node => node.key, 'post')).toEqual(expected);
});
it('after morris traversals should the structure of the tree be correct', () => {
const node1 = tree.get(1);
const node2 = tree.get(2);
const node3 = tree.get(3);
expect(node1?.left).toBe(node2);
expect(node1?.right).toBe(node3);
});
});

View file

@ -1,6 +1,7 @@
import {BST, BSTNode, CP} from '../../../../src';
import {isDebugTest} from '../../../config';
const isDebug = true;
const isDebug = isDebugTest;
describe('BST operations test', () => {
it('should perform various operations on a Binary Search Tree with numeric values', () => {
@ -444,4 +445,10 @@ describe('BST Performance test', function () {
isDebug && console.log('---listLevels', arr);
isDebug && console.log('---listLevels', performance.now() - startL);
});
it('should the lastKey of a BST to be the largest key', function () {
const bst = new BST();
bst.addMany([9, 8, 7, 3, 1, 2, 5, 4, 6], undefined, false);
expect(bst.lastKey()).toBe(9);
});
});

View file

@ -1,6 +1,7 @@
import {CP, TreeMultiset, TreeMultisetNode} from '../../../../src';
import {isDebugTest} from '../../../config';
const isDebug = false;
const isDebug = isDebugTest;
describe('TreeMultiset operations test', () => {
it('should perform various operations on a Binary Search Tree with numeric values', () => {
const treeMultiset = new TreeMultiset();

View file

@ -1,6 +1,7 @@
import {AnyFunction} from '../types';
import {isDebugTest} from '../config';
const isDebug = false;
const isDebug = isDebugTest;
const orderReducedBy = 2; // reduction of bigO's order compared to the baseline bigO
export const magnitude = {