[binary-tree] Each method that traverses based on the root node can specify any node within the tree as the root node.

This commit is contained in:
Revone 2023-10-23 22:36:49 +08:00
parent 48e94f29ed
commit 3983ab089c
9 changed files with 91 additions and 27 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

@ -646,7 +646,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @param {N | null} subTreeRoot - The `node` parameter represents the root node of a binary search tree (BST).
* @returns a boolean value.
*/
isSubtreeBST(subTreeRoot: N | null): boolean {
isSubtreeBST(subTreeRoot: N): boolean {
// TODO there is a bug
if (!subTreeRoot) return true;
@ -681,6 +681,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* @returns The `isBST()` function is returning a boolean value.
*/
isBST(): boolean {
if (this.root === null) return true;
return this.isSubtreeBST(this.root);
}
@ -814,7 +815,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
bfs(
callback: BFSCallback<N> = this._defaultCallbackByKey,
withLevel: boolean = false,
node?: N | null
node: N | null = this.root
): BFSCallbackReturn<N>[] {
if (!node) node = this.root;
if (!node) return [];
@ -870,18 +871,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 +958,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
}
cur = cur.right;
}
_printEdge(this.root);
_printEdge(beginRoot);
break;
}
return ans;

View file

@ -227,10 +227,10 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* 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(): 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): BinaryTreeNodeKey {
if (this._compare(0, 1) === CP.lt) return this.getRightMost(beginRoot)?.key ?? 0;
else if (this._compare(0, 1) === CP.gt) return this.getLeftMost(beginRoot)?.key ?? 0;
else return this.getRightMost(beginRoot)?.key ?? 0;
}
/**
@ -312,13 +312,13 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
lesserOrGreaterTraverse(
callback: MapCallback<N> = this._defaultCallbackByKey,
lesserOrGreater: CP = CP.lt,
node: N | BinaryTreeNodeKey | null
node: N | BinaryTreeNodeKey | null = this.root
): MapCallbackReturn<N> {
if (typeof node === 'number') node = this.get(node);
const ans: MapCallbackReturn<N>[] = [];
if (!node) return [];
if (!node) return ans;
const key = node.key;
if (!this.root) return false;
if (!this.root) return ans;
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
@ -331,7 +331,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
};
_traverse(this.root);
return true;
return ans;
} else {
const queue = new Queue<N>([this.root]);
while (queue.size > 0) {

View file

@ -293,7 +293,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
* not be taken into account when removing it. If `ignoreCount` is set to `false
* @returns The function `delete` returns an array of `BinaryTreeDeletedResult<N>` objects.
*/
override delete(nodeOrKey: N | BinaryTreeNodeKey, ignoreCount = false): BinaryTreeDeletedResult<N>[] {
override delete(nodeOrKey: N | BinaryTreeNodeKey, ignoreCount = false): BinaryTreeDeletedResult<N>[]{
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
if (!this.root) return bstDeletedResult;

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 = {