[binary-tree] Translate the return value type inference for methods that take a callback function as a parameter.

This commit is contained in:
Revone 2023-10-26 20:26:06 +08:00
parent 31c228668e
commit f2d3be7574
11 changed files with 242 additions and 143 deletions

View file

@ -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

62
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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<V = any, FAMILY extends AVLTreeNode<V, FAMILY> = AVLTreeNodeNested<V>> extends BSTNode<
V,
@ -64,12 +71,19 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = 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<C>} 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<N>` objects.
*/
override delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult<N>[] {
const deletedResults = super.delete(nodeOrKey);
override delete<C extends MapCallback<N>>(
identifier: ReturnType<C>,
callback: C = this._defaultCallbackByKey as C): BinaryTreeDeletedResult<N>[] {
const deletedResults = super.delete(identifier, callback);
for (const {needBalanced} of deletedResults) {
if (needBalanced) {
this._balancePath(needBalanced);

View file

@ -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<V = any, FAMILY extends BinaryTreeNode<V, FAMILY> =
*/
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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
return keysOrNodes.length === this.addMany(keysOrNodes, data).length;
}
delete<C extends MapCallback<N>>(
identifier: ReturnType<C> | N
): BinaryTreeDeletedResult<N>[];
delete<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
callback: C
): BinaryTreeDeletedResult<N>[];
/**
* 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<N>` objects.
* @param {ReturnType<C>} 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<N>[] {
delete<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
callback: C = this._defaultCallbackByKey as C): BinaryTreeDeletedResult<N>[] {
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
return this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot);
}
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | N
): N[];
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
callback: C
): N[];
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
onlyOne: boolean
): N[];
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
callback: C,
onlyOne: boolean
): N[];
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
callback: C,
onlyOne: boolean,
beginRoot: N | null
): N[];
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | 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<C>} 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<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* traverse the binary tree. It can have two possible values:
* @returns The function `getNodes` returns an array of nodes (`N[]`).
*/
getNodes<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
nodeProperty: BinaryTreeNodeKey | N,
getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | 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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
return ans;
}
has<C extends MapCallback<N>>(identifier: ReturnType<C> | N): boolean;
has<C extends MapCallback<N>>(identifier: ReturnType<C> | N, callback: C): boolean;
has<C extends MapCallback<N>>(identifier: ReturnType<C> | N, beginRoot: N | null): boolean;
has<C extends MapCallback<N>>(identifier: ReturnType<C> | 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<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* performed when searching for nodes in the binary tree. It can have one of the following values:
* @returns a boolean value.
*/
has<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
nodeProperty: BinaryTreeNodeKey | N,
has<C extends MapCallback<N>>(
identifier: ReturnType<C> | 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<C extends MapCallback<N>>(identifier: ReturnType<C> | N): N | null;
get<C extends MapCallback<N>>(identifier: ReturnType<C> | N, callback: C): N | null;
get<C extends MapCallback<N>>(identifier: ReturnType<C> | N, beginRoot: N | null): N | null;
get<C extends MapCallback<N>>(identifier: ReturnType<C> | N, callback: C, beginRoot: N | null): N | null;
get<C extends MapCallback<N>>(identifier: ReturnType<C> | 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<N extends BinaryTreeNode<N['val'], N> = 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<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
nodeProperty: BinaryTreeNodeKey | N,
get<C extends MapCallback<N>>(
identifier: ReturnType<C> | 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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = 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<N extends BinaryTreeNode<N['val'], N> = 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<N>`.
*/
subTreeTraverse<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
subTreeTraverse<C extends MapCallback<N>>(
callback: C = this._defaultCallbackByKey as C,
beginRoot: N | BinaryTreeNodeKey | null = this.root,
beginRoot: BinaryTreeNodeKey | N | null = this.root,
iterationType = this.iterationType
): ReturnType<C>[] {
if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot);
@ -808,7 +866,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* iteration used in the depth-first search algorithm. It can have two possible values:
* @returns The function `dfs` returns an array of `MapCallbackReturn<N>` values.
*/
dfs<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
dfs<C extends MapCallback<N>>(
callback: C = this._defaultCallbackByKey as C,
pattern: DFSOrderPattern = 'in',
beginRoot: N | null = this.root,
@ -886,7 +944,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* breadth-first search. It takes a node of type `N` as its argument and returns a value of type
* `BFSCallbackReturn<N>`. 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<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode>
* `beginRoot` is `null`, an empty array will be returned.
* @returns The `morris` function returns an array of `MapCallbackReturn<N>` values.
*/
morris<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
morris<C extends MapCallback<N>>(
callback: C = this._defaultCallbackByKey as C,
pattern: DFSOrderPattern = 'in',
beginRoot: N | null = this.root
@ -1077,8 +1135,7 @@ export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = 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<N> = node => node.key;
/**
* The function `_addTo` adds a new node to a binary tree if there is an available position.

View file

@ -132,7 +132,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
/**
* 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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
/**
* 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<C> | 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<N>`) that determines
* whether a node matches the desired property.
@ -238,13 +238,13 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* @returns either the first node that matches the given nodeProperty and callback, or null if no
* matching node is found.
*/
override get<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
nodeProperty: BinaryTreeNodeKey | N,
override get<C extends MapCallback<N>>(
identifier: ReturnType<C> | 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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
/**
* 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<C> | 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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* traverse the binary tree. It can have one of the following values:
* @returns an array of nodes (N[]).
*/
override getNodes<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
nodeProperty: BinaryTreeNodeKey | N,
override getNodes<C extends MapCallback<N>>(
identifier: ReturnType<C> | N,
callback: C = this._defaultCallbackByKey as C,
onlyOne = false,
beginRoot: N | null = this.root,
@ -302,7 +302,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* @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<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* done recursively or iteratively. It can have two possible values:
* @returns The function `lesserOrGreaterTraverse` returns an array of `MapCallbackReturn<N>`.
*/
lesserOrGreaterTraverse<C extends MapCallback<N> = MapCallback<N, BinaryTreeNodeKey>>(
lesserOrGreaterTraverse<C extends MapCallback<N>>(
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<C>[] {
if (typeof targetNode === 'number') targetNode = this.get(targetNode);

View file

@ -205,8 +205,8 @@ export class RBTree<N extends RBTreeNode<N['val'], N> = RBTreeNode> extends BST<
// node.right = null;
// }
//
// override delete(nodeOrKey: BinaryTreeNodeKey | N): BinaryTreeDeletedResult<N>[] {
// const node = this.get(nodeOrKey);
// override delete(keyOrNode: BinaryTreeNodeKey | N): BinaryTreeDeletedResult<N>[] {
// const node = this.get(keyOrNode);
// const result: BinaryTreeDeletedResult<N>[] = [{deleted: undefined, needBalanced: null}];
// if (!node) return result; // Node does not exist
//

View file

@ -5,8 +5,8 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @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<N extends TreeMultisetNode<N['val'], N> = 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<C>} 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<N>` objects.
*/
override delete(nodeOrKey: N | BinaryTreeNodeKey, ignoreCount = false): BinaryTreeDeletedResult<N>[] {
override delete<C extends MapCallback<N>>(
identifier: ReturnType<C>,
callback: C = this._defaultCallbackByKey as C, ignoreCount = false): BinaryTreeDeletedResult<N>[] {
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
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;

View file

@ -6,5 +6,5 @@ export interface IBinaryTree<N extends BinaryTreeNode<N['val'], N>> {
add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined;
delete(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult<N>[];
// delete(keyOrNode: BinaryTreeNodeKey | N): BinaryTreeDeletedResult<N>[];
}

View file

@ -1,9 +1,13 @@
import {BinaryTreeNodeKey} from "./data-structures";
export type Comparator<T> = (a: T, b: T) => number;
export type DFSOrderPattern = 'pre' | 'in' | 'post';
export type MapCallback<N, D = any> = (node: N) => D;
export type DefaultMapCallback<N, D = BinaryTreeNodeKey> = (node: N) => D;
export type MapCallbackReturn<N> = ReturnType<MapCallback<N>>;
export enum CP {

View file

@ -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);