From d29ff07f403db627fc7dc4cccd3cab236ede8e4a Mon Sep 17 00:00:00 2001 From: Revone Date: Sat, 26 Aug 2023 10:24:31 +0800 Subject: [PATCH] Swap the positions of val and id to enhance the intuitiveness of the API. Revise the design of familyPosition in AbstractBinaryTree such that the previous assignment approach is replaced with a real-time retrieval method. Standardize the BST.remove method with the AbstractBinaryTree.remove method. Eliminate the redundant attribute name from TreeNode. --- .idea/compiler.xml | 1 - .npmignore | 5 +- package-lock.json | 14 +- package.json | 2 +- .../binary-tree/abstract-binary-tree.ts | 264 ++++++++++-------- src/data-structures/binary-tree/avl-tree.ts | 14 +- .../binary-tree/binary-tree.ts | 12 +- src/data-structures/binary-tree/bst.ts | 78 +----- src/data-structures/binary-tree/rb-tree.ts | 18 +- .../binary-tree/tree-multiset.ts | 10 +- .../interfaces/abstract-binary-tree.ts | 13 +- src/data-structures/interfaces/bst.ts | 4 +- src/data-structures/interfaces/rb-tree.ts | 4 +- src/data-structures/tree/tree.ts | 13 +- .../types/abstract-binary-tree.ts | 12 +- src/data-structures/types/helpers.ts | 3 +- src/data-structures/types/tree-multiset.ts | 4 +- src/utils/types/utils.ts | 27 ++ src/utils/utils.ts | 33 ++- .../data-structures/binary-tree/bst.test.ts | 140 +++++----- 20 files changed, 345 insertions(+), 326 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d695009..6ff0bf9 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,6 +2,5 @@ \ No newline at end of file diff --git a/.npmignore b/.npmignore index 64d29af..06a8d71 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,7 @@ /.idea /src -/tests \ No newline at end of file +/tests +/notes +/docs +/backup \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 19fcff7..e08f5a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-structure-typed", - "version": "1.18.6", + "version": "1.18.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.18.6", + "version": "1.18.7", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.3", @@ -15,7 +15,7 @@ "jest": "^29.6.2", "ts-jest": "^29.1.1", "typedoc": "^0.24.8", - "typescript": "^5.1.5" + "typescript": "^4.9.5" } }, "node_modules/@ampproject/remapping": { @@ -5409,16 +5409,16 @@ } }, "node_modules/typescript": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.5.tgz", - "integrity": "sha512-FOH+WN/DQjUvN6WgW+c4Ml3yi0PH+a/8q+kNIfRehv1wLhWONedw85iu+vQ39Wp49IzTJEsZ2lyLXpBF7mkF1g==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/update-browserslist-db": { diff --git a/package.json b/package.json index c4025e7..92ecf8c 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,6 @@ "jest": "^29.6.2", "ts-jest": "^29.1.1", "typedoc": "^0.24.8", - "typescript": "^5.1.5" + "typescript": "^4.9.5" } } diff --git a/src/data-structures/binary-tree/abstract-binary-tree.ts b/src/data-structures/binary-tree/abstract-binary-tree.ts index 25956eb..1fc5e34 100644 --- a/src/data-structures/binary-tree/abstract-binary-tree.ts +++ b/src/data-structures/binary-tree/abstract-binary-tree.ts @@ -6,16 +6,15 @@ * @license MIT License */ -import {trampoline} from '../../utils'; +import {ObjectWithNumberId, trampoline} from '../../utils'; import type { + AbstractBinaryTreeNodeNested, AbstractBinaryTreeNodeProperties, AbstractBinaryTreeNodeProperty, - AbstractBinaryTreeNodeNested, BinaryTreeDeletedResult, BinaryTreeNodeId, BinaryTreeNodePropertyName, DFSOrderPattern, - KeyValObject, NodeOrPropertyName } from '../types'; import {AbstractBinaryTreeOptions, FamilyPosition, LoopType} from '../types'; @@ -32,7 +31,7 @@ export abstract class AbstractBinaryTreeNode { @@ -304,7 +320,7 @@ export abstract class AbstractBinaryTree): (N | null | undefined)[] { + // TODO not sure addMany not be run multi times const inserted: (N | null | undefined)[] = []; const map: Map = new Map(); - if (!this._isDuplicatedVal) { - for (const i of data) map.set(i, (map.get(i) ?? 0) + 1); + if (this.isMergeDuplicatedVal) { + for (const nodeOrVal of data) map.set(nodeOrVal, (map.get(nodeOrVal) ?? 0) + 1); } - for (const item of data) { - // TODO will this cause an issue? - const count = this._isDuplicatedVal ? 1 : map.get(item); + for (const nodeOrVal of data) { - if (item instanceof AbstractBinaryTreeNode) { - inserted.push(this.add(item.id, item.val, item.count)); - } else if (typeof item === 'number') { - if (!this._autoIncrementId) { - if (!this._isDuplicatedVal) { - if (map.get(item) !== undefined) { - inserted.push(this.add(item, item, count)); - map.delete(item); - } - } else { - inserted.push(this.add(item, item, 1)); - } - } - - } else if (item instanceof Object) { - if (!this._isDuplicatedVal) { - if (map.has(item)) { - let newId: number; - if (!this._autoIncrementId) { - if (Object.keys(item).includes('id')) { - newId = (item as KeyValObject).id; - } else { - console.warn('Object value must has an id property when the autoIncrementId is false'); - break; - } - } else { - newId = this.maxId + 1; - this._setMaxId(newId); - } - inserted.push(this.add(newId, item, count)); - map.delete(item); - } - } else { - inserted.push(this.add(++this._maxId, item, 1)); - } - } else if (item === null) { - inserted.push(this.add(Number.MAX_SAFE_INTEGER, item, 0)); + if (nodeOrVal instanceof AbstractBinaryTreeNode) { + inserted.push(this.add(nodeOrVal.val, nodeOrVal.id, nodeOrVal.count)); + continue; } + + if (nodeOrVal === null) { + inserted.push(this.add(null, NaN, 0)); + continue; + } + + + // TODO will this cause an issue? + const count = this.isMergeDuplicatedVal ? map.get(nodeOrVal) : 1; + let newId: BinaryTreeNodeId; + if (typeof nodeOrVal === 'number') { + newId = this.autoIncrementId ? this.maxId + 1 : nodeOrVal; + } else if (nodeOrVal instanceof Object) { + if (this.autoIncrementId) { + newId = this.maxId + 1; + } else { + if (Object.keys(nodeOrVal).includes('id')) { + newId = (nodeOrVal as ObjectWithNumberId).id; + } else { + console.warn(nodeOrVal, 'Object value must has an id property when the autoIncrementId is false'); + continue; + } + } + } else { + console.warn(nodeOrVal, ` is not added`); + continue; + } + + if (this.isMergeDuplicatedVal) { + if (map.has(nodeOrVal)) { + inserted.push(this.add(nodeOrVal, newId, count)); + map.delete(nodeOrVal); + } + } else { + inserted.push(this.add(nodeOrVal, newId, 1)); + } + + this._setMaxId(newId); } return inserted; } @@ -455,39 +472,49 @@ export abstract class AbstractBinaryTree[] { - const nodes = this.getNodes(id, 'id', true); - let node: N | null | undefined = nodes[0]; + const bstDeletedResult: BinaryTreeDeletedResult[] = []; + if (!this.root) return bstDeletedResult; - if (!node) node = undefined; - else if (node.count > 1 && !ignoreCount) { - node.count--; + const curr: N | null = this.get(id); + if (!curr) return bstDeletedResult; + + const parent: N | null = curr?.parent ? curr.parent : null; + let needBalanced: N | null = null, orgCurrent = curr; + + if (curr.count > 1 && !ignoreCount) { + curr.count--; this._setCount(this.count - 1); - } else if (node instanceof AbstractBinaryTreeNode) { - const [subSize, subCount] = this.getSubTreeSizeAndCount(node); - - switch (node.familyPosition) { - case 0: - this._setSize(this.size - subSize); - this._setCount(this.count - subCount); - node = undefined; - break; - case 1: - if (node.parent) { - this._setSize(this.size - subSize); - this._setCount(this.count - subCount); - node.parent.left = null; + } else { + if (!curr.left) { + if (!parent) { + if (curr.right !== undefined) this._setRoot(curr.right); + } else { + const {familyPosition: fp} = curr; + if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) { + parent.left = curr.right; + } else if (fp === FamilyPosition.RIGHT || fp === FamilyPosition.ROOT_RIGHT) { + parent.right = curr.right; } - break; - case 2: - if (node.parent) { - this._setSize(this.size - subSize); - this._setCount(this.count - subCount); - node.parent.right = null; + needBalanced = parent; + } + } else { + const leftSubTreeRightMost = curr.left ? this.getRightMost(curr.left) : null; + if (leftSubTreeRightMost) { + const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent; + orgCurrent = curr.swapLocation(leftSubTreeRightMost); + if (parentOfLeftSubTreeMax) { + if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left; + else parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left; + needBalanced = parentOfLeftSubTreeMax; } - break; + } } + this._setSize(this.size - 1); + this._setCount(this.count - orgCurrent.count); } - return [{deleted: node, needBalanced: null}]; + + bstDeletedResult.push({deleted: orgCurrent, needBalanced}); + return bstDeletedResult; } /** @@ -776,7 +803,7 @@ export abstract class AbstractBinaryTree = AVLTreeNodeNested> extends BSTNode implements IAVLTreeNode { - override createNode(id: BinaryTreeNodeId, val?: T, count?: number): FAMILY { - return new AVLTreeNode(id, (val === undefined ? id : val) as T, count) as FAMILY; + override createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY { + return new AVLTreeNode(val, id, count) as FAMILY; } } @@ -20,8 +20,8 @@ export class AVLTree = AVLTreeNode> extends B super(options); } - override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N { - return new AVLTreeNode(id, (val === undefined ? id : val), count) as N; + override createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N { + return new AVLTreeNode(val, id, count) as N; } /** @@ -36,8 +36,8 @@ export class AVLTree = AVLTreeNode> extends B * to `1`, indicating that the value should be inserted once. * @returns The method is returning either an N object or null. */ - override add(id: BinaryTreeNodeId, val: N['val'] | null, count?: number): N | null { - const inserted = super.add(id, val, count); + override add(val: N['val'], id: BinaryTreeNodeId, count?: number): N | null { + const inserted = super.add(val, id, count); if (inserted) this.balancePath(inserted); return inserted; } diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 3aa34a3..ba4b271 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -23,8 +23,8 @@ export class BinaryTreeNode = * appears in the binary tree node. * @returns a new instance of the BinaryTreeNode class, casted as the FAMILY type. */ - createNode(id: BinaryTreeNodeId, val?: T, count?: number): FAMILY { - return new BinaryTreeNode(id, (val === undefined ? id : val) as T, count) as FAMILY; + createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY { + return new BinaryTreeNode(val, id, count) as FAMILY; } } @@ -33,11 +33,11 @@ export class BinaryTree = BinaryTreeNode> /** * The constructor function accepts an optional options object and sets the values of loopType, autoIncrementId, and - * isDuplicatedVal based on the provided options. + * isMergeDuplicatedVal based on the provided options. * @param [options] - An optional object that can contain the following properties: */ constructor(options?: BinaryTreeOptions) { - super(); + super(options); } @@ -52,8 +52,8 @@ export class BinaryTree = BinaryTreeNode> * of occurrences of the value in the binary tree node. If not provided, the default value is `undefined`. * @returns a BinaryTreeNode object if the value is not null, otherwise it returns null. */ - createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N { - return new BinaryTreeNode(id, val === undefined ? id : val, count) as N; + createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N { + return new BinaryTreeNode(val, id, count) as N; } } \ No newline at end of file diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index d3dd827..fc570b1 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -6,7 +6,7 @@ * @license MIT License */ import type {BinaryTreeNodeId, BinaryTreeNodePropertyName, BSTComparator, BSTNodeNested} from '../types'; -import {BinaryTreeDeletedResult, BSTOptions, CP, FamilyPosition, LoopType} from '../types'; +import {BSTOptions, CP, LoopType} from '../types'; import {BinaryTree, BinaryTreeNode} from './binary-tree'; import {IBST, IBSTNode} from '../interfaces'; @@ -21,8 +21,8 @@ export class BSTNode = BSTNodeNested< * search tree node. It is an optional parameter, so it can be omitted when calling the `createNode` method. * @returns The method is returning a new instance of the BSTNode class, casted as the FAMILY type. */ - override createNode(id: BinaryTreeNodeId, val?: T, count?: number): FAMILY { - return new BSTNode(id, (val === undefined ? id : val) as T, count) as FAMILY; + override createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY { + return new BSTNode(val, id, count) as FAMILY; } } @@ -51,8 +51,8 @@ export class BST = BSTNode> extends BinaryTree * of a particular value in the binary search tree node. * @returns a new instance of the BSTNode class, casted as type N. */ - override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N { - return new BSTNode(id, val === undefined ? id : val, count) as N; + override createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N { + return new BSTNode(val, id, count) as N; } /** @@ -67,9 +67,9 @@ export class BST = BSTNode> extends BinaryTree * inserted once. * @returns The method `add` returns a `N` object or `null`. */ - override add(id: BinaryTreeNodeId, val: N['val'] | null, count: number = 1): N | null { + override add(val: N['val'], id: BinaryTreeNodeId, count: number = 1): N | null { let inserted: N | null = null; - const newNode = this.createNode(id, val, count); + const newNode = this.createNode(val, id, count); if (this.root === null) { this._setRoot(newNode); this._setSize(this.size + 1); @@ -94,7 +94,6 @@ export class BST = BSTNode> extends BinaryTree if (cur.left === undefined) { if (newNode) { newNode.parent = cur; - newNode.familyPosition = FamilyPosition.LEFT; } //Add to the left of the current node cur.left = newNode; @@ -111,7 +110,6 @@ export class BST = BSTNode> extends BinaryTree if (cur.right === undefined) { if (newNode) { newNode.parent = cur; - newNode.familyPosition = FamilyPosition.RIGHT; } //Add to the right of the current node cur.right = newNode; @@ -160,64 +158,6 @@ export class BST = BSTNode> extends BinaryTree else return this.getRightMost()?.id ?? 0; } - /** - * The `remove` function in this TypeScript code removes a node from a binary search tree and returns information about - * the deleted node and any nodes that need to be balanced. - * @param {BinaryTreeNodeId} id - The `id` parameter is the identifier of the binary tree node that needs to be removed - * from the binary search tree. - * @param {boolean} [ignoreCount] - A boolean flag indicating whether to ignore the count of the node being removed. If - * set to true, the count of the node will not be considered and the node will be removed regardless of its count. If - * set to false or not provided, the count of the node will be taken into account and the - * @returns an array of `BSTDeletedResult` objects. - */ - override remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeletedResult[] { - const bstDeletedResult: BinaryTreeDeletedResult[] = []; - if (!this.root) return bstDeletedResult; - - const curr: N | null = this.get(id); - if (!curr) return bstDeletedResult; - - const parent: N | null = curr?.parent ? curr.parent : null; - let needBalanced: N | null = null, orgCurrent = curr; - - if (curr.count > 1 && !ignoreCount) { - curr.count--; - this._setCount(this.count - 1); - } else { - if (!curr.left) { - if (!parent) { - if (curr.right !== undefined) this._setRoot(curr.right); - } else { - switch (curr.familyPosition) { - case FamilyPosition.LEFT: - parent.left = curr.right; - break; - case FamilyPosition.RIGHT: - parent.right = curr.right; - break; - } - needBalanced = parent; - } - } else { - const leftSubTreeMax = curr.left ? this.getRightMost(curr.left) : null; - if (leftSubTreeMax) { - const parentOfLeftSubTreeMax = leftSubTreeMax.parent; - orgCurrent = curr.swapLocation(leftSubTreeMax); - if (parentOfLeftSubTreeMax) { - if (parentOfLeftSubTreeMax.right === leftSubTreeMax) parentOfLeftSubTreeMax.right = leftSubTreeMax.left; - else parentOfLeftSubTreeMax.left = leftSubTreeMax.left; - needBalanced = parentOfLeftSubTreeMax; - } - } - } - this._setSize(this.size - 1); - this._setCount(this.count - curr.count); - } - - bstDeletedResult.push({deleted: orgCurrent, needBalanced}); - return bstDeletedResult; - } - /** * The function `getNodes` returns an array of binary search tree nodes that match a given property value, with the * option to specify the property name and whether to return only one node. @@ -420,7 +360,7 @@ export class BST = BSTNode> extends BinaryTree if (l > r) return; const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add(midNode.id, midNode.val, midNode.count); + this.add(midNode.val, midNode.id, midNode.count); buildBalanceBST(l, m - 1); buildBalanceBST(m + 1, r); }; @@ -436,7 +376,7 @@ export class BST = BSTNode> extends BinaryTree if (l <= r) { const m = l + Math.floor((r - l) / 2); const midNode = sorted[m]; - this.add(midNode.id, midNode.val, midNode.count); + this.add(midNode.val, midNode.id, midNode.count); stack.push([m + 1, r]); stack.push([l, m - 1]); } diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 2870c31..e76c7f4 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,11 +1,11 @@ -import {BinaryTreeNodeId, RBColor, RBTreeOptions, RBTreeNodeNested} from '../types'; +import {BinaryTreeNodeId, RBColor, RBTreeNodeNested, RBTreeOptions} from '../types'; import {IRBTree, IRBTreeNode} from '../interfaces/rb-tree'; import {BST, BSTNode} from './bst'; -export class RBTreeNode =RBTreeNodeNested> extends BSTNode implements IRBTreeNode { - constructor(id: number, val: T, count?: number) { - super(id, val, count); +export class RBTreeNode = RBTreeNodeNested> extends BSTNode implements IRBTreeNode { + constructor(val: T, id: BinaryTreeNodeId, count?: number) { + super(val, id, count); } private _color: RBColor = RBColor.RED; @@ -28,8 +28,8 @@ export class RBTreeNode =RBTreeNod * node. * @returns The method is returning a new instance of the RBTreeNode class, casted as a FAMILY type. */ - override createNode(id: BinaryTreeNodeId, val?: T | null, count?: number): FAMILY { - return new RBTreeNode(id, val, count) as FAMILY; + override createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY { + return new RBTreeNode(val, id, count) as FAMILY; } // private override _parent: RBNode | null; @@ -48,7 +48,6 @@ export class RBTreeNode =RBTreeNod // override set left(v: RBNode | null | undefined) { // if (v) { // v.parent = this; - // v.familyPosition = FamilyPosition.LEFT; // } // this._left = v; // } @@ -62,7 +61,6 @@ export class RBTreeNode =RBTreeNod // override set right(v: RBNode | null | undefined) { // if (v) { // v.parent = this; - // v.familyPosition = FamilyPosition.RIGHT; // } // this._right = v; // } @@ -73,8 +71,8 @@ export class RBTree = RBTreeNode> extends BST< super(options); } - override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N { - return new RBTreeNode(id, val, count) as N; + override createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N { + return new RBTreeNode(val, id, count) as N; } // private override _root: BinaryTreeNode | null = null; diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index 9d15067..cc8626c 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -20,8 +20,8 @@ export class TreeMultiSetNode = TreeMultiSetNode> extends AVLTree implements ITreeMultiSet { constructor(options?: TreeMultiSetOptions) { - super({...options, isDuplicatedVal: true}); + super({...options, isMergeDuplicatedVal: true}); } /** @@ -42,7 +42,7 @@ export class TreeMultiSet = TreeMultiSet * occurrences of the value in the binary search tree node. If not provided, the count will default to 1. * @returns A new instance of the BSTNode class with the specified id, value, and count (if provided). */ - override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N { - return new TreeMultiSetNode(id, val === undefined ? id : val, count) as N; + override createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N { + return new TreeMultiSetNode(val, id, count) as N; } } diff --git a/src/data-structures/interfaces/abstract-binary-tree.ts b/src/data-structures/interfaces/abstract-binary-tree.ts index 8f88275..21c3a47 100644 --- a/src/data-structures/interfaces/abstract-binary-tree.ts +++ b/src/data-structures/interfaces/abstract-binary-tree.ts @@ -4,8 +4,7 @@ import { BinaryTreeDeletedResult, BinaryTreeNodeId, BinaryTreeNodePropertyName, - DFSOrderPattern, - FamilyPosition, + DFSOrderPattern, FamilyPosition, LoopType, NodeOrPropertyName } from '../types'; @@ -13,7 +12,7 @@ import {AbstractBinaryTreeNode} from '../binary-tree'; export interface IAbstractBinaryTreeNode> { - createNode(id: BinaryTreeNodeId, val?: T, count?: number): FAMILY; + createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY; get id(): BinaryTreeNodeId @@ -37,8 +36,6 @@ export interface IAbstractBinaryTreeNode> { - createNode(id: BinaryTreeNodeId, val: N['val'] | null, count?: number): N | null + createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N | null get loopType(): LoopType @@ -71,7 +68,7 @@ export interface IAbstractBinaryTree> extends IBinaryTreeNode { - createNode(id: BinaryTreeNodeId, val?: T, count?: number): FAMILY + createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY } export interface IBST> extends IBinaryTree { - createNode(id: BinaryTreeNodeId, val?: N['val'] | null, count?: number): N + createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N add(id: BinaryTreeNodeId, val: N['val'] | null, count: number): N | null diff --git a/src/data-structures/interfaces/rb-tree.ts b/src/data-structures/interfaces/rb-tree.ts index 72e2a95..848eb3d 100644 --- a/src/data-structures/interfaces/rb-tree.ts +++ b/src/data-structures/interfaces/rb-tree.ts @@ -3,10 +3,10 @@ import {IBST, IBSTNode} from './bst'; import {BinaryTreeNodeId} from '../types'; export interface IRBTreeNode> extends IBSTNode { - createNode(id: BinaryTreeNodeId, val?: T | null, count?: number): FAMILY + createNode(val: T, id: BinaryTreeNodeId, count?: number): FAMILY } export interface IRBTree> extends IBST { - createNode(id: BinaryTreeNodeId, val?: N | null, count?: number): N + createNode(val: N['val'], id: BinaryTreeNodeId, count?: number): N } \ No newline at end of file diff --git a/src/data-structures/tree/tree.ts b/src/data-structures/tree/tree.ts index d42b472..72ecfda 100644 --- a/src/data-structures/tree/tree.ts +++ b/src/data-structures/tree/tree.ts @@ -1,7 +1,6 @@ export class TreeNode { - constructor(id: string, name?: string, value?: T, children?: TreeNode[]) { + constructor(id: string, value?: T, children?: TreeNode[]) { this._id = id; - this._name = name || ''; this._value = value || undefined; this._children = children || []; } @@ -16,16 +15,6 @@ export class TreeNode { this._id = value; } - private _name?: string | undefined; - - get name(): string | undefined { - return this._name; - } - - set name(value: string | undefined) { - this._name = value; - } - private _value?: T | undefined; get value(): T | undefined { diff --git a/src/data-structures/types/abstract-binary-tree.ts b/src/data-structures/types/abstract-binary-tree.ts index 604dd98..00ed7da 100644 --- a/src/data-structures/types/abstract-binary-tree.ts +++ b/src/data-structures/types/abstract-binary-tree.ts @@ -9,7 +9,15 @@ import {AbstractBinaryTreeNode} from '../binary-tree'; export enum LoopType { ITERATIVE = 'ITERATIVE', RECURSIVE = 'RECURSIVE'} /* This enumeration defines the position of a node within a family tree composed of three associated nodes, where 'root' represents the root node of the family tree, 'left' represents the left child node, and 'right' represents the right child node. */ -export enum FamilyPosition {ROOT, LEFT, RIGHT} +export enum FamilyPosition { + ROOT = 'ROOT', + LEFT = 'LEFT', + RIGHT = 'RIGHT', + ROOT_LEFT = 'ROOT_LEFT', + ROOT_RIGHT = 'ROOT_RIGHT', + ISOLATED = 'ISOLATED', + MAL_NODE = 'MAL_NODE' +} export type BinaryTreeNodePropertyName = 'id' | 'val' | 'count'; export type NodeOrPropertyName = 'node' | BinaryTreeNodePropertyName; @@ -27,5 +35,5 @@ export type AbstractBinaryTreeNodeNested = AbstractBinaryTreeNode = TreeMultiSetNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type TreeMultiSetOptions = Omit & { - isDuplicatedVal: true, +export type TreeMultiSetOptions = Omit & { + isMergeDuplicatedVal: true, } diff --git a/src/utils/types/utils.ts b/src/utils/types/utils.ts index dc1f129..78403d6 100644 --- a/src/utils/types/utils.ts +++ b/src/utils/types/utils.ts @@ -4,3 +4,30 @@ export type TrlFn = (...args: any[]) => any; export type TrlAsyncFn = (...args: any[]) => any; export type SpecifyOptional = Omit & Partial>; + +export type KeyValueObject = { [key: string]: any }; + +export type KeyValueObjectWithId = { [key: string]: any, id: string | number | symbol }; + +export type NonNumberNonObjectButDefined = string | boolean | symbol | null; + +// export type ObjectWithoutId = Omit; +export type ObjectWithoutId = Omit; + +// export type ObjectWithNonNumberId = object & { +// id: string | boolean | symbol | null | object | undefined; +// } +export type ObjectWithNonNumberId = { + [key: string]: any, + id: string | boolean | symbol | null | object | undefined; +} + +// export type ObjectWithNumberId = object & { +// id: number; +// } +export type ObjectWithNumberId = { + [key: string]: any, + id: number; +} + +export type DummyAny = string | number | boolean | null | undefined | object | symbol | void | Function | never; \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 6e2e71f..a28334f 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -29,7 +29,16 @@ export const arrayRemove = function (array: T[], predicate: (item: T, index: * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type {Thunk, ToThunkFn, TrlAsyncFn, TrlFn} from './types'; +import type { + NonNumberNonObjectButDefined, + ObjectWithNonNumberId, + ObjectWithNumberId, + ObjectWithoutId, + Thunk, + ToThunkFn, + TrlAsyncFn, + TrlFn +} from './types'; export const THUNK_SYMBOL = Symbol('thunk') @@ -138,4 +147,24 @@ export const trampolineAsync = (fn: TrlAsyncFn) => { // super.delete(key); // } // } -// } \ No newline at end of file +// } + +export function isNonNumberNonObjectButDefined(val: any): val is NonNumberNonObjectButDefined { + return typeof val !== 'number' && typeof val !== 'object' && val !== undefined; +} + +export function isObjectWithoutId(val: any): val is ObjectWithoutId { + return typeof val === 'object' && !('id' in val); +} + +export function isObjectWithNonNumberId(val: any): val is ObjectWithNonNumberId { + return typeof val === 'object' && 'id' in val && typeof val.id !== 'number'; +} + +export function isObjectWithNumberId(val: any): val is ObjectWithNumberId { + return typeof val === 'object' && 'id' in val && typeof val.id === 'number'; +} + +export function isNumber(val: any): val is number { + return typeof val === 'number'; +} \ No newline at end of file diff --git a/tests/unit/data-structures/binary-tree/bst.test.ts b/tests/unit/data-structures/binary-tree/bst.test.ts index 0f43e24..adb522f 100644 --- a/tests/unit/data-structures/binary-tree/bst.test.ts +++ b/tests/unit/data-structures/binary-tree/bst.test.ts @@ -2,193 +2,195 @@ import {BST, BSTNode} from '../../../../src'; describe('BST operations test', () => { it('should perform various operations on a Binary Search Tree with numeric values', () => { - const tree = new BST(); - expect(tree).toBeInstanceOf(BST); + const bst = new BST(); + expect(bst).toBeInstanceOf(BST); const values = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; - tree.addMany(values); - expect(tree.root).toBeInstanceOf(BSTNode); + bst.addMany(values); + expect(bst.root).toBeInstanceOf(BSTNode); - if (tree.root) expect(tree.root.id).toBe(11); + if (bst.root) expect(bst.root.id).toBe(11); - expect(tree.count).toBe(16); + expect(bst.count).toBe(16); - expect(tree.has(6)).toBe(true); + expect(bst.has(6)).toBe(true); - const node6 = tree.get(6); - expect(node6 && tree.getHeight(node6)).toBe(2); - expect(node6 && tree.getDepth(node6)).toBe(3); + const node6 = bst.get(6); + expect(node6 && bst.getHeight(node6)).toBe(2); + expect(node6 && bst.getDepth(node6)).toBe(3); - const nodeId10 = tree.get(10, 'id'); + const nodeId10 = bst.get(10, 'id'); expect(nodeId10?.id).toBe(10); - const nodeVal9 = tree.get(9, 'val'); + const nodeVal9 = bst.get(9, 'val'); expect(nodeVal9?.id).toBe(9); - const nodesByCount1 = tree.getNodes(1, 'count'); + const nodesByCount1 = bst.getNodes(1, 'count'); expect(nodesByCount1.length).toBe(16); - const leftMost = tree.getLeftMost(); + const leftMost = bst.getLeftMost(); expect(leftMost?.id).toBe(1); - const node15 = tree.get(15); - const minNodeBySpecificNode = node15 && tree.getLeftMost(node15); + const node15 = bst.get(15); + const minNodeBySpecificNode = node15 && bst.getLeftMost(node15); expect(minNodeBySpecificNode?.id).toBe(12); - const subTreeSum = node15 && tree.subTreeSum(node15); + const subTreeSum = node15 && bst.subTreeSum(node15); expect(subTreeSum).toBe(70); - const lesserSum = tree.lesserSum(10); + const lesserSum = bst.lesserSum(10); expect(lesserSum).toBe(45); expect(node15).toBeInstanceOf(BSTNode); if (node15 instanceof BSTNode) { - const subTreeAdd = tree.subTreeAdd(node15, 1, 'count'); + const subTreeAdd = bst.subTreeAdd(node15, 1, 'count'); expect(subTreeAdd).toBeDefined(); } - const node11 = tree.get(11); + const node11 = bst.get(11); expect(node11).toBeInstanceOf(BSTNode); if (node11 instanceof BSTNode) { - const allGreaterNodesAdded = tree.allGreaterNodesAdd(node11, 2, 'count'); + const allGreaterNodesAdded = bst.allGreaterNodesAdd(node11, 2, 'count'); expect(allGreaterNodesAdded).toBeDefined(); } - const dfsInorderNodes = tree.DFS('in', 'node'); + const dfsInorderNodes = bst.DFS('in', 'node'); expect(dfsInorderNodes[0].id).toBe(1); expect(dfsInorderNodes[dfsInorderNodes.length - 1].id).toBe(16); - tree.balance(); - expect(tree.isBalanced()).toBe(true); + bst.balance(); + expect(bst.isBalanced()).toBe(true); - const bfsNodesAfterBalanced = tree.BFS('node'); + const bfsNodesAfterBalanced = bst.BFS('node'); expect(bfsNodesAfterBalanced[0].id).toBe(8); expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].id).toBe(16); - const removed11 = tree.remove(11, true); + const removed11 = bst.remove(11, true); expect(removed11).toBeInstanceOf(Array); expect(removed11[0]).toBeDefined(); expect(removed11[0].deleted).toBeDefined(); if (removed11[0].deleted) expect(removed11[0].deleted.id).toBe(11); - expect(tree.isAVLBalanced()).toBe(true); + expect(bst.isAVLBalanced()).toBe(true); - expect(node15 && tree.getHeight(node15)).toBe(2); + expect(node15 && bst.getHeight(node15)).toBe(2); - const removed1 = tree.remove(1, true); + const removed1 = bst.remove(1, true); expect(removed1).toBeInstanceOf(Array); expect(removed1[0]).toBeDefined(); expect(removed1[0].deleted).toBeDefined(); if (removed1[0].deleted) expect(removed1[0].deleted.id).toBe(1); - expect(tree.isAVLBalanced()).toBe(true); + expect(bst.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(4); + expect(bst.getHeight()).toBe(4); - const removed4 = tree.remove(4, true); + const removed4 = bst.remove(4, true); expect(removed4).toBeInstanceOf(Array); expect(removed4[0]).toBeDefined(); expect(removed4[0].deleted).toBeDefined(); if (removed4[0].deleted) expect(removed4[0].deleted.id).toBe(4); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(4); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(4); - const removed10 = tree.remove(10, true); + const removed10 = bst.remove(10, true); expect(removed10).toBeInstanceOf(Array); expect(removed10[0]).toBeDefined(); expect(removed10[0].deleted).toBeDefined(); if (removed10[0].deleted) expect(removed10[0].deleted.id).toBe(10); - expect(tree.isAVLBalanced()).toBe(false); - expect(tree.getHeight()).toBe(4); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(4); - const removed15 = tree.remove(15, true); + const removed15 = bst.remove(15, true); expect(removed15).toBeInstanceOf(Array); expect(removed15[0]).toBeDefined(); expect(removed15[0].deleted).toBeDefined(); if (removed15[0].deleted) expect(removed15[0].deleted.id).toBe(15); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); - const removed5 = tree.remove(5, true); + const removed5 = bst.remove(5, true); expect(removed5).toBeInstanceOf(Array); expect(removed5[0]).toBeDefined(); expect(removed5[0].deleted).toBeDefined(); if (removed5[0].deleted) expect(removed5[0].deleted.id).toBe(5); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); - const removed13 = tree.remove(13, true); + const removed13 = bst.remove(13, true); expect(removed13).toBeInstanceOf(Array); expect(removed13[0]).toBeDefined(); expect(removed13[0].deleted).toBeDefined(); if (removed13[0].deleted) expect(removed13[0].deleted.id).toBe(13); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); - const removed3 = tree.remove(3, true); + const removed3 = bst.remove(3, true); expect(removed3).toBeInstanceOf(Array); expect(removed3[0]).toBeDefined(); expect(removed3[0].deleted).toBeDefined(); if (removed3[0].deleted) expect(removed3[0].deleted.id).toBe(3); - expect(tree.isAVLBalanced()).toBe(false); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); - const removed8 = tree.remove(8, true); + const removed8 = bst.remove(8, true); expect(removed8).toBeInstanceOf(Array); expect(removed8[0]).toBeDefined(); expect(removed8[0].deleted).toBeDefined(); if (removed8[0].deleted) expect(removed8[0].deleted.id).toBe(8); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); - const removed6 = tree.remove(6, true); + const removed6 = bst.remove(6, true); expect(removed6).toBeInstanceOf(Array); expect(removed6[0]).toBeDefined(); expect(removed6[0].deleted).toBeDefined(); if (removed6[0].deleted) expect(removed6[0].deleted.id).toBe(6); - expect(tree.remove(6, true).length).toBe(0); - expect(tree.isAVLBalanced()).toBe(false); - expect(tree.getHeight()).toBe(3); + expect(bst.remove(6, true).length).toBe(0); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); - const removed7 = tree.remove(7, true); + const removed7 = bst.remove(7, true); expect(removed7).toBeInstanceOf(Array); expect(removed7[0]).toBeDefined(); expect(removed7[0].deleted).toBeDefined(); if (removed7[0].deleted) expect(removed7[0].deleted.id).toBe(7); - expect(tree.isAVLBalanced()).toBe(false); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); - const removed9 = tree.remove(9, true); + const removed9 = bst.remove(9, true); expect(removed9).toBeInstanceOf(Array); expect(removed9[0]).toBeDefined(); expect(removed9[0].deleted).toBeDefined(); if (removed9[0].deleted) expect(removed9[0].deleted.id).toBe(9); - expect(tree.isAVLBalanced()).toBe(false); - expect(tree.getHeight()).toBe(3); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); - const removed14 = tree.remove(14, true); + const removed14 = bst.remove(14, true); expect(removed14).toBeInstanceOf(Array); expect(removed14[0]).toBeDefined(); expect(removed14[0].deleted).toBeDefined(); if (removed14[0].deleted) expect(removed14[0].deleted.id).toBe(14); - expect(tree.isAVLBalanced()).toBe(false); - expect(tree.getHeight()).toBe(2); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(2); - expect(tree.isAVLBalanced()).toBe(false); + expect(bst.isAVLBalanced()).toBe(false); - const bfsIDs = tree.BFS(); + const bfsIDs = bst.BFS(); expect(bfsIDs[0]).toBe(2); expect(bfsIDs[1]).toBe(12); expect(bfsIDs[2]).toBe(16); - const bfsNodes = tree.BFS('node'); + const bfsNodes = bst.BFS('node'); expect(bfsNodes[0].id).toBe(2); expect(bfsNodes[1].id).toBe(12); expect(bfsNodes[2].id).toBe(16); + + expect(bst.count).toBe(5); }); it('should perform various operations on a Binary Search Tree with object values', () => { @@ -388,5 +390,7 @@ describe('BST operations test', () => { expect(bfsNodes[0].id).toBe(2); expect(bfsNodes[1].id).toBe(12); expect(bfsNodes[2].id).toBe(16); + + expect(objBST.count).toBe(5); }); });