From f2d3be75744cc1ba21d2eeaa4b135c4da2b71134 Mon Sep 17 00:00:00 2001 From: Revone Date: Thu, 26 Oct 2023 20:26:06 +0800 Subject: [PATCH] [binary-tree] Translate the return value type inference for methods that take a callback function as a parameter. --- CHANGELOG.md | 2 +- package-lock.json | 62 ++++-- package.json | 10 +- src/data-structures/binary-tree/avl-tree.ts | 24 ++- .../binary-tree/binary-tree.ts | 195 +++++++++++------- src/data-structures/binary-tree/bst.ts | 34 +-- src/data-structures/binary-tree/rb-tree.ts | 4 +- .../binary-tree/tree-multiset.ts | 20 +- src/interfaces/binary-tree.ts | 2 +- src/types/helpers.ts | 4 + .../binary-tree/tree-multiset.test.ts | 28 +-- 11 files changed, 242 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1dd951..5e34f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file. - [Semantic Versioning](https://semver.org/spec/v2.0.0.html) - [`auto-changelog`](https://github.com/CookPete/auto-changelog) -## [v1.38.4](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming) +## [v1.38.5](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming) ### Changes diff --git a/package-lock.json b/package-lock.json index 01deb13..469cb06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-structure-typed", - "version": "1.38.4", + "version": "1.38.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.38.4", + "version": "1.38.5", "license": "MIT", "devDependencies": { "@types/benchmark": "^2.1.3", @@ -15,17 +15,17 @@ "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "auto-changelog": "^2.4.0", - "avl-tree-typed": "^1.38.3", + "avl-tree-typed": "^1.38.4", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.38.3", - "bst-typed": "^1.38.3", + "binary-tree-typed": "^1.38.4", + "bst-typed": "^1.38.4", "dependency-cruiser": "^14.1.0", "eslint": "^8.50.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.28.1", - "heap-typed": "^1.38.2", + "heap-typed": "^1.38.4", "istanbul-badges-readme": "^1.8.5", "jest": "^29.7.0", "prettier": "^3.0.3", @@ -2728,10 +2728,13 @@ } }, "node_modules/avl-tree-typed": { - "version": "1.38.3", - "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.38.3.tgz", - "integrity": "sha512-ecoSXprvLtWQlq3Cgp4HNeSeWQgruHyjePxZ8+2ldeUhNN0xgI4Bm8yv/GbN7wV6WGG3V85EidBnu9skZ7jDUA==", - "dev": true + "version": "1.38.4", + "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.38.4.tgz", + "integrity": "sha512-m4+vC3t52CnPqMB5dxZI016OKMCxitk6vDsdqSadPHUeZd92E9LEQSHzgjdZ380FGcIZuz9q0IS7VTHR4F3+gg==", + "dev": true, + "dependencies": { + "data-structure-typed": "^1.38.4" + } }, "node_modules/babel-jest": { "version": "29.7.0", @@ -2924,10 +2927,13 @@ } }, "node_modules/binary-tree-typed": { - "version": "1.38.3", - "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.38.3.tgz", - "integrity": "sha512-fMnVgcByTNPM3D5sGJOQJ7/N3BUPqiMju4uTb7vDuGFC/sOcM1yN2MzbCEg/Ewwz7AQ7fydC2icuvqyY/AEUWA==", - "dev": true + "version": "1.38.4", + "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.38.4.tgz", + "integrity": "sha512-InWS3ggQsmEyqgvaO+Veme/uTphUxaFQ1C7/9Bz07zcr8lV0riDsj0En9qwGTI8R4L32CpBL1cxQXJbk9Do45g==", + "dev": true, + "dependencies": { + "data-structure-typed": "^1.38.4" + } }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -3005,10 +3011,13 @@ } }, "node_modules/bst-typed": { - "version": "1.38.3", - "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.38.3.tgz", - "integrity": "sha512-T83xkJXfuTjh0wGLiabSqmje4Ky5VxAfYA0RcM4UcgM7lFsYvyGwuSaI9AEWYbr+8Tb2sKqQ8uNP45mF+rB9Lg==", - "dev": true + "version": "1.38.4", + "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.38.4.tgz", + "integrity": "sha512-OCY+VQBfj8m8KYZullDuKrwYD8vzRBiIQ92hYDL3GWzuDMlTeEFUyIcDni0SR91JxRuUPbL7CuxN0fQRjDMKRg==", + "dev": true, + "dependencies": { + "data-structure-typed": "^1.38.4" + } }, "node_modules/buffer-from": { "version": "1.1.2", @@ -3403,6 +3412,12 @@ "node": ">= 8" } }, + "node_modules/data-structure-typed": { + "version": "1.38.4", + "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.38.4.tgz", + "integrity": "sha512-Yt9yjrx3Cm030z3o/6hr+AG0n+ZpWd+YpG2YiwxNwOT+hTZ6PYWt9MI+UVpiJSW/1Mrr0gchLlipU1r7l+UI/w==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4756,10 +4771,13 @@ } }, "node_modules/heap-typed": { - "version": "1.38.2", - "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.38.2.tgz", - "integrity": "sha512-GVNHQlbQkuuZMpMBj8fvekI8ClHst3s8W1qozLragMLD/ndb+acumWCXeypR9gwgjDTneqOW6fAgpUMDDKOn9Q==", - "dev": true + "version": "1.38.4", + "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.38.4.tgz", + "integrity": "sha512-fI+xbxoC3jpkmwqKIFRoJ8BGojd7FitEeYhwrkGpKnNMs9zp5dxA2qmrk+Gof0lDMuRca5CplVyInvGgL7cjzw==", + "dev": true, + "dependencies": { + "data-structure-typed": "^1.38.4" + } }, "node_modules/html-escaper": { "version": "2.0.2", diff --git a/package.json b/package.json index 3616ea3..dfce9bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.38.4", + "version": "1.38.5", "description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", @@ -61,17 +61,17 @@ "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "auto-changelog": "^2.4.0", - "avl-tree-typed": "^1.38.3", + "avl-tree-typed": "^1.38.4", "benchmark": "^2.1.4", - "binary-tree-typed": "^1.38.3", - "bst-typed": "^1.38.3", + "binary-tree-typed": "^1.38.4", + "bst-typed": "^1.38.4", "dependency-cruiser": "^14.1.0", "eslint": "^8.50.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.28.1", - "heap-typed": "^1.38.2", + "heap-typed": "^1.38.4", "istanbul-badges-readme": "^1.8.5", "jest": "^29.7.0", "prettier": "^3.0.3", diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 1b1c88d..da4d2e6 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -6,8 +6,15 @@ * @license MIT License */ import {BST, BSTNode} from './bst'; -import type {AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeletedResult, BinaryTreeNodeKey} from '../../types'; +import type { + AVLTreeNodeNested, + AVLTreeOptions, + BinaryTreeDeletedResult, + BinaryTreeNodeKey, + DefaultMapCallback +} from '../../types'; import {IBinaryTree} from '../../interfaces'; +import {MapCallback} from "../../types"; export class AVLTreeNode = AVLTreeNodeNested> extends BSTNode< V, @@ -64,12 +71,19 @@ export class AVLTree = AVLTreeNode> extends B /** * The function overrides the delete method of a binary tree and balances the tree after deleting a * node if necessary. - * @param {N | BinaryTreeNodeKey} nodeOrKey - The `nodeOrKey` parameter can be either a node object - * (`N`) or a key value (`BinaryTreeNodeKey`). + * @param {ReturnType} identifier - The `identifier` parameter is either a + * `BinaryTreeNodeKey` or a generic type `N`. It represents the property of the node that we are + * searching for. It can be a specific key value or any other property of the node. + * @param callback - The `callback` parameter is a function that takes a node as input and returns a + * value. This value is compared with the `identifier` parameter to determine if the node should be + * included in the result. The `callback` parameter has a default value of + * `this._defaultCallbackByKey` * @returns The method is returning an array of `BinaryTreeDeletedResult` objects. */ - override delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult[] { - const deletedResults = super.delete(nodeOrKey); + override delete>( + identifier: ReturnType, + callback: C = this._defaultCallbackByKey as C): BinaryTreeDeletedResult[] { + const deletedResults = super.delete(identifier, callback); for (const {needBalanced} of deletedResults) { if (needBalanced) { this._balancePath(needBalanced); diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 7b2ea5e..70aa5d5 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -15,7 +15,7 @@ import type { MapCallback, MapCallbackReturn } from '../../types'; -import {BinaryTreeDeletedResult, DFSOrderPattern, FamilyPosition, IterationType} from '../../types'; +import {BinaryTreeDeletedResult, DefaultMapCallback, DFSOrderPattern, FamilyPosition, IterationType} from '../../types'; import {IBinaryTree} from '../../interfaces'; import {trampoline} from '../../utils'; import {Queue} from '../queue'; @@ -97,29 +97,17 @@ export class BinaryTreeNode = */ get familyPosition(): FamilyPosition { const that = this as unknown as FAMILY; - if (that.parent) { - if (that.parent.left === that) { - if (that.left || that.right) { - return FamilyPosition.ROOT_LEFT; - } else { - return FamilyPosition.LEFT; - } - } else if (that.parent.right === that) { - if (that.left || that.right) { - return FamilyPosition.ROOT_RIGHT; - } else { - return FamilyPosition.RIGHT; - } - } else { - return FamilyPosition.MAL_NODE; - } - } else { - if (that.left || that.right) { - return FamilyPosition.ROOT; - } else { - return FamilyPosition.ISOLATED; - } + if (!this.parent) { + return this.left || this.right ? FamilyPosition.ROOT : FamilyPosition.ISOLATED; } + + if (this.parent.left === that) { + return this.left || this.right ? FamilyPosition.ROOT_LEFT : FamilyPosition.LEFT; + } else if (this.parent.right === that) { + return this.left || this.right ? FamilyPosition.ROOT_RIGHT : FamilyPosition.RIGHT; + } + + return FamilyPosition.MAL_NODE; } } @@ -234,7 +222,8 @@ export class BinaryTree = BinaryTreeNode> return; } - const existNode = keyOrNode ? this.get(keyOrNode, this._defaultCallbackByKey) : undefined; + const key = typeof keyOrNode === 'number' ? keyOrNode : keyOrNode ? keyOrNode.key: undefined; + const existNode = key !== undefined ? this.get(key, this._defaultCallbackByKey) : undefined; if (this.root) { if (existNode) { @@ -267,24 +256,18 @@ export class BinaryTree = BinaryTreeNode> */ addMany(keysOrNodes: (BinaryTreeNodeKey | null)[] | (N | null)[], values?: N['val'][]): (N | null | undefined)[] { // TODO not sure addMany not be run multi times - const inserted: (N | null | undefined)[] = []; - - for (let i = 0; i < keysOrNodes.length; i++) { - const keyOrNode = keysOrNodes[i]; + return keysOrNodes.map((keyOrNode, i) => { if (keyOrNode instanceof BinaryTreeNode) { - inserted.push(this.add(keyOrNode.key, keyOrNode.val)); - continue; + return this.add(keyOrNode.key, keyOrNode.val); } if (keyOrNode === null) { - inserted.push(this.add(null)); - continue; + return this.add(null); } const val = values?.[i]; - inserted.push(this.add(keyOrNode, val)); - } - return inserted; + return this.add(keyOrNode, val); + }); } /** @@ -301,19 +284,37 @@ export class BinaryTree = BinaryTreeNode> return keysOrNodes.length === this.addMany(keysOrNodes, data).length; } + delete>( + identifier: ReturnType | N + ): BinaryTreeDeletedResult[]; + + delete>( + identifier: ReturnType | N, + callback: C + ): BinaryTreeDeletedResult[]; + /** * The `delete` function removes a node from a binary search tree and returns the deleted node along * with the parent node that needs to be balanced. - * @param {N | BinaryTreeNodeKey} nodeOrKey - The `nodeOrKey` parameter can be either a node (`N`) or * a key (`BinaryTreeNodeKey`). If it is a key, the function will find the corresponding node in the * binary tree. * @returns an array of `BinaryTreeDeletedResult` objects. + * @param {ReturnType} identifier - The `identifier` parameter is either a + * `BinaryTreeNodeKey` or a generic type `N`. It represents the property of the node that we are + * searching for. It can be a specific key value or any other property of the node. + * @param callback - The `callback` parameter is a function that takes a node as input and returns a + * value. This value is compared with the `identifier` parameter to determine if the node should be + * included in the result. The `callback` parameter has a default value of + * `this._defaultCallbackByKey`, which */ - delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult[] { + delete>( + identifier: ReturnType | N, + callback: C = this._defaultCallbackByKey as C): BinaryTreeDeletedResult[] { const bstDeletedResult: BinaryTreeDeletedResult[] = []; if (!this.root) return bstDeletedResult; + if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; - const curr: N | null = typeof nodeOrKey === 'number' ? this.get(nodeOrKey) : nodeOrKey; + const curr = this.get(identifier, callback); if (!curr) return bstDeletedResult; const parent: N | null = curr?.parent ? curr.parent : null; @@ -354,16 +355,16 @@ export class BinaryTree = BinaryTreeNode> /** * The function `getDepth` calculates the depth of a given node in a binary tree relative to a * specified root node. - * @param {N | BinaryTreeNodeKey | null} distNode - The `distNode` parameter represents the node + * @param {BinaryTreeNodeKey | N | null} distNode - The `distNode` parameter represents the node * whose depth we want to find in the binary tree. It can be either a node object (`N`), a key value * of the node (`BinaryTreeNodeKey`), or `null`. - * @param {N | BinaryTreeNodeKey | null} beginRoot - The `beginRoot` parameter represents the + * @param {BinaryTreeNodeKey | N | null} beginRoot - The `beginRoot` parameter represents the * starting node from which we want to calculate the depth. It can be either a node object or the key * of a node in the binary tree. If no value is provided for `beginRoot`, it defaults to the root * node of the binary tree. * @returns the depth of the `distNode` relative to the `beginRoot`. */ - getDepth(distNode: N | BinaryTreeNodeKey | null, beginRoot: N | BinaryTreeNodeKey | null = this.root): number { + getDepth(distNode: BinaryTreeNodeKey | N | null, beginRoot: BinaryTreeNodeKey | N | null = this.root): number { if (typeof distNode === 'number') distNode = this.get(distNode); if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot); let depth = 0; @@ -380,7 +381,7 @@ export class BinaryTree = BinaryTreeNode> /** * The `getHeight` function calculates the maximum height of a binary tree using either recursive or * iterative approach. - * @param {N | BinaryTreeNodeKey | null} beginRoot - The `beginRoot` parameter represents the + * @param {BinaryTreeNodeKey | N | null} beginRoot - The `beginRoot` parameter represents the * starting node from which the height of the binary tree is calculated. It can be either a node * object (`N`), a key value of a node in the tree (`BinaryTreeNodeKey`), or `null` if no starting * node is specified. If ` @@ -389,7 +390,7 @@ export class BinaryTree = BinaryTreeNode> * possible values: * @returns the height of the binary tree. */ - getHeight(beginRoot: N | BinaryTreeNodeKey | null = this.root, iterationType = this.iterationType): number { + getHeight(beginRoot: BinaryTreeNodeKey | N | null = this.root, iterationType = this.iterationType): number { if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot); if (!beginRoot) return -1; @@ -491,18 +492,55 @@ export class BinaryTree = BinaryTreeNode> return this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot); } + + getNodes>( + identifier: ReturnType | N + ): N[]; + + getNodes>( + identifier: ReturnType | N, + callback: C + ): N[]; + + getNodes>( + identifier: ReturnType | N, + onlyOne: boolean + ): N[]; + + getNodes>( + identifier: ReturnType | N, + callback: C, + onlyOne: boolean + ): N[]; + + getNodes>( + identifier: ReturnType | N, + callback: C, + onlyOne: boolean, + beginRoot: N | null + ): N[]; + + getNodes>( + identifier: ReturnType | N, + callback: C, + onlyOne: boolean, + beginRoot: N | null, + iterationType: IterationType + ): N[]; + + /** * The function `getNodes` returns an array of nodes that match a given node property, using either * recursive or iterative traversal. - * @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter is either a + * @param {ReturnType} identifier - The `identifier` parameter is either a * `BinaryTreeNodeKey` or a generic type `N`. It represents the property of the node that we are * searching for. It can be a specific key value or any other property of the node. * @param callback - The `callback` parameter is a function that takes a node as input and returns a - * value. This value is compared with the `nodeProperty` parameter to determine if the node should be + * value. This value is compared with the `identifier` parameter to determine if the node should be * included in the result. The `callback` parameter has a default value of * `this._defaultCallbackByKey`, which * @param [onlyOne=false] - A boolean value indicating whether to stop searching after finding the - * first node that matches the nodeProperty. If set to true, the function will return an array with + * first node that matches the identifier. If set to true, the function will return an array with * only one element (or an empty array if no matching node is found). If set to false (default), the * function will continue searching for all * @param {N | null} beginRoot - The `beginRoot` parameter is the starting node from which the @@ -512,20 +550,20 @@ export class BinaryTree = BinaryTreeNode> * traverse the binary tree. It can have two possible values: * @returns The function `getNodes` returns an array of nodes (`N[]`). */ - getNodes = MapCallback>( - nodeProperty: BinaryTreeNodeKey | N, + getNodes>( + identifier: ReturnType | N, callback: C = this._defaultCallbackByKey as C, onlyOne = false, beginRoot: N | null = this.root, iterationType = this.iterationType ): N[] { if (!beginRoot) return []; - + if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; const ans: N[] = []; if (iterationType === IterationType.RECURSIVE) { const _traverse = (cur: N) => { - if (callback(cur) === nodeProperty) { + if (callback(cur) === identifier) { ans.push(cur); if (onlyOne) return; } @@ -540,7 +578,7 @@ export class BinaryTree = BinaryTreeNode> while (queue.size > 0) { const cur = queue.shift(); if (cur) { - if (callback(cur) === nodeProperty) { + if (callback(cur) === identifier) { ans.push(cur); if (onlyOne) return ans; } @@ -553,9 +591,17 @@ export class BinaryTree = BinaryTreeNode> return ans; } + has>(identifier: ReturnType | N): boolean; + + has>(identifier: ReturnType | N, callback: C): boolean; + + has>(identifier: ReturnType | N, beginRoot: N | null): boolean; + + has>(identifier: ReturnType | N, callback: C, beginRoot: N | null): boolean; + /** * The function checks if a binary tree has a node with a given property or key. - * @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter is the key or value of + * @param {BinaryTreeNodeKey | N} identifier - The `identifier` parameter is the key or value of * the node that you want to find in the binary tree. It can be either a `BinaryTreeNodeKey` or a * generic type `N`. * @param callback - The `callback` parameter is a function that is used to determine whether a node @@ -570,19 +616,30 @@ export class BinaryTree = BinaryTreeNode> * performed when searching for nodes in the binary tree. It can have one of the following values: * @returns a boolean value. */ - has = MapCallback>( - nodeProperty: BinaryTreeNodeKey | N, + has>( + identifier: ReturnType | N, callback: C = this._defaultCallbackByKey as C, beginRoot = this.root, iterationType = this.iterationType ): boolean { + if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; // TODO may support finding node by value equal - return this.getNodes(nodeProperty, callback, true, beginRoot, iterationType).length > 0; + return this.getNodes(identifier, callback, true, beginRoot, iterationType).length > 0; } + get>(identifier: ReturnType | N): N | null; + + get>(identifier: ReturnType | N, callback: C): N | null; + + get>(identifier: ReturnType | N, beginRoot: N | null): N | null; + + get>(identifier: ReturnType | N, callback: C, beginRoot: N | null): N | null; + + get>(identifier: ReturnType | N, callback: C, beginRoot: N | null, iterationType: IterationType): N | null; + /** * The function `get` returns the first node in a binary tree that matches the given property or key. - * @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter is the key or value of + * @param {BinaryTreeNodeKey | N} identifier - The `identifier` parameter is the key or value of * the node that you want to find in the binary tree. It can be either a `BinaryTreeNodeKey` or `N` * type. * @param callback - The `callback` parameter is a function that is used to determine whether a node @@ -595,14 +652,15 @@ export class BinaryTree = BinaryTreeNode> * performed when searching for a node in the binary tree. It can have one of the following values: * @returns either the found node (of type N) or null if no node is found. */ - get = MapCallback>( - nodeProperty: BinaryTreeNodeKey | N, + get>( + identifier: ReturnType | N, callback: C = this._defaultCallbackByKey as C, beginRoot = this.root, iterationType = this.iterationType ): N | null { + if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; // TODO may support finding node by value equal - return this.getNodes(nodeProperty, callback, true, beginRoot, iterationType)[0] ?? null; + return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null; } /** @@ -631,7 +689,7 @@ export class BinaryTree = BinaryTreeNode> /** * The function `getLeftMost` returns the leftmost node in a binary tree, either using recursive or * iterative traversal. - * @param {N | BinaryTreeNodeKey | null} beginRoot - The `beginRoot` parameter is the starting point + * @param {BinaryTreeNodeKey | N | null} beginRoot - The `beginRoot` parameter is the starting point * for finding the leftmost node in a binary tree. It can be either a node object (`N`), a key value * of a node (`BinaryTreeNodeKey`), or `null` if the tree is empty. * @param iterationType - The `iterationType` parameter is used to determine the type of iteration to @@ -639,7 +697,7 @@ export class BinaryTree = BinaryTreeNode> * @returns The function `getLeftMost` returns the leftmost node (`N`) in a binary tree. If there is * no leftmost node, it returns `null`. */ - getLeftMost(beginRoot: N | BinaryTreeNodeKey | null = this.root, iterationType = this.iterationType): N | null { + getLeftMost(beginRoot: BinaryTreeNodeKey | N | null = this.root, iterationType = this.iterationType): N | null { if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot); if (!beginRoot) return beginRoot; @@ -754,16 +812,16 @@ export class BinaryTree = BinaryTreeNode> * subtree traversal. It takes a single argument, which is the current node being traversed, and * returns a value. The return values from each callback invocation will be collected and returned as * an array. - * @param {N | BinaryTreeNodeKey | null} beginRoot - The `beginRoot` parameter is the starting point + * @param {BinaryTreeNodeKey | N | null} beginRoot - The `beginRoot` parameter is the starting point * for traversing the subtree. It can be either a node object, a key value of a node, or `null` to * start from the root of the tree. * @param iterationType - The `iterationType` parameter determines the type of traversal to be * performed on the binary tree. It can have two possible values: * @returns The function `subTreeTraverse` returns an array of `MapCallbackReturn`. */ - subTreeTraverse = MapCallback>( + subTreeTraverse>( callback: C = this._defaultCallbackByKey as C, - beginRoot: N | BinaryTreeNodeKey | null = this.root, + beginRoot: BinaryTreeNodeKey | N | null = this.root, iterationType = this.iterationType ): ReturnType[] { if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot); @@ -808,7 +866,7 @@ export class BinaryTree = BinaryTreeNode> * iteration used in the depth-first search algorithm. It can have two possible values: * @returns The function `dfs` returns an array of `MapCallbackReturn` values. */ - dfs = MapCallback>( + dfs>( callback: C = this._defaultCallbackByKey as C, pattern: DFSOrderPattern = 'in', beginRoot: N | null = this.root, @@ -886,7 +944,7 @@ export class BinaryTree = BinaryTreeNode> * breadth-first search. It takes a node of type `N` as its argument and returns a value of type * `BFSCallbackReturn`. The default value for this parameter is `this._defaultCallbackByKey * @param {boolean} [withLevel=false] - The `withLevel` parameter is a boolean flag that determines - * whether or not to include the level of each node in the callback function. If `withLevel` is set + * whether to include the level of each node in the callback function. If `withLevel` is set * to `true`, the level of each node will be passed as an argument to the callback function. If * `withLevel` is * @param {N | null} beginRoot - The `beginRoot` parameter is the starting node for the breadth-first @@ -964,7 +1022,7 @@ export class BinaryTree = BinaryTreeNode> * `beginRoot` is `null`, an empty array will be returned. * @returns The `morris` function returns an array of `MapCallbackReturn` values. */ - morris = MapCallback>( + morris>( callback: C = this._defaultCallbackByKey as C, pattern: DFSOrderPattern = 'in', beginRoot: N | null = this.root @@ -1077,8 +1135,7 @@ export class BinaryTree = BinaryTreeNode> * 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. */ - - protected _defaultCallbackByKey: (node: N) => number = node => node.key; + protected _defaultCallbackByKey: DefaultMapCallback = node => node.key; /** * The function `_addTo` adds a new node to a binary tree if there is an available position. diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 0237476..1e632aa 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -132,7 +132,7 @@ export class BST = BSTNode> extends BinaryTree /** * The `addMany` function is used to efficiently add multiple nodes to a binary search tree while * maintaining balance. - * @param {[BinaryTreeNodeKey | N, N['val']][]} arr - The `arr` parameter in the `addMany` function + * @param {[BinaryTreeNodeKey | N, N['val']][]} keysOrNodes - The `arr` parameter in the `addMany` function * represents an array of keys or nodes that need to be added to the binary search tree. It can be an * array of `BinaryTreeNodeKey` or `N` (which represents the node type in the binary search tree) or * `null @@ -223,7 +223,7 @@ export class BST = BSTNode> extends BinaryTree /** * The function returns the first node in the binary tree that matches the given node property and * callback. - * @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter is used to specify the + * @param {ReturnType | N} identifier - The `nodeProperty` parameter is used to specify the * property of the binary tree node that you want to search for. It can be either a specific key * value (`BinaryTreeNodeKey`) or a custom callback function (`MapCallback`) that determines * whether a node matches the desired property. @@ -238,13 +238,13 @@ export class BST = BSTNode> extends BinaryTree * @returns either the first node that matches the given nodeProperty and callback, or null if no * matching node is found. */ - override get = MapCallback>( - nodeProperty: BinaryTreeNodeKey | N, + override get>( + identifier: ReturnType | N, callback: C = this._defaultCallbackByKey as C, beginRoot = this.root, iterationType = this.iterationType ): N | null { - return this.getNodes(nodeProperty, callback, true, beginRoot, iterationType)[0] ?? null; + return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null; } /** @@ -271,7 +271,7 @@ export class BST = BSTNode> extends BinaryTree /** * The function `getNodes` retrieves nodes from a binary tree based on a given node property or key, * using either recursive or iterative traversal. - * @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter represents the property + * @param {ReturnType | N} identifier - The `nodeProperty` parameter represents the property * of the binary tree node that you want to search for. It can be either a `BinaryTreeNodeKey` or a * generic type `N`. * @param callback - The `callback` parameter is a function that takes a node as input and returns a @@ -289,8 +289,8 @@ export class BST = BSTNode> extends BinaryTree * traverse the binary tree. It can have one of the following values: * @returns an array of nodes (N[]). */ - override getNodes = MapCallback>( - nodeProperty: BinaryTreeNodeKey | N, + override getNodes>( + identifier: ReturnType | N, callback: C = this._defaultCallbackByKey as C, onlyOne = false, beginRoot: N | null = this.root, @@ -302,7 +302,7 @@ export class BST = BSTNode> extends BinaryTree if (iterationType === IterationType.RECURSIVE) { const _traverse = (cur: N) => { const callbackResult = callback(cur); - if (callbackResult === nodeProperty) { + if (callbackResult === identifier) { ans.push(cur); if (onlyOne) return; } @@ -310,8 +310,8 @@ export class BST = BSTNode> extends BinaryTree if (!cur.left && !cur.right) return; // TODO potential bug if (callback === this._defaultCallbackByKey) { - if (this._compare(cur.key, nodeProperty as number) === CP.gt) cur.left && _traverse(cur.left); - if (this._compare(cur.key, nodeProperty as number) === CP.lt) cur.right && _traverse(cur.right); + if (this._compare(cur.key, identifier as number) === CP.gt) cur.left && _traverse(cur.left); + if (this._compare(cur.key, identifier as number) === CP.lt) cur.right && _traverse(cur.right); } else { cur.left && _traverse(cur.left); cur.right && _traverse(cur.right); @@ -325,14 +325,14 @@ export class BST = BSTNode> extends BinaryTree const cur = queue.shift(); if (cur) { const callbackResult = callback(cur); - if (callbackResult === nodeProperty) { + if (callbackResult === identifier) { ans.push(cur); if (onlyOne) return ans; } // TODO potential bug if (callback === this._defaultCallbackByKey) { - if (this._compare(cur.key, nodeProperty as number) === CP.gt) cur.left && queue.push(cur.left); - if (this._compare(cur.key, nodeProperty as number) === CP.lt) cur.right && queue.push(cur.right); + if (this._compare(cur.key, identifier as number) === CP.gt) cur.left && queue.push(cur.left); + if (this._compare(cur.key, identifier as number) === CP.lt) cur.right && queue.push(cur.right); } else { cur.left && queue.push(cur.left); cur.right && queue.push(cur.right); @@ -355,7 +355,7 @@ export class BST = BSTNode> extends BinaryTree * @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to * traverse nodes that are lesser than, greater than, or equal to the `targetNode`. It can take one * of the following values: - * @param {N | BinaryTreeNodeKey | null} targetNode - The `targetNode` parameter in the + * @param {BinaryTreeNodeKey | N | null} targetNode - The `targetNode` parameter in the * `lesserOrGreaterTraverse` function is used to specify the node from which the traversal should * start. It can be either a reference to a specific node (`N`), the key of a node * (`BinaryTreeNodeKey`), or `null` to @@ -363,10 +363,10 @@ export class BST = BSTNode> extends BinaryTree * done recursively or iteratively. It can have two possible values: * @returns The function `lesserOrGreaterTraverse` returns an array of `MapCallbackReturn`. */ - lesserOrGreaterTraverse = MapCallback>( + lesserOrGreaterTraverse>( callback: C = this._defaultCallbackByKey as C, lesserOrGreater: CP = CP.lt, - targetNode: N | BinaryTreeNodeKey | null = this.root, + targetNode: BinaryTreeNodeKey | N | null = this.root, iterationType = this.iterationType ): ReturnType[] { if (typeof targetNode === 'number') targetNode = this.get(targetNode); diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index d8d1685..475f058 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -205,8 +205,8 @@ export class RBTree = RBTreeNode> extends BST< // node.right = null; // } // - // override delete(nodeOrKey: BinaryTreeNodeKey | N): BinaryTreeDeletedResult[] { - // const node = this.get(nodeOrKey); + // override delete(keyOrNode: BinaryTreeNodeKey | N): BinaryTreeDeletedResult[] { + // const node = this.get(keyOrNode); // const result: BinaryTreeDeletedResult[] = [{deleted: undefined, needBalanced: null}]; // if (!node) return result; // Node does not exist // diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index a36826b..464956b 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -5,8 +5,8 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type {BinaryTreeNodeKey, TreeMultisetNodeNested, TreeMultisetOptions} from '../../types'; -import {BinaryTreeDeletedResult, CP, FamilyPosition, IterationType} from '../../types'; +import type {BinaryTreeNodeKey, DefaultMapCallback, TreeMultisetNodeNested, TreeMultisetOptions} from '../../types'; +import {BinaryTreeDeletedResult, CP, FamilyPosition, IterationType, MapCallback} from '../../types'; import {IBinaryTree} from '../../interfaces'; import {AVLTree, AVLTreeNode} from './avl-tree'; @@ -265,20 +265,26 @@ export class TreeMultiset = TreeMultiset /** * The `delete` function in a binary search tree deletes a node from the tree and returns the deleted * node along with the parent node that needs to be balanced. - * @param {N | BinaryTreeNodeKey} nodeOrKey - The `nodeOrKey` parameter can be either a node object - * (`N`) or a key value (`BinaryTreeNodeKey`). It represents the node or key that needs to be deleted - * from the binary tree. + * @param {ReturnType} identifier - The `identifier` parameter is either a + * `BinaryTreeNodeKey` or a generic type `N`. It represents the property of the node that we are + * searching for. It can be a specific key value or any other property of the node. + * @param callback - The `callback` parameter is a function that takes a node as input and returns a + * value. This value is compared with the `identifier` parameter to determine if the node should be + * included in the result. The `callback` parameter has a default value of + * `this._defaultCallbackByKey` * @param [ignoreCount=false] - A boolean flag indicating whether to ignore the count of the node * being deleted. If set to true, the count of the node will not be considered and the node will be * deleted regardless of its count. If set to false (default), the count of the node will be * decremented by 1 and * @returns The method `delete` returns an array of `BinaryTreeDeletedResult` objects. */ - override delete(nodeOrKey: N | BinaryTreeNodeKey, ignoreCount = false): BinaryTreeDeletedResult[] { + override delete>( + identifier: ReturnType, + callback: C = this._defaultCallbackByKey as C, ignoreCount = false): BinaryTreeDeletedResult[] { const bstDeletedResult: BinaryTreeDeletedResult[] = []; if (!this.root) return bstDeletedResult; - const curr: N | null = this.get(nodeOrKey); + const curr: N | null = this.get(identifier, callback); if (!curr) return bstDeletedResult; const parent: N | null = curr?.parent ? curr.parent : null; diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index 62a788b..ca63969 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -6,5 +6,5 @@ export interface IBinaryTree> { add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined; - delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult[]; + // delete(keyOrNode: BinaryTreeNodeKey | N): BinaryTreeDeletedResult[]; } diff --git a/src/types/helpers.ts b/src/types/helpers.ts index 077114d..93a5b64 100644 --- a/src/types/helpers.ts +++ b/src/types/helpers.ts @@ -1,9 +1,13 @@ +import {BinaryTreeNodeKey} from "./data-structures"; + export type Comparator = (a: T, b: T) => number; export type DFSOrderPattern = 'pre' | 'in' | 'post'; export type MapCallback = (node: N) => D; +export type DefaultMapCallback = (node: N) => D; + export type MapCallbackReturn = ReturnType>; export enum CP { diff --git a/test/unit/data-structures/binary-tree/tree-multiset.test.ts b/test/unit/data-structures/binary-tree/tree-multiset.test.ts index 6812143..e0f7125 100644 --- a/test/unit/data-structures/binary-tree/tree-multiset.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multiset.test.ts @@ -73,7 +73,7 @@ describe('TreeMultiset operations test', () => { expect(bfsNodesAfterBalanced[0].key).toBe(8); expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); - const removed11 = treeMultiset.delete(11, true); + const removed11 = treeMultiset.delete(11, undefined, true); expect(removed11 instanceof Array); expect(removed11[0]); expect(removed11[0].deleted); @@ -84,7 +84,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.getHeight(15)).toBe(1); - const removed1 = treeMultiset.delete(1, true); + const removed1 = treeMultiset.delete(1, undefined, true); expect(removed1 instanceof Array); expect(removed1[0]); expect(removed1[0].deleted); @@ -94,7 +94,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.getHeight()).toBe(4); - const removed4 = treeMultiset.delete(4, true); + const removed4 = treeMultiset.delete(4, undefined, true); expect(removed4 instanceof Array); expect(removed4[0]); expect(removed4[0].deleted); @@ -103,7 +103,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(4); - const removed10 = treeMultiset.delete(10, true); + const removed10 = treeMultiset.delete(10, undefined, true); expect(removed10 instanceof Array); expect(removed10[0]); expect(removed10[0].deleted); @@ -112,7 +112,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.getHeight()).toBe(3); - const removed15 = treeMultiset.delete(15, true); + const removed15 = treeMultiset.delete(15, undefined, true); expect(removed15 instanceof Array); expect(removed15[0]); expect(removed15[0].deleted); @@ -121,7 +121,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(3); - const removed5 = treeMultiset.delete(5, true); + const removed5 = treeMultiset.delete(5, undefined, true); expect(removed5 instanceof Array); expect(removed5[0]); expect(removed5[0].deleted); @@ -130,7 +130,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(3); - const removed13 = treeMultiset.delete(13, true); + const removed13 = treeMultiset.delete(13, undefined, true); expect(removed13 instanceof Array); expect(removed13[0]); expect(removed13[0].deleted); @@ -138,7 +138,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(3); - const removed3 = treeMultiset.delete(3, true); + const removed3 = treeMultiset.delete(3, undefined, true); expect(removed3 instanceof Array); expect(removed3[0]); expect(removed3[0].deleted); @@ -146,7 +146,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(3); - const removed8 = treeMultiset.delete(8, true); + const removed8 = treeMultiset.delete(8, undefined, true); expect(removed8 instanceof Array); expect(removed8[0]); expect(removed8[0].deleted); @@ -154,17 +154,17 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(3); - const removed6 = treeMultiset.delete(6, true); + const removed6 = treeMultiset.delete(6, undefined, true); expect(removed6 instanceof Array); expect(removed6[0]); expect(removed6[0].deleted); if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); - expect(treeMultiset.delete(6, true).length).toBe(0); + expect(treeMultiset.delete(6, undefined, true).length).toBe(0); expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(2); - const removed7 = treeMultiset.delete(7, true); + const removed7 = treeMultiset.delete(7, undefined, true); expect(removed7 instanceof Array); expect(removed7[0]); expect(removed7[0].deleted); @@ -172,7 +172,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(2); - const removed9 = treeMultiset.delete(9, true); + const removed9 = treeMultiset.delete(9, undefined, true); expect(removed9 instanceof Array); expect(removed9[0]); expect(removed9[0].deleted); @@ -180,7 +180,7 @@ describe('TreeMultiset operations test', () => { expect(treeMultiset.isAVLBalanced()).toBe(true); expect(treeMultiset.getHeight()).toBe(2); - const removed14 = treeMultiset.delete(14, true); + const removed14 = treeMultiset.delete(14, undefined, true); expect(removed14 instanceof Array); expect(removed14[0]); expect(removed14[0].deleted);