From b099b759e85cf77988eb87c58e53d06f9be10bb8 Mon Sep 17 00:00:00 2001 From: Revone Date: Sat, 25 Nov 2023 20:40:11 +0800 Subject: [PATCH] refactor: The strategy of all binary tree data structures is to replace a node only when the same node is added. --- src/data-structures/binary-tree/avl-tree.ts | 18 ++- .../binary-tree/binary-tree.ts | 72 ++++++---- src/data-structures/binary-tree/bst.ts | 110 +++++++-------- src/data-structures/binary-tree/rb-tree.ts | 13 +- .../binary-tree/tree-multimap.ts | 125 +++++------------- src/interfaces/binary-tree.ts | 2 +- .../binary-tree/tree-multimap.test.ts | 60 +++++---- 7 files changed, 186 insertions(+), 214 deletions(-) diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index d26ea3f..cacd812 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -12,8 +12,8 @@ import type { AVLTreeOptions, BiTreeDeleteResult, BSTNodeKeyOrNode, - BTNodeExemplar, - BTNKey + BTNKey, + BTNodeExemplar } from '../../types'; import { BTNCallback } from '../../types'; import { IBinaryTree } from '../../interfaces'; @@ -117,7 +117,7 @@ export class AVLTree = AVLTreeNode = AVLTreeNode, destNode: BSTNodeKeyOrNode): N | undefined { - srcNode = this.ensureNotKey(srcNode); - destNode = this.ensureNotKey(destNode); + protected override _swapProperties(srcNode: BSTNodeKeyOrNode, destNode: BSTNodeKeyOrNode): N | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); if (srcNode && destNode) { const { key, value, height } = destNode; @@ -441,4 +441,10 @@ export class AVLTree = AVLTreeNode = BinaryTree /** * The parent node of the current node. */ - parent?: N | null; + parent?: N; /** * Creates a new instance of BinaryTreeNode. @@ -201,8 +201,8 @@ export class BinaryTree = BinaryTreeNode while (queue.size > 0) { const cur = queue.shift()!; if (newNode && cur.key === newNode.key) { - cur.value = newNode.value; - return; + this._replaceNode(cur, newNode); + return newNode; } const inserted = this._addTo(newNode, cur); if (inserted !== undefined) return inserted; @@ -364,7 +364,7 @@ export class BinaryTree = BinaryTreeNode const leftSubTreeRightMost = this.getRightMost(curr.left); if (leftSubTreeRightMost) { const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; - orgCurrent = this._swap(curr, leftSubTreeRightMost); + orgCurrent = this._swapProperties(curr, leftSubTreeRightMost); if (parentOfLeftSubTreeMax) { if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; @@ -399,8 +399,8 @@ export class BinaryTree = BinaryTreeNode * @returns the depth of the `distNode` relative to the `beginRoot`. */ getDepth(distNode: BTNodeKeyOrNode, beginRoot: BTNodeKeyOrNode = this.root): number { - distNode = this.ensureNotKey(distNode); - beginRoot = this.ensureNotKey(beginRoot); + distNode = this.ensureNode(distNode); + beginRoot = this.ensureNode(beginRoot); let depth = 0; while (distNode?.parent) { if (distNode === beginRoot) { @@ -432,7 +432,7 @@ export class BinaryTree = BinaryTreeNode * @returns the height of the binary tree. */ getHeight(beginRoot: BTNodeKeyOrNode = this.root, iterationType = this.iterationType): number { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { @@ -481,7 +481,7 @@ export class BinaryTree = BinaryTreeNode * @returns The function `getMinHeight` returns the minimum height of a binary tree. */ getMinHeight(beginRoot: BTNodeKeyOrNode = this.root, iterationType = this.iterationType): number { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return -1; if (iterationType === IterationType.RECURSIVE) { @@ -607,7 +607,7 @@ export class BinaryTree = BinaryTreeNode ): N[] { if ((!callback || callback === this._defaultOneParamCallback) && (identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; const ans: N[] = []; @@ -809,7 +809,7 @@ export class BinaryTree = BinaryTreeNode */ /** - * The function `ensureNotKey` returns the node corresponding to the given key if it is a valid node + * The function `ensureNode` returns the node corresponding to the given key if it is a valid node * key, otherwise it returns the key itself. * @param {BTNKey | N | null | undefined} key - The `key` parameter can be of type `BTNKey`, `N`, * `null`, or `undefined`. It represents a key used to identify a node in a binary tree. @@ -819,7 +819,7 @@ export class BinaryTree = BinaryTreeNode * @returns either the node corresponding to the given key if it is a valid node key, or the key * itself if it is not a valid node key. */ - ensureNotKey(key: BTNodeKeyOrNode, iterationType = IterationType.ITERATIVE): N | null | undefined { + ensureNode(key: BTNodeKeyOrNode, iterationType = IterationType.ITERATIVE): N | null | undefined { return this.isNodeKey(key) ? this.getNodeByKey(key, iterationType) : key; } @@ -916,7 +916,7 @@ export class BinaryTree = BinaryTreeNode getPathToRoot(beginRoot: BTNodeKeyOrNode, isReverse = true): N[] { // TODO to support get path through passing key const result: N[] = []; - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return result; @@ -953,7 +953,7 @@ export class BinaryTree = BinaryTreeNode beginRoot: BTNodeKeyOrNode = this.root, iterationType = this.iterationType ): N | null | undefined { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return beginRoot; @@ -1000,7 +1000,7 @@ export class BinaryTree = BinaryTreeNode iterationType = this.iterationType ): N | null | undefined { // TODO support get right most by passing key in - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return beginRoot; if (iterationType === IterationType.RECURSIVE) { @@ -1040,7 +1040,7 @@ export class BinaryTree = BinaryTreeNode */ isSubtreeBST(beginRoot: BTNodeKeyOrNode, iterationType = this.iterationType): boolean { // TODO there is a bug - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return true; if (iterationType === IterationType.RECURSIVE) { @@ -1144,7 +1144,7 @@ export class BinaryTree = BinaryTreeNode iterationType = this.iterationType, includeNull = false ): ReturnType[] { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); const ans: (ReturnType> | null | undefined)[] = []; if (!beginRoot) return ans; @@ -1281,7 +1281,7 @@ export class BinaryTree = BinaryTreeNode iterationType: IterationType = IterationType.ITERATIVE, includeNull = false ): ReturnType[] { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; const ans: ReturnType[] = []; if (iterationType === IterationType.RECURSIVE) { @@ -1422,7 +1422,7 @@ export class BinaryTree = BinaryTreeNode iterationType = this.iterationType, includeNull = false ): ReturnType[] { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; const ans: ReturnType>[] = []; @@ -1523,7 +1523,7 @@ export class BinaryTree = BinaryTreeNode iterationType = this.iterationType, includeNull = false ): ReturnType[][] { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); const levelsNodes: ReturnType[][] = []; if (!beginRoot) return levelsNodes; @@ -1578,7 +1578,7 @@ export class BinaryTree = BinaryTreeNode * @returns The function `getPredecessor` returns a value of type `N | undefined`. */ getPredecessor(node: BTNodeKeyOrNode): N | undefined { - node = this.ensureNotKey(node); + node = this.ensureNode(node); if (!this.isRealNode(node)) return undefined; if (node.left) { @@ -1601,7 +1601,7 @@ export class BinaryTree = BinaryTreeNode * after the given node in the inorder traversal of the binary tree. */ getSuccessor(x?: BTNKey | N | null): N | null | undefined { - x = this.ensureNotKey(x); + x = this.ensureNode(x); if (!x) return undefined; if (x.right) { @@ -1639,7 +1639,7 @@ export class BinaryTree = BinaryTreeNode pattern: DFSOrderPattern = 'in', beginRoot: BTNodeKeyOrNode = this.root ): ReturnType[] { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (beginRoot === null) return []; const ans: ReturnType>[] = []; @@ -1847,7 +1847,7 @@ export class BinaryTree = BinaryTreeNode */ print(beginRoot: BTNodeKeyOrNode = this.root, options?: BinaryTreePrintOptions): void { const opts = { isShowUndefined: false, isShowNull: false, isShowRedBlackNIL: false, ...options }; - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return; if (opts.isShowUndefined) console.log(`U for undefined @@ -1925,9 +1925,9 @@ export class BinaryTree = BinaryTreeNode * @param {N} destNode - The destination node to swap. * @returns {N} - The destination node after the swap. */ - protected _swap(srcNode: BTNodeKeyOrNode, destNode: BTNodeKeyOrNode): N | undefined { - srcNode = this.ensureNotKey(srcNode); - destNode = this.ensureNotKey(destNode); + protected _swapProperties(srcNode: BTNodeKeyOrNode, destNode: BTNodeKeyOrNode): N | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); if (srcNode && destNode) { const { key, value } = destNode; @@ -1946,6 +1946,24 @@ export class BinaryTree = BinaryTreeNode return undefined; } + protected _replaceNode(oldNode: N, newNode: N): N { + if (oldNode.parent) { + if (oldNode.parent.left === oldNode) { + oldNode.parent.left = newNode; + } else if (oldNode.parent.right === oldNode) { + oldNode.parent.right = newNode; + } + } + newNode.left = oldNode.left; + newNode.right = oldNode.right; + newNode.parent = oldNode.parent; + if (this.root === oldNode) { + this._root = newNode; + } + + return newNode; + } + /** * The function `_addTo` adds a new node to a binary tree if there is an available position. * @param {N | null | undefined} newNode - The `newNode` parameter represents the node that you want to add to diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index d805937..0599afb 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -11,10 +11,10 @@ import type { BSTNodeNested, BSTOptions, BTNCallback, - BTNodeExemplar, BTNKey, - Comparator, - BTNodePureExemplar + BTNodeExemplar, + BTNodePureExemplar, + Comparator } from '../../types'; import { CP, IterationType } from '../../types'; import { BinaryTree, BinaryTreeNode } from './binary-tree'; @@ -135,9 +135,10 @@ export class BST = BSTNode> * no node was inserted, it returns `undefined`. */ override add(keyOrNodeOrEntry: BTNodeExemplar): N | undefined { - if (keyOrNodeOrEntry === null) return undefined; - // TODO support node as a parameter - let inserted: N | undefined; + if (keyOrNodeOrEntry === null || keyOrNodeOrEntry === undefined) { + return undefined; + } + let newNode: N | undefined; if (keyOrNodeOrEntry instanceof BSTNode) { newNode = keyOrNodeOrEntry; @@ -149,64 +150,51 @@ export class BST = BSTNode> return; } else { newNode = this.createNode(key, value); - } } else { - newNode = undefined; + return; } + if (this.root === undefined) { this._setRoot(newNode); - this._size = this.size + 1; - inserted = this.root; - } else { - let cur = this.root; - let traversing = true; - while (traversing) { - if (cur !== undefined && newNode !== undefined) { - if (this._compare(cur.key, newNode.key) === CP.eq) { - if (newNode) { - cur.value = newNode.value; - } - //Duplicates are not accepted. - traversing = false; - inserted = cur; - } else if (this._compare(cur.key, newNode.key) === CP.gt) { - // Traverse left of the node - if (cur.left === undefined) { - if (newNode) { - newNode.parent = cur; - } - //Add to the left of the current node - cur.left = newNode; - this._size = this.size + 1; - traversing = false; - inserted = cur.left; - } else { - //Traverse the left of the current node - if (cur.left) cur = cur.left; - } - } else if (this._compare(cur.key, newNode.key) === CP.lt) { - // Traverse right of the node - if (cur.right === undefined) { - if (newNode) { - newNode.parent = cur; - } - //Add to the right of the current node - cur.right = newNode; - this._size = this.size + 1; - traversing = false; - inserted = cur.right; - } else { - //Traverse the left of the current node - if (cur.right) cur = cur.right; - } - } - } else { - traversing = false; + this._size++; + return this.root; + } + + let current = this.root; + while (current !== undefined) { + if (this._compare(current.key, newNode.key) === CP.eq) { + // if (current !== newNode) { + // The key value is the same but the reference is different, update the value of the existing node + this._replaceNode(current, newNode); + return newNode; + + // } else { + // The key value is the same and the reference is the same, replace the entire node + // this._replaceNode(current, newNode); + + // return; + // } + } else if (this._compare(current.key, newNode.key) === CP.gt) { + if (current.left === undefined) { + current.left = newNode; + newNode.parent = current; + this._size++; + return newNode; } + current = current.left; + } else { + if (current.right === undefined) { + current.right = newNode; + newNode.parent = current; + this._size++; + return newNode; + } + current = current.right; } } - return inserted; + + return undefined; } /** @@ -294,7 +282,7 @@ export class BST = BSTNode> const [l, r] = popped; if (l <= r) { const m = l + Math.floor((r - l) / 2); - const newNode = this.add(realBTNExemplars[m]); + const newNode = this.add(sorted[m]); inserted.push(newNode); stack.push([m + 1, r]); stack.push([l, m - 1]); @@ -387,7 +375,7 @@ export class BST = BSTNode> */ /** - * The function `ensureNotKey` returns the node corresponding to the given key if it is a node key, + * The function `ensureNode` returns the node corresponding to the given key if it is a node key, * otherwise it returns the key itself. * @param {BTNKey | N | undefined} key - The `key` parameter can be of type `BTNKey`, `N`, or * `undefined`. @@ -395,7 +383,7 @@ export class BST = BSTNode> * type of iteration to be performed. It has a default value of `IterationType.ITERATIVE`. * @returns either a node object (N) or undefined. */ - override ensureNotKey(key: BSTNodeKeyOrNode, iterationType = IterationType.ITERATIVE): N | undefined { + override ensureNode(key: BSTNodeKeyOrNode, iterationType = IterationType.ITERATIVE): N | undefined { return this.isNodeKey(key) ? this.getNodeByKey(key, iterationType) : key; } @@ -429,7 +417,7 @@ export class BST = BSTNode> beginRoot: BSTNodeKeyOrNode = this.root, iterationType = this.iterationType ): N[] { - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); if (!beginRoot) return []; const ans: N[] = []; @@ -510,7 +498,7 @@ export class BST = BSTNode> targetNode: BSTNodeKeyOrNode = this.root, iterationType = this.iterationType ): ReturnType[] { - targetNode = this.ensureNotKey(targetNode); + targetNode = this.ensureNode(targetNode); const ans: ReturnType>[] = []; if (!targetNode) return ans; if (!this.root) return ans; diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index ac11c78..dbebd42 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -10,8 +10,8 @@ import { BiTreeDeleteResult, BSTNodeKeyOrNode, BTNCallback, - BTNodeExemplar, BTNKey, + BTNodeExemplar, IterationType, RBTNColor, RBTreeOptions, @@ -126,6 +126,9 @@ export class RedBlackTree = RedBlackTr } else if (node.key > x.key) { x = x?.right; } else { + if (node !== x) { + this._replaceNode(x, node) + } return; } } @@ -295,7 +298,7 @@ export class RedBlackTree = RedBlackTr iterationType = this.iterationType ): N | null | undefined { if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; - beginRoot = this.ensureNotKey(beginRoot); + beginRoot = this.ensureNode(beginRoot); return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? undefined; } @@ -589,4 +592,10 @@ export class RedBlackTree = RedBlackTr } this.root.color = RBTNColor.BLACK; } + + protected _replaceNode(oldNode: N, newNode: N): N { + newNode.color = oldNode.color; + + return super._replaceNode(oldNode, newNode) + } } diff --git a/src/data-structures/binary-tree/tree-multimap.ts b/src/data-structures/binary-tree/tree-multimap.ts index a9e564d..6b03f0e 100644 --- a/src/data-structures/binary-tree/tree-multimap.ts +++ b/src/data-structures/binary-tree/tree-multimap.ts @@ -5,8 +5,14 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type { BSTNodeKeyOrNode, BTNodeExemplar, BTNKey, TreeMultimapNodeNested, TreeMultimapOptions } from '../../types'; -import { BiTreeDeleteResult, BTNCallback, CP, FamilyPosition, IterationType, TreeMultimapNested } from '../../types'; +import type { + BSTNodeKeyOrNode, + BTNKey, + BTNodeExemplar, + TreeMultimapNodeNested, + TreeMultimapOptions +} from '../../types'; +import { BiTreeDeleteResult, BTNCallback, FamilyPosition, IterationType, TreeMultimapNested } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { AVLTree, AVLTreeNode } from './avl-tree'; @@ -95,82 +101,28 @@ export class TreeMultimap = TreeMultim * @returns a node (`N`) or `undefined`. */ override add(keyOrNodeOrEntry: BTNodeExemplar, count = 1): N | undefined { - if (keyOrNodeOrEntry === null) return; - let inserted: N | undefined = undefined, - newNode: N | undefined; - if (keyOrNodeOrEntry instanceof TreeMultimapNode) { - newNode = this.createNode(keyOrNodeOrEntry.key, keyOrNodeOrEntry.value, keyOrNodeOrEntry.count); - } else if (keyOrNodeOrEntry === undefined) { + let newNode: N | undefined; + if (keyOrNodeOrEntry === undefined || keyOrNodeOrEntry === null) { return; + } else if (keyOrNodeOrEntry instanceof TreeMultimapNode) { + newNode = keyOrNodeOrEntry; + } else if (this.isNodeKey(keyOrNodeOrEntry)) { + newNode = this.createNode(keyOrNodeOrEntry, undefined, count); } else if (this.isEntry(keyOrNodeOrEntry)) { const [key, value] = keyOrNodeOrEntry; - if (key === null || key === undefined) { - return + if (key === undefined || key === null) { + return; } else { newNode = this.createNode(key, value, count); } - } else if (typeof keyOrNodeOrEntry === 'number') { - newNode = this.createNode(keyOrNodeOrEntry, undefined, 1); } else { - - return + return; } - - if (!this.root) { - this._setRoot(newNode); - this._size = this.size + 1; - if (newNode) this._count += newNode.count; - inserted = this.root; - } else { - let cur = this.root; - let traversing = true; - while (traversing) { - if (cur) { - if (newNode) { - if (this._compare(cur.key, newNode.key) === CP.eq) { - cur.value = newNode.value; - cur.count += newNode.count; - this._count += newNode.count; - traversing = false; - inserted = cur; - } else if (this._compare(cur.key, newNode.key) === CP.gt) { - // Traverse left of the node - if (cur.left === undefined) { - //Add to the left of the current node - cur.left = newNode; - this._size = this.size + 1; - this._count += newNode.count; - - traversing = false; - inserted = cur.left; - } else { - //Traverse the left of the current node - if (cur.left) cur = cur.left; - } - } else if (this._compare(cur.key, newNode.key) === CP.lt) { - // Traverse right of the node - if (cur.right === undefined) { - //Add to the right of the current node - cur.right = newNode; - this._size = this.size + 1; - this._count += newNode.count; - - traversing = false; - inserted = cur.right; - } else { - //Traverse the left of the current node - if (cur.right) cur = cur.right; - } - } - } else { - // TODO may need to support undefined inserted - } - } else { - traversing = false; - } - } + const orgNodeCount = newNode?.count || 0; + const inserted = super.add(newNode); + if (inserted) { + this._count += orgNodeCount; } - if (inserted) this._balancePath(inserted); return inserted; } @@ -193,23 +145,7 @@ export class TreeMultimap = TreeMultim * @returns The function `addMany` returns an array of nodes (`N`) or `undefined` values. */ override addMany(keysOrNodesOrEntries: Iterable>): (N | undefined)[] { - const inserted: (N | undefined)[] = []; - - for (const keyOrNode of keysOrNodesOrEntries) { - - if (keyOrNode instanceof TreeMultimapNode) { - inserted.push(this.add(keyOrNode, keyOrNode.count)); - continue; - } - - if (keyOrNode === undefined || keyOrNode === null) { - inserted.push(this.add(NaN, 0)); - continue; - } - - inserted.push(this.add(keyOrNode, 1)); - } - return inserted; + return super.addMany(keysOrNodesOrEntries); } /** @@ -325,7 +261,7 @@ export class TreeMultimap = TreeMultim const leftSubTreeRightMost = curr.left ? this.getRightMost(curr.left) : undefined; if (leftSubTreeRightMost) { const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; - orgCurrent = this._swap(curr, leftSubTreeRightMost); + orgCurrent = this._swapProperties(curr, leftSubTreeRightMost); if (parentOfLeftSubTreeMax) { if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) { parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; @@ -379,7 +315,7 @@ export class TreeMultimap = TreeMultim * added, or `undefined` if no node was added. */ protected override _addTo(newNode: N | undefined, parent: BSTNodeKeyOrNode): N | undefined { - parent = this.ensureNotKey(parent); + parent = this.ensureNode(parent); if (parent) { if (parent.left === undefined) { parent.left = newNode; @@ -405,7 +341,7 @@ export class TreeMultimap = TreeMultim } /** - * The `_swap` function swaps the key, value, count, and height properties between two nodes. + * The `_swapProperties` function swaps the key, value, count, and height properties between two nodes. * @param {BTNKey | N | undefined} srcNode - The `srcNode` parameter represents the source node from * which the values will be swapped. It can be of type `BTNKey`, `N`, or `undefined`. * @param {BTNKey | N | undefined} destNode - The `destNode` parameter represents the destination @@ -413,9 +349,9 @@ export class TreeMultimap = TreeMultim * @returns either the `destNode` object if both `srcNode` and `destNode` are defined, or `undefined` * if either `srcNode` or `destNode` is undefined. */ - protected _swap(srcNode: BSTNodeKeyOrNode, destNode: BSTNodeKeyOrNode): N | undefined { - srcNode = this.ensureNotKey(srcNode); - destNode = this.ensureNotKey(destNode); + protected override _swapProperties(srcNode: BSTNodeKeyOrNode, destNode: BSTNodeKeyOrNode): N | undefined { + srcNode = this.ensureNode(srcNode); + destNode = this.ensureNode(destNode); if (srcNode && destNode) { const { key, value, count, height } = destNode; const tempNode = this.createNode(key, value, count); @@ -437,4 +373,9 @@ export class TreeMultimap = TreeMultim } return undefined; } + + protected _replaceNode(oldNode: N, newNode: N): N { + newNode.count = oldNode.count + newNode.count + return super._replaceNode(oldNode, newNode); + } } diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index 5615c3b..f7c7e38 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -5,8 +5,8 @@ import { BinaryTreeOptions, BiTreeDeleteResult, BTNCallback, - BTNodeExemplar, BTNKey, + BTNodeExemplar, } from '../types'; export interface IBinaryTree = BinaryTreeNodeNested, TREE extends BinaryTree = BinaryTreeNested> { diff --git a/test/unit/data-structures/binary-tree/tree-multimap.test.ts b/test/unit/data-structures/binary-tree/tree-multimap.test.ts index 56d50ed..c34e0c0 100644 --- a/test/unit/data-structures/binary-tree/tree-multimap.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multimap.test.ts @@ -4,7 +4,17 @@ import { isDebugTest } from '../../../config'; const isDebug = isDebugTest; describe('TreeMultimap count', () => { - const tm = new TreeMultimap(); + let tm: TreeMultimap; + beforeEach(() => { + tm = new TreeMultimap(); + + }) + it('Should added isolated node count ', () => { + tm.addMany([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]); + const newNode = new TreeMultimapNode(3, 33, 10); + tm.add(newNode); + expect(tm.count).toBe(15) + }) it('Should count', () => { tm.addMany([[1, 1], [2, 2], [3, 3]]); @@ -13,13 +23,13 @@ describe('TreeMultimap count', () => { }) }) -describe('TreeMultimap operations test', () => { - it('should perform various operations on a Binary Search Tree with numeric values', () => { +describe('TreeMultimap operations test1', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { const treeMultimap = new TreeMultimap(); expect(treeMultimap instanceof TreeMultimap); - treeMultimap.add(11, 11); - treeMultimap.add(3, 3); + treeMultimap.add([11, 11]); + treeMultimap.add([3, 3]); const idAndValues: [number, number][] = [ [11, 11], [3, 3], @@ -48,8 +58,8 @@ describe('TreeMultimap operations test', () => { expect(treeMultimap.has(6)); - expect(treeMultimap.getHeight(6)).toBe(3); - expect(treeMultimap.getDepth(6)).toBe(1); + expect(treeMultimap.getHeight(6)).toBe(4); + expect(treeMultimap.getDepth(6)).toBe(0); const nodeId10 = treeMultimap.getNode(10); expect(nodeId10?.key).toBe(10); @@ -66,14 +76,14 @@ describe('TreeMultimap operations test', () => { const node15 = treeMultimap.getNode(15); const minNodeBySpecificNode = node15 && treeMultimap.getLeftMost(node15); - expect(minNodeBySpecificNode?.key).toBe(12); + expect(minNodeBySpecificNode?.key).toBe(15); let subTreeSum = 0; node15 && treeMultimap.subTreeTraverse((node: TreeMultimapNode) => (subTreeSum += node.key), 15); - expect(subTreeSum).toBe(70); + expect(subTreeSum).toBe(31); let lesserSum = 0; treeMultimap.lesserOrGreaterTraverse((node: TreeMultimapNode) => (lesserSum += node.key), CP.lt, 10); - expect(lesserSum).toBe(45); + expect(lesserSum).toBe(21); expect(node15 instanceof TreeMultimapNode); if (node15 instanceof TreeMultimapNode) { @@ -230,7 +240,7 @@ describe('TreeMultimap operations test', () => { expect(bfsNodes[1].key).toBe(2); expect(bfsNodes[2].key).toBe(16); - expect(treeMultimap.count).toBe(9); + expect(treeMultimap.count).toBe(4); }); it('should perform various operations on a Binary Search Tree with object values', () => { @@ -261,7 +271,7 @@ describe('TreeMultimap operations test', () => { expect(objTreeMultimap.root).toBeInstanceOf(TreeMultimapNode); - if (objTreeMultimap.root) expect(objTreeMultimap.root.key).toBe(11); + if (objTreeMultimap.root) expect(objTreeMultimap.root.key).toBe(6); expect(objTreeMultimap.count).toBe(16); @@ -269,13 +279,13 @@ describe('TreeMultimap operations test', () => { }); }); -describe('TreeMultimap operations test recursively', () => { - it('should perform various operations on a Binary Search Tree with numeric values', () => { +describe('TreeMultimap operations test recursively1', () => { + it('should perform various operations on a Binary Search Tree with numeric values1', () => { const treeMultimap = new TreeMultimap([], { iterationType: IterationType.RECURSIVE }); expect(treeMultimap instanceof TreeMultimap); - treeMultimap.add(11, 11); - treeMultimap.add(3, 3); + treeMultimap.add([11, 11]); + treeMultimap.add([3, 3]); const idAndValues: [number, number][] = [ [11, 11], [3, 3], @@ -297,15 +307,15 @@ describe('TreeMultimap operations test recursively', () => { treeMultimap.addMany(idAndValues); expect(treeMultimap.root).toBeInstanceOf(TreeMultimapNode); - if (treeMultimap.root) expect(treeMultimap.root.key == 11); + if (treeMultimap.root) expect(treeMultimap.root.key).toBe(6); expect(treeMultimap.size).toBe(16); expect(treeMultimap.count).toBe(18); expect(treeMultimap.has(6)); - expect(treeMultimap.getHeight(6)).toBe(3); - expect(treeMultimap.getDepth(6)).toBe(1); + expect(treeMultimap.getHeight(6)).toBe(4); + expect(treeMultimap.getDepth(6)).toBe(0); const nodeId10 = treeMultimap.getNode(10); expect(nodeId10?.key).toBe(10); @@ -322,14 +332,14 @@ describe('TreeMultimap operations test recursively', () => { const node15 = treeMultimap.getNode(15); const minNodeBySpecificNode = node15 && treeMultimap.getLeftMost(node15); - expect(minNodeBySpecificNode?.key).toBe(12); + expect(minNodeBySpecificNode?.key).toBe(15); let subTreeSum = 0; node15 && treeMultimap.subTreeTraverse((node: TreeMultimapNode) => (subTreeSum += node.key), 15); - expect(subTreeSum).toBe(70); + expect(subTreeSum).toBe(31); let lesserSum = 0; treeMultimap.lesserOrGreaterTraverse((node: TreeMultimapNode) => (lesserSum += node.key), CP.lt, 10); - expect(lesserSum).toBe(45); + expect(lesserSum).toBe(21); expect(node15 instanceof TreeMultimapNode); if (node15 instanceof TreeMultimapNode) { @@ -346,7 +356,7 @@ describe('TreeMultimap operations test recursively', () => { const dfsInorderNodes = treeMultimap.dfs(node => node, 'in'); expect(dfsInorderNodes[0].key).toBe(1); expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); - expect(treeMultimap.isPerfectlyBalanced()).toBe(false); + expect(treeMultimap.isPerfectlyBalanced()).toBe(true); treeMultimap.perfectlyBalance(); @@ -486,7 +496,7 @@ describe('TreeMultimap operations test recursively', () => { expect(bfsNodes[1].key).toBe(2); expect(bfsNodes[2].key).toBe(16); - expect(treeMultimap.count).toBe(9); + expect(treeMultimap.count).toBe(4); }); it('should perform various operations on a Binary Search Tree with object values', () => { @@ -517,7 +527,7 @@ describe('TreeMultimap operations test recursively', () => { expect(objTreeMultimap.root).toBeInstanceOf(TreeMultimapNode); - if (objTreeMultimap.root) expect(objTreeMultimap.root.key).toBe(11); + if (objTreeMultimap.root) expect(objTreeMultimap.root.key).toBe(6); expect(objTreeMultimap.count).toBe(16);