mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
commit
44c29d89fc
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
1
test/config.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const isDebugTest = true;
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in a new issue