diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index f5d5aca..3081725 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -72,27 +72,25 @@ export class AVLTree = 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` 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[] { - const deletedResults = super.delete(key); + override delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult[] { + const deletedResults = super.delete(nodeOrKey); for (const {needBalanced} of deletedResults) { if (needBalanced) { this._balancePath(needBalanced); diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 3c58236..d5000b8 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = this._defaultCallbackByKey): boolean { + has( + nodeProperty: BinaryTreeNodeKey | N, + callback: MapCallback = 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 = 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 = this._defaultCallbackByKey): N | null { + get( + nodeProperty: BinaryTreeNodeKey | N, + callback: MapCallback = 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 = 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 = 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 = 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 = 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 = this._defaultCallbackByKey, - subTreeRoot: N | BinaryTreeNodeKey | null = this.root + beginRoot: N | BinaryTreeNodeKey | null = this.root, + iterationType = this.iterationType ): MapCallbackReturn[] { - if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot); + if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot); const ans: MapCallbackReturn[] = []; - 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 = 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 = this._defaultCallbackByKey, pattern: DFSOrderPattern = 'in', beginRoot: N | null = this.root, - loopType: LoopType = LoopType.ITERATIVE + iterationType: IterationType = IterationType.ITERATIVE ): MapCallbackReturn[] { if (!beginRoot) return []; const ans: MapCallbackReturn[] = []; - if (loopType === LoopType.RECURSIVE) { + if (iterationType === IterationType.RECURSIVE) { const _traverse = (node: N) => { switch (pattern) { case 'in': @@ -807,30 +831,31 @@ export class BinaryTree = 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 = this._defaultCallbackByKey, withLevel: boolean = false, - node?: N | null + beginRoot: N | null = this.root, + iterationType = this.iterationType ): BFSCallbackReturn[] { - if (!node) node = this.root; - if (!node) return []; + if (!beginRoot) return []; const ans: BFSCallbackReturn[] = []; - 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 = 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 objects. */ morris( callback: MapCallback = this._defaultCallbackByKey, - pattern: DFSOrderPattern = 'in' + pattern: DFSOrderPattern = 'in', + beginRoot: N | null = this.root ): MapCallbackReturn[] { - if (this.root === null) return []; + if (beginRoot === null) return []; const ans: MapCallbackReturn[] = []; - 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 = BinaryTreeNode> } cur = cur.right; } - _printEdge(this.root); + _printEdge(beginRoot); break; } return ans; diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index e5ae80c..6cb619a 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -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 = BSTNode> extends BinaryTree * 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 = BSTNode> extends BinaryTree } } }; - if (this.loopType === LoopType.RECURSIVE) { + if (iterationType === IterationType.RECURSIVE) { recursive(sortedKeysOrNodes, sortedData); } else { iterative(); @@ -221,16 +223,15 @@ export class BST = BSTNode> extends BinaryTree } /** - * 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 = BSTNode> extends BinaryTree * 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 = 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 = BSTNode> extends BinaryTree * 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 = this._defaultCallbackByKey, lesserOrGreater: CP = CP.lt, - node: N | BinaryTreeNodeKey | null + targetNode: N | BinaryTreeNodeKey | null = this.root, + iterationType = this.iterationType ): MapCallbackReturn { - if (typeof node === 'number') node = this.get(node); + if (typeof targetNode === 'number') targetNode = this.get(targetNode); const ans: MapCallbackReturn[] = []; - 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([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 = BSTNode> extends BinaryTree * 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 = BSTNode> extends BinaryTree * 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), diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index c2ea9fa..aa51cc0 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -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 = 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); diff --git a/src/types/data-structures/binary-tree.ts b/src/types/data-structures/binary-tree.ts index 0371633..e392a52 100644 --- a/src/types/data-structures/binary-tree.ts +++ b/src/types/data-structures/binary-tree.ts @@ -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> = export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type BinaryTreeOptions = { loopType?: LoopType } +export type BinaryTreeOptions = { iterationType?: IterationType } diff --git a/test/config.ts b/test/config.ts new file mode 100644 index 0000000..2316e5e --- /dev/null +++ b/test/config.ts @@ -0,0 +1 @@ +export const isDebugTest = true; diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index acd8368..21cbe99 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -144,3 +144,53 @@ describe('BinaryTree', () => { expect(binaryTree.root).toBeNull(); }); }); + +describe('BinaryTree Morris Traversal', () => { + // Create a binary tree + const tree = new BinaryTree>(); + 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); + }); +}); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 75d4e2d..9683af9 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -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); + }); }); diff --git a/test/unit/data-structures/binary-tree/tree-multiset.test.ts b/test/unit/data-structures/binary-tree/tree-multiset.test.ts index 4a9a101..6812143 100644 --- a/test/unit/data-structures/binary-tree/tree-multiset.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multiset.test.ts @@ -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(); diff --git a/test/utils/big-o.ts b/test/utils/big-o.ts index e9082a8..43d7eca 100644 --- a/test/utils/big-o.ts +++ b/test/utils/big-o.ts @@ -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 = {