feat: The add method of the binary tree is compatible with the multi-parameter mode of add(key, value), and it is also compatible with the entry method.

This commit is contained in:
Revone 2023-12-07 09:16:25 +08:00
parent f56542a07a
commit 1b1d5c6669
13 changed files with 59 additions and 130 deletions

View file

@ -1,6 +1,6 @@
import { ElementCallback, PairCallback, ReduceElementCallback, ReducePairCallback } from "../../types";
import { ElementCallback, EntryCallback, ReduceElementCallback, ReduceEntryCallback } from "../../types";
export abstract class IterablePairBase<K = any, V = any> {
export abstract class IterableEntryBase<K = any, V = any> {
/**
* Time Complexity: O(n)
@ -87,7 +87,7 @@ export abstract class IterablePairBase<K = any, V = any> {
* @returns The `every` method is returning a boolean value. It returns `true` if every element in
* the collection satisfies the provided predicate function, and `false` otherwise.
*/
every(predicate: PairCallback<K, V, boolean>, thisArg?: any): boolean {
every(predicate: EntryCallback<K, V, boolean>, thisArg?: any): boolean {
let index = 0;
for (const item of this) {
if (!predicate.call(thisArg, item[1], item[0], index++, this)) {
@ -116,7 +116,7 @@ export abstract class IterablePairBase<K = any, V = any> {
* @returns a boolean value. It returns true if the predicate function returns true for any pair in
* the collection, and false otherwise.
*/
some(predicate: PairCallback<K, V, boolean>, thisArg?: any): boolean {
some(predicate: EntryCallback<K, V, boolean>, thisArg?: any): boolean {
let index = 0;
for (const item of this) {
if (predicate.call(thisArg, item[1], item[0], index++, this)) {
@ -143,7 +143,7 @@ export abstract class IterablePairBase<K = any, V = any> {
* specify the value of `this` within the callback function. If `thisArg` is provided, it will be
* used as the `this` value when calling the callback function. If `thisArg` is not provided, `
*/
forEach(callbackfn: PairCallback<K, V, void>, thisArg?: any): void {
forEach(callbackfn: EntryCallback<K, V, void>, thisArg?: any): void {
let index = 0;
for (const item of this) {
const [key, value] = item;
@ -171,7 +171,7 @@ export abstract class IterablePairBase<K = any, V = any> {
* @returns The `reduce` method is returning the final value of the accumulator after iterating over
* all the elements in the collection.
*/
reduce<U>(callbackfn: ReducePairCallback<K, V, U>, initialValue: U): U {
reduce<U>(callbackfn: ReduceEntryCallback<K, V, U>, initialValue: U): U {
let accumulator = initialValue;
let index = 0;
for (const item of this) {

View file

@ -95,19 +95,10 @@ export class AVLTree<K = any, V = any, N extends AVLTreeNode<K, V, N> = AVLTreeN
* Space Complexity: O(1) - constant space, as it doesn't use additional data structures that scale with input size.
*/
/**
* Time Complexity: O(log n) - logarithmic time, where "n" is the number of nodes in the tree. The add method of the superclass (BST) has logarithmic time complexity.
* Space Complexity: O(1) - constant space, as it doesn't use additional data structures that scale with input size.
*
* The function overrides the add method of a binary tree node and balances the tree after inserting
* a new node.
* @param keyOrNodeOrEntry - The parameter `keyOrNodeOrEntry` can be either a key, a node, or an
* entry.
* @returns The method is returning either the inserted node or `undefined`.
*/
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>): N | undefined {
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V): N | undefined {
if (keyOrNodeOrEntry === null) return undefined;
const inserted = super.add(keyOrNodeOrEntry);
const inserted = super.add(keyOrNodeOrEntry, value);
if (inserted) this._balancePath(inserted);
return inserted;
}

View file

@ -19,15 +19,15 @@ import {
BinaryTreePrintOptions,
BiTreeDeleteResult,
DFSOrderPattern,
EntryCallback,
FamilyPosition,
IterationType,
NodeDisplayLayout,
PairCallback
NodeDisplayLayout
} from '../../types';
import { IBinaryTree } from '../../interfaces';
import { trampoline } from '../../utils';
import { Queue } from '../queue';
import { IterablePairBase } from "../base";
import { IterableEntryBase } from "../base";
/**
* Represents a node in a binary tree.
@ -104,7 +104,7 @@ export class BinaryTreeNode<K = any, V = any, N extends BinaryTreeNode<K, V, N>
* 9. Complete Trees: All levels are fully filled except possibly the last, filled from left to right.
*/
export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = BinaryTreeNode<K, V, BinaryTreeNodeNested<K, V>>, TREE extends BinaryTree<K, V, N, TREE> = BinaryTree<K, V, N, BinaryTreeNested<K, V, N>>> extends IterablePairBase<K, V | undefined>
export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = BinaryTreeNode<K, V, BinaryTreeNodeNested<K, V>>, TREE extends BinaryTree<K, V, N, TREE> = BinaryTree<K, V, N, BinaryTreeNested<K, V, N>>> extends IterableEntryBase<K, V | undefined>
implements IBinaryTree<K, V, N, TREE> {
iterationType = IterationType.ITERATIVE
@ -183,14 +183,7 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
return exemplar instanceof BinaryTreeNode;
}
/**
* The function `exemplarToNode` converts an exemplar of a binary tree node into an actual node
* object.
* @param exemplar - BTNodeExemplar<K, V,N> - A generic type representing the exemplar parameter of the
* function. It can be any type.
* @returns a value of type `N` (which represents a node), or `null`, or `undefined`.
*/
exemplarToNode(exemplar: BTNodeExemplar<K, V, N>): N | null | undefined {
exemplarToNode(exemplar: BTNodeExemplar<K, V, N>, value?: V): N | null | undefined {
if (exemplar === undefined) return;
let node: N | null | undefined;
@ -208,7 +201,7 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
} else if (this.isNode(exemplar)) {
node = exemplar;
} else if (this.isNotNodeInstance(exemplar)) {
node = this.createNode(exemplar);
node = this.createNode(exemplar, value);
} else {
return;
}
@ -230,18 +223,11 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
* Space Complexity O(1)
*/
/**
* Time Complexity O(log n) - O(n)
* Space Complexity O(1)
*
* The `add` function adds a new node to a binary tree, either by key or by providing a node object.
* @param keyOrNodeOrEntry - The parameter `keyOrNodeOrEntry` can be one of the following:
* @returns The function `add` returns the inserted node (`N`), `null`, or `undefined`.
*/
add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>): N | null | undefined {
add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V): N | null | undefined {
let inserted: N | null | undefined;
const newNode = this.exemplarToNode(keyOrNodeOrEntry);
const newNode = this.exemplarToNode(keyOrNodeOrEntry, value);
if (newNode === undefined) return;
const _bfs = (root: N, newNode: N | null): N | undefined | null => {
@ -1775,7 +1761,7 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
* @returns The `filter` method is returning a new tree object that contains the key-value pairs that
* pass the given predicate function.
*/
filter(predicate: PairCallback<K, V | undefined, boolean>, thisArg?: any) {
filter(predicate: EntryCallback<K, V | undefined, boolean>, thisArg?: any) {
const newTree = this.createTree();
let index = 0;
for (const [key, value] of this) {
@ -1806,7 +1792,7 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
* will be used as the `this` value when the callback function is called. If you don't pass a value
* @returns The `map` method is returning a new tree object.
*/
map(callback: PairCallback<K, V | undefined, V>, thisArg?: any) {
map(callback: EntryCallback<K, V | undefined, V>, thisArg?: any) {
const newTree = this.createTree();
let index = 0;
for (const [key, value] of this) {

View file

@ -154,13 +154,8 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
return exemplar instanceof BSTNode;
}
/**
* The function `exemplarToNode` takes an exemplar and returns a corresponding node if the exemplar
* is valid, otherwise it returns undefined.
* @param exemplar - The `exemplar` parameter is of type `BTNodeExemplar<K, V, N>`.
* @returns a variable `node` which is of type `N` or `undefined`.
*/
override exemplarToNode(exemplar: BTNodeExemplar<K, V, N>): N | undefined {
override exemplarToNode(exemplar: BTNodeExemplar<K, V, N>, value?: V): N | undefined {
let node: N | undefined;
if (exemplar === null || exemplar === undefined) {
return;
@ -174,7 +169,7 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
node = this.createNode(key, value);
}
} else if (this.isNotNodeInstance(exemplar)) {
node = this.createNode(exemplar);
node = this.createNode(exemplar, value);
} else {
return;
}
@ -186,18 +181,8 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
* Space Complexity: O(1) - Constant space is used.
*/
/**
* Time Complexity: O(log n) - Average case for a balanced tree. In the worst case (unbalanced tree), it can be O(n).
* Space Complexity: O(1) - Constant space is used.
*
* The `add` function adds a new node to a binary search tree, either by key or by providing a node
* object.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be one of the following:
* @returns The method returns either the newly added node (`newNode`) or `undefined` if the input
* (`keyOrNodeOrEntry`) is null, undefined, or does not match any of the expected types.
*/
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>): N | undefined {
const newNode = this.exemplarToNode(keyOrNodeOrEntry);
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V): N | undefined {
const newNode = this.exemplarToNode(keyOrNodeOrEntry, value);
if (newNode === undefined) return;
if (this.root === undefined) {

View file

@ -115,15 +115,7 @@ export class RedBlackTree<K = any, V = any, N extends RedBlackTreeNode<K, V, N>
return exemplar instanceof RedBlackTreeNode;
}
/**
* The function `exemplarToNode` takes an exemplar and returns a node if the exemplar is valid,
* otherwise it returns undefined.
* @param exemplar - BTNodeExemplar<K, V, N> - A generic type representing an exemplar of a binary tree
* node. It can be either a node itself, an entry (key-value pair), a node key, or any other value
* that is not a valid exemplar.
* @returns a variable `node` which is of type `N | undefined`.
*/
override exemplarToNode(exemplar: BTNodeExemplar<K, V, N>): N | undefined {
override exemplarToNode(exemplar: BTNodeExemplar<K, V, N>, value?: V): N | undefined {
let node: N | undefined;
if (exemplar === null || exemplar === undefined) {
@ -138,7 +130,7 @@ export class RedBlackTree<K = any, V = any, N extends RedBlackTreeNode<K, V, N>
node = this.createNode(key, value, RBTNColor.RED);
}
} else if (this.isNotNodeInstance(exemplar)) {
node = this.createNode(exemplar, undefined, RBTNColor.RED);
node = this.createNode(exemplar, value, RBTNColor.RED);
} else {
return;
}
@ -150,14 +142,8 @@ export class RedBlackTree<K = any, V = any, N extends RedBlackTreeNode<K, V, N>
* Space Complexity: O(1)
*/
/**
* The function adds a node to a Red-Black Tree data structure.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be one of the following:
* @returns The method `add` returns either an instance of `N` (the node that was added) or
* `undefined`.
*/
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>): N | undefined {
const newNode = this.exemplarToNode(keyOrNodeOrEntry);
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V): N | undefined {
const newNode = this.exemplarToNode(keyOrNodeOrEntry, value);
if (newNode === undefined) return;
newNode.left = this.Sentinel;

View file

@ -85,15 +85,8 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
return exemplar instanceof TreeMultimapNode;
}
/**
* The function `exemplarToNode` converts an exemplar object into a node object.
* @param exemplar - The `exemplar` parameter is of type `BTNodeExemplar<K, V, N>`, where `V` represents
* the value type and `N` represents the node type.
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
* times the node should be created. If not provided, it defaults to 1.
* @returns a value of type `N` (the generic type parameter) or `undefined`.
*/
override exemplarToNode(exemplar: BTNodeExemplar<K, V, N>, count = 1): N | undefined {
override exemplarToNode(exemplar: BTNodeExemplar<K, V, N>, value?: V, count = 1): N | undefined {
let node: N | undefined;
if (exemplar === undefined || exemplar === null) {
return;
@ -107,7 +100,7 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
node = this.createNode(key, value, count);
}
} else if (this.isNotNodeInstance(exemplar)) {
node = this.createNode(exemplar, undefined, count);
node = this.createNode(exemplar, value, count);
} else {
return;
}
@ -119,20 +112,8 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
* Space Complexity: O(1) - constant space, as it doesn't use additional data structures that scale with input size.
*/
/**
* Time Complexity: O(log n) - logarithmic time, where "n" is the number of nodes in the tree. The add method of the superclass (AVLTree) has logarithmic time complexity.
* Space Complexity: O(1) - constant space, as it doesn't use additional data structures that scale with input size.
*
* The `add` function overrides the base class `add` function to add a new node to the tree multimap
* and update the count.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be one of the following:
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
* times the key or node or entry should be added to the multimap. If not provided, the default value
* is 1.
* @returns either a node (`N`) or `undefined`.
*/
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, count = 1): N | undefined {
const newNode = this.exemplarToNode(keyOrNodeOrEntry, count);
override add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V, count = 1): N | undefined {
const newNode = this.exemplarToNode(keyOrNodeOrEntry, value, count);
if (newNode === undefined) return;
const orgNodeCount = newNode?.count || 0;
@ -190,7 +171,7 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
if (l > r) return;
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add([midNode.key, midNode.value], midNode.count);
this.add(midNode.key, midNode.value, midNode.count);
buildBalanceBST(l, m - 1);
buildBalanceBST(m + 1, r);
};
@ -206,7 +187,7 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
if (l <= r) {
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add([midNode.key, midNode.value], midNode.count);
this.add(midNode.key, midNode.value, midNode.count);
stack.push([m + 1, r]);
stack.push([l, m - 1]);
}
@ -327,7 +308,7 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
*/
override clone(): TREE {
const cloned = this.createTree();
this.bfs(node => cloned.add([node.key, node.value], node.count));
this.bfs(node => cloned.add(node.key, node.value, node.count));
return cloned;
}

View file

@ -8,10 +8,10 @@
import { uuidV4 } from '../../utils';
import { PriorityQueue } from '../priority-queue';
import type { DijkstraResult, VertexKey } from '../../types';
import { PairCallback } from "../../types";
import { EntryCallback } from "../../types";
import { IGraph } from '../../interfaces';
import { Queue } from '../queue';
import { IterablePairBase } from "../base";
import { IterableEntryBase } from "../base";
export abstract class AbstractVertex<V = any> {
key: VertexKey;
@ -66,7 +66,7 @@ export abstract class AbstractGraph<
E = any,
VO extends AbstractVertex<V> = AbstractVertex<V>,
EO extends AbstractEdge<E> = AbstractEdge<E>
> extends IterablePairBase<VertexKey, V | undefined> implements IGraph<V, E, VO, EO> {
> extends IterableEntryBase<VertexKey, V | undefined> implements IGraph<V, E, VO, EO> {
constructor() {
super();
}
@ -1191,7 +1191,7 @@ export abstract class AbstractGraph<
* @returns The `filter` method returns an array of key-value pairs `[VertexKey, V | undefined][]`
* that satisfy the given predicate function.
*/
filter(predicate: PairCallback<VertexKey, V | undefined, boolean>, thisArg?: any): [VertexKey, V | undefined][] {
filter(predicate: EntryCallback<VertexKey, V | undefined, boolean>, thisArg?: any): [VertexKey, V | undefined][] {
const filtered: [VertexKey, V | undefined][] = [];
let index = 0;
for (const [key, value] of this) {
@ -1221,7 +1221,7 @@ export abstract class AbstractGraph<
* used as the `this` value when calling the callback function. If `thisArg` is not provided, `
* @returns The `map` function is returning an array of type `T[]`.
*/
map<T>(callback: PairCallback<VertexKey, V | undefined, T>, thisArg?: any): T[] {
map<T>(callback: EntryCallback<VertexKey, V | undefined, T>, thisArg?: any): T[] {
const mapped: T[] = [];
let index = 0;
for (const [key, value] of this) {

View file

@ -7,10 +7,10 @@
*/
import { isWeakKey, rangeCheck } from '../../utils';
import { HashMapLinkedNode, HashMapOptions, HashMapStoreItem, PairCallback } from '../../types';
import { IterablePairBase } from "../base";
import { EntryCallback, HashMapLinkedNode, HashMapOptions, HashMapStoreItem } from '../../types';
import { IterableEntryBase } from "../base";
export class HashMap<K = any, V = any> extends IterablePairBase<K, V> {
export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
protected _store: { [key: string]: HashMapStoreItem<K, V> } = {};
protected _objMap: Map<object, V> = new Map();
@ -165,7 +165,7 @@ export class HashMap<K = any, V = any> extends IterablePairBase<K, V> {
* @returns The `map` method is returning a new `HashMap` object with the transformed values based on
* the provided callback function.
*/
map<U>(callbackfn: PairCallback<K, V, U>, thisArg?: any): HashMap<K, U> {
map<U>(callbackfn: EntryCallback<K, V, U>, thisArg?: any): HashMap<K, U> {
const resultMap = new HashMap<K, U>();
let index = 0;
for (const [key, value] of this) {
@ -195,7 +195,7 @@ export class HashMap<K = any, V = any> extends IterablePairBase<K, V> {
* @returns The `filter` method is returning a new `HashMap` object that contains the key-value pairs
* from the original `HashMap` that pass the provided `predicate` function.
*/
filter(predicate: PairCallback<K, V, boolean>, thisArg?: any): HashMap<K, V> {
filter(predicate: EntryCallback<K, V, boolean>, thisArg?: any): HashMap<K, V> {
const filteredMap = new HashMap<K, V>();
let index = 0;
for (const [key, value] of this) {
@ -248,7 +248,7 @@ export class HashMap<K = any, V = any> extends IterablePairBase<K, V> {
}
}
export class LinkedHashMap<K = any, V = any> extends IterablePairBase<K, V> {
export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
protected _noObjMap: Record<string, HashMapLinkedNode<K, V | undefined>> = {};
protected _objMap = new WeakMap<object, HashMapLinkedNode<K, V | undefined>>();
@ -567,7 +567,7 @@ export class LinkedHashMap<K = any, V = any> extends IterablePairBase<K, V> {
* @returns a new `LinkedHashMap` object that contains the key-value pairs from the original
* `LinkedHashMap` object that satisfy the given predicate function.
*/
filter(predicate: PairCallback<K, V, boolean>, thisArg?: any): LinkedHashMap<K, V> {
filter(predicate: EntryCallback<K, V, boolean>, thisArg?: any): LinkedHashMap<K, V> {
const filteredMap = new LinkedHashMap<K, V>();
let index = 0;
for (const [key, value] of this) {
@ -601,7 +601,7 @@ export class LinkedHashMap<K = any, V = any> extends IterablePairBase<K, V> {
* @returns a new `LinkedHashMap` object with the values mapped according to the provided callback
* function.
*/
map<NV>(callback: PairCallback<K, V, NV>, thisArg?: any): LinkedHashMap<K, NV> {
map<NV>(callback: EntryCallback<K, V, NV>, thisArg?: any): LinkedHashMap<K, NV> {
const mappedMap = new LinkedHashMap<K, NV>();
let index = 0;
for (const [key, value] of this) {

View file

@ -13,7 +13,7 @@ export interface IBinaryTree<K = number, V = any, N extends BinaryTreeNode<K, V,
createTree(options?: Partial<BinaryTreeOptions<K>>): TREE;
add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, count?: number): N | null | undefined;
add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V, count?: number): N | null | undefined;
addMany(nodes: Iterable<BTNodeExemplar<K, V, N>>): (N | null | undefined)[];

View file

@ -1,6 +1,6 @@
import { IterableElementBase, IterablePairBase } from "../../../data-structures";
import { IterableElementBase, IterableEntryBase } from "../../../data-structures";
export type PairCallback<K, V, R> = (value: V, key: K, index: number, container: IterablePairBase<K, V>) => R;
export type EntryCallback<K, V, R> = (value: V, key: K, index: number, container: IterableEntryBase<K, V>) => R;
export type ElementCallback<V, R> = (element: V, index: number, container: IterableElementBase<V>) => R;
export type ReducePairCallback<K, V, R> = (accumulator: R, value: V, key: K, index: number, container: IterablePairBase<K, V>) => R;
export type ReduceEntryCallback<K, V, R> = (accumulator: R, value: V, key: K, index: number, container: IterableEntryBase<K, V>) => R;
export type ReduceElementCallback<V, R> = (accumulator: R, element: V, index: number, container: IterableElementBase<V>) => R;

View file

@ -183,7 +183,7 @@ describe('Individual package BST operations test', () => {
});
it('should perform various operations on a Binary Search Tree with object values', () => {
const objBST = new BST<number,{ key: number; keyA: number }>();
const objBST = new BST<number, { key: number; keyA: number }>();
expect(objBST).toBeInstanceOf(BST);
objBST.add([11, { key: 11, keyA: 11 }]);
objBST.add([3, { key: 3, keyA: 3 }]);

View file

@ -569,7 +569,7 @@ describe('BinaryTree iterative methods test', () => {
beforeEach(() => {
binaryTree = new BinaryTree();
binaryTree.add([1, 'a']);
binaryTree.add([2, 'b']);
binaryTree.add(2, 'b');
binaryTree.add([3, 'c']);
});

View file

@ -605,9 +605,9 @@ describe('TreeMultimap iterative methods test', () => {
let treeMM: TreeMultimap<number, string>;
beforeEach(() => {
treeMM = new TreeMultimap<number, string>();
treeMM.add([1, 'a'], 10);
treeMM.add([2, 'b'], 10);
treeMM.add([3, 'c'], 1);
treeMM.add(1, 'a', 10);
treeMM.add([2, 'b'], undefined, 10);
treeMM.add([3, 'c'], undefined, 1);
});
test('The node obtained by get Node should match the node type', () => {