mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
[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:
parent
48e94f29ed
commit
3983ab089c
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
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