mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2025-01-18 11:14:05 +00:00
feat: Implemented correct TreeMultiMap and AVLTreeMultiMap. Removed support for Raw parameters in the add and delete methods, #115.
fix: Reimplemented the original TreeMultiMap and AVLTreeMultiMap as two new data structures: TreeCounter and AVLTreeCounter, #108. style: Code cleanup, such as removing unnecessary comments. docs: Fixed some time complexity and space complexity. chore: Changed the ESModules build directory to the standard dist/ems.
This commit is contained in:
parent
6dc64dbb53
commit
b16559bc54
|
@ -828,6 +828,7 @@ macOS Big Sur
|
|||
|
||||
Version 11.7.9
|
||||
|
||||
***Our performance testing is conducted directly on the TypeScript source code. The actual performance of the compiled JavaScript code is generally 3 times higher. We have compared it with C++, and it is only 30% slower than C++.***
|
||||
|
||||
[//]: # (No deletion!!! Start of Replace Section)
|
||||
<div class="json-to-html-collapse clearfix 0">
|
||||
|
|
14
package.json
14
package.json
|
@ -3,19 +3,19 @@
|
|||
"version": "1.54.0",
|
||||
"description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
"module": "dist/esm/index.js",
|
||||
"browser": "dist/umd/data-structure-typed.min.js",
|
||||
"types": "dist/mjs/index.d.ts",
|
||||
"types": "dist/esm/index.d.ts",
|
||||
"umd:main": "dist/umd/data-structure-typed.min.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/mjs/index.js",
|
||||
"import": "./dist/esm/index.js",
|
||||
"require": "./dist/cjs/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs-class",
|
||||
"build:mjs": "rm -rf dist/mjs && tsc -p tsconfig-mjs.json",
|
||||
"build": "npm run build:esm && npm run build:cjs && npm run build:umd && npm run build:docs-class",
|
||||
"build:esm": "rm -rf dist/esm && tsc -p tsconfig-esm.json",
|
||||
"build:cjs": "rm -rf dist/cjs && tsc -p tsconfig-cjs.json",
|
||||
"build:umd": "tsup",
|
||||
"build:docs": "npm run gen:examples && typedoc --out docs ./src",
|
||||
|
@ -24,7 +24,7 @@
|
|||
"test:in-band": "jest --runInBand",
|
||||
"test": "npm run test:in-band",
|
||||
"test:integration": "npm run update:subs && jest --config jest.integration.config.js && tsc test/integration/compile.ts",
|
||||
"test:perf": "npm run build:cjs && npm run build:mjs && ts-node test/performance/reportor.ts",
|
||||
"test:perf": "npm run build:cjs && npm run build:esm && ts-node test/performance/reportor.ts",
|
||||
"check": "tsc --noEmit",
|
||||
"check:circular-refs": "dependency-cruiser src",
|
||||
"lint:src": "eslint --fix 'src/**/*.{js,ts}'",
|
||||
|
@ -54,7 +54,7 @@
|
|||
"url": "https://github.com/zrwusa/data-structure-typed/issues"
|
||||
},
|
||||
"homepage": "https://data-structure-typed-docs.vercel.app",
|
||||
"author": "Tyler Zeng <zrwusa@gmail.com>",
|
||||
"author": "Pablo Zeng <zrwusa@gmail.com>",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"@zrwusa:registry": "https://npm.pkg.github.com"
|
||||
|
|
463
src/data-structures/binary-tree/avl-tree-counter.ts
Normal file
463
src/data-structures/binary-tree/avl-tree-counter.ts
Normal file
|
@ -0,0 +1,463 @@
|
|||
/**
|
||||
* data-structure-typed
|
||||
*
|
||||
* @author Pablo Zeng
|
||||
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
import type {
|
||||
AVLTreeCounterOptions,
|
||||
BinaryTreeDeleteResult,
|
||||
BSTNOptKeyOrNode,
|
||||
BTNRep,
|
||||
EntryCallback,
|
||||
IterationType,
|
||||
OptNodeOrNull
|
||||
} from '../../types';
|
||||
import { IBinaryTree } from '../../interfaces';
|
||||
import { AVLTree, AVLTreeNode } from './avl-tree';
|
||||
|
||||
export class AVLTreeCounterNode<K = any, V = any> extends AVLTreeNode<K, V> {
|
||||
/**
|
||||
* The constructor function initializes a BinaryTreeNode object with a key, value, and count.
|
||||
* @param {K} key - The `key` parameter is of type `K` and represents the unique identifier
|
||||
* of the binary tree node.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter of type `V`. It represents the value of the binary
|
||||
* tree node. If no value is provided, it will be `undefined`.
|
||||
* @param {number} [count=1] - The `count` parameter is a number that represents the number of times a particular value
|
||||
* occurs in a binary tree node. It has a default value of 1, which means that if no value is provided for the `count`
|
||||
* parameter when creating a new instance of the `BinaryTreeNode` class.
|
||||
*/
|
||||
constructor(key: K, value?: V, count = 1) {
|
||||
super(key, value);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
override parent?: AVLTreeCounterNode<K, V> = undefined;
|
||||
|
||||
override _left?: OptNodeOrNull<AVLTreeCounterNode<K, V>> = undefined;
|
||||
|
||||
override get left(): OptNodeOrNull<AVLTreeCounterNode<K, V>> {
|
||||
return this._left;
|
||||
}
|
||||
|
||||
override set left(v: OptNodeOrNull<AVLTreeCounterNode<K, V>>) {
|
||||
if (v) {
|
||||
v.parent = this;
|
||||
}
|
||||
this._left = v;
|
||||
}
|
||||
|
||||
override _right?: OptNodeOrNull<AVLTreeCounterNode<K, V>> = undefined;
|
||||
|
||||
override get right(): OptNodeOrNull<AVLTreeCounterNode<K, V>> {
|
||||
return this._right;
|
||||
}
|
||||
|
||||
override set right(v: OptNodeOrNull<AVLTreeCounterNode<K, V>>) {
|
||||
if (v) {
|
||||
v.parent = this;
|
||||
}
|
||||
this._right = v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The only distinction between a AVLTreeCounter and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters.
|
||||
*/
|
||||
export class AVLTreeCounter<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
||||
extends AVLTree<K, V, R, MK, MV, MR>
|
||||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
/**
|
||||
* The constructor initializes a new AVLTreeCounter object with optional initial elements.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an
|
||||
* iterable object that can contain either keys, nodes, entries, or raw elements.
|
||||
* @param [options] - The `options` parameter is an optional object that can be used to customize the
|
||||
* behavior of the AVLTreeCounter. It can include properties such as `compareKeys` and
|
||||
* `compareValues` functions to define custom comparison logic for keys and values, respectively.
|
||||
*/
|
||||
constructor(
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V, AVLTreeCounterNode<K, V>> | R> = [],
|
||||
options?: AVLTreeCounterOptions<K, V, R>
|
||||
) {
|
||||
super([], options);
|
||||
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
||||
}
|
||||
|
||||
protected _count = 0;
|
||||
|
||||
/**
|
||||
* The function calculates the sum of the count property of all nodes in a tree using depth-first
|
||||
* search.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
get count(): number {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function calculates the sum of the count property of all nodes in a tree using depth-first
|
||||
* search.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
getComputedCount(): number {
|
||||
let sum = 0;
|
||||
this.dfs(node => (sum += node.count));
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new AVLTreeCounterNode with the specified key, value, and count.
|
||||
* @param {K} key - The key parameter represents the key of the node being created. It is of type K,
|
||||
* which is a generic type that can be replaced with any specific type when using the function.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the node. It is of type `V`, which can be any data type.
|
||||
* @param {number} [count] - The `count` parameter represents the number of occurrences of a
|
||||
* key-value pair in the AVLTreeCounterNode. It is an optional parameter, so it can be omitted when
|
||||
* calling the `createNode` method. If provided, it specifies the initial count for the node.
|
||||
* @returns a new instance of the AVLTreeCounterNode class, casted as AVLTreeCounterNode<K, V>.
|
||||
*/
|
||||
override createNode(key: K, value?: V, count?: number): AVLTreeCounterNode<K, V> {
|
||||
return new AVLTreeCounterNode(key, this._isMapMode ? undefined : value, count) as AVLTreeCounterNode<K, V>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new AVLTreeCounter object with the specified options and returns it.
|
||||
* @param [options] - The `options` parameter is an optional object that contains additional
|
||||
* configuration options for creating the AVLTreeCounter. It can have the following properties:
|
||||
* @returns a new instance of the AVLTreeCounter class, with the specified options, as a TREE
|
||||
* object.
|
||||
*/
|
||||
override createTree(options?: AVLTreeCounterOptions<K, V, R>) {
|
||||
return new AVLTreeCounter<K, V, R, MK, MV, MR>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
specifyComparable: this._specifyComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
isReverse: this._isReverse,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if the input is an instance of AVLTreeCounterNode.
|
||||
* @param {BTNRep<K, V, AVLTreeCounterNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can be of type `R` or `BTNRep<K, V, AVLTreeCounterNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is
|
||||
* an instance of the `AVLTreeCounterNode` class.
|
||||
*/
|
||||
override isNode(keyNodeOrEntry: BTNRep<K, V, AVLTreeCounterNode<K, V>>): keyNodeOrEntry is AVLTreeCounterNode<K, V> {
|
||||
return keyNodeOrEntry instanceof AVLTreeCounterNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides the add method of a TypeScript class to add a new node to a data structure
|
||||
* and update the count.
|
||||
* @param {BTNRep<K, V, AVLTreeCounterNode<K, V>>} keyNodeOrEntry - The
|
||||
* `keyNodeOrEntry` parameter can accept a value of type `R`, which can be any type. It
|
||||
* can also accept a value of type `BTNRep<K, V, AVLTreeCounterNode<K, V>>`, which represents a key, node,
|
||||
* entry, or raw element
|
||||
* @param {V} [value] - The `value` parameter represents the value associated with the key in the
|
||||
* data structure. It is an optional parameter, so it can be omitted if not needed.
|
||||
* @param [count=1] - The `count` parameter represents the number of times the key-value pair should
|
||||
* be added to the data structure. By default, it is set to 1, meaning that the key-value pair will
|
||||
* be added once. However, you can specify a different value for `count` if you want to add
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
override add(keyNodeOrEntry: BTNRep<K, V, AVLTreeCounterNode<K, V>>, value?: V, count = 1): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value, count);
|
||||
if (newNode === undefined) return false;
|
||||
|
||||
const orgNodeCount = newNode?.count || 0;
|
||||
const inserted = super.add(newNode, newValue);
|
||||
if (inserted) {
|
||||
this._count += orgNodeCount;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides the delete method in a binary tree data structure, handling deletion of
|
||||
* nodes and maintaining balance in the tree.
|
||||
* @param {BTNRep<K, V, AVLTreeCounterNode<K, V>>} keyNodeOrEntry - The `predicate`
|
||||
* parameter in the `delete` method is used to specify the condition for deleting a node from the
|
||||
* binary tree. It can be a key, node, or entry that determines which
|
||||
* node(s) should be deleted.
|
||||
* @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a
|
||||
* boolean flag that determines whether to ignore the count of the node being deleted. If
|
||||
* `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If
|
||||
* `ignoreCount` is set to
|
||||
* @returns The `delete` method overrides the default delete behavior in a binary tree data
|
||||
* structure. It takes a predicate or node to be deleted and an optional flag to ignore count. The
|
||||
* method returns an array of `BinaryTreeDeleteResult` objects, each containing information about the
|
||||
* deleted node and whether balancing is needed in the tree.
|
||||
*/
|
||||
override delete(
|
||||
keyNodeOrEntry: BTNRep<K, V, AVLTreeCounterNode<K, V>>,
|
||||
ignoreCount = false
|
||||
): BinaryTreeDeleteResult<AVLTreeCounterNode<K, V>>[] {
|
||||
const deletedResult: BinaryTreeDeleteResult<AVLTreeCounterNode<K, V>>[] = [];
|
||||
if (!this.root) return deletedResult;
|
||||
|
||||
const curr: AVLTreeCounterNode<K, V> | undefined = this.getNode(keyNodeOrEntry) ?? undefined;
|
||||
if (!curr) return deletedResult;
|
||||
|
||||
const parent: AVLTreeCounterNode<K, V> | undefined = curr?.parent ? curr.parent : undefined;
|
||||
let needBalanced: AVLTreeCounterNode<K, V> | undefined = undefined,
|
||||
orgCurrent: AVLTreeCounterNode<K, V> | undefined = curr;
|
||||
|
||||
if (curr.count > 1 && !ignoreCount) {
|
||||
curr.count--;
|
||||
this._count--;
|
||||
} else {
|
||||
if (!curr.left) {
|
||||
if (!parent) {
|
||||
if (curr.right !== undefined && curr.right !== null) this._setRoot(curr.right);
|
||||
} else {
|
||||
const { familyPosition: fp } = curr;
|
||||
if (fp === 'LEFT' || fp === 'ROOT_LEFT') {
|
||||
parent.left = curr.right;
|
||||
} else if (fp === 'RIGHT' || fp === 'ROOT_RIGHT') {
|
||||
parent.right = curr.right;
|
||||
}
|
||||
needBalanced = parent;
|
||||
}
|
||||
} else {
|
||||
const leftSubTreeRightMost = curr.left ? this.getRightMost(node => node, curr.left) : undefined;
|
||||
if (leftSubTreeRightMost) {
|
||||
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
|
||||
orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
|
||||
if (parentOfLeftSubTreeMax) {
|
||||
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) {
|
||||
parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
|
||||
} else {
|
||||
parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
|
||||
}
|
||||
needBalanced = parentOfLeftSubTreeMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._size = this._size - 1;
|
||||
// TODO How to handle when the count of target node is lesser than current node's count
|
||||
if (orgCurrent) this._count -= orgCurrent.count;
|
||||
}
|
||||
|
||||
deletedResult.push({ deleted: orgCurrent, needBalanced });
|
||||
|
||||
if (needBalanced) {
|
||||
this._balancePath(needBalanced);
|
||||
}
|
||||
|
||||
return deletedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The "clear" function overrides the parent class's "clear" function and also resets the count to
|
||||
* zero.
|
||||
*/
|
||||
override clear() {
|
||||
super.clear();
|
||||
this._count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n log n)
|
||||
* Space Complexity: O(log n)
|
||||
* The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search
|
||||
* tree using either a recursive or iterative approach.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that
|
||||
* specifies the type of iteration to use when building the balanced binary search tree. It has a
|
||||
* default value of `this.iterationType`, which means it will use the iteration type currently set in
|
||||
* the object.
|
||||
* @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the
|
||||
* balancing operation is successful, and `false` if there are no nodes to balance.
|
||||
*/
|
||||
override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'IN'),
|
||||
n = sorted.length;
|
||||
if (sorted.length < 1) return false;
|
||||
|
||||
this.clear();
|
||||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const buildBalanceBST = (l: number, r: number) => {
|
||||
if (l > r) return;
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
buildBalanceBST(l, m - 1);
|
||||
buildBalanceBST(m + 1, r);
|
||||
};
|
||||
|
||||
buildBalanceBST(0, n - 1);
|
||||
return true;
|
||||
} else {
|
||||
const stack: [[number, number]] = [[0, n - 1]];
|
||||
while (stack.length > 0) {
|
||||
const popped = stack.pop();
|
||||
if (popped) {
|
||||
const [l, r] = popped;
|
||||
if (l <= r) {
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
stack.push([m + 1, r]);
|
||||
stack.push([l, m - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
*
|
||||
* The function overrides the clone method to create a deep copy of a tree object.
|
||||
* @returns The `clone()` method is returning a cloned instance of the `TREE` object.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
if (this._isMapMode) this.bfs(node => cloned.add(node.key, undefined, node.count));
|
||||
else this.bfs(node => cloned.add(node.key, node.value, node.count));
|
||||
if (this._isMapMode) cloned._store = this._store;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `map` function in TypeScript overrides the default behavior to create a new AVLTreeCounter
|
||||
* with modified entries based on a provided callback.
|
||||
* @param callback - The `callback` parameter is a function that will be called for each entry in the
|
||||
* AVLTreeCounter. It takes four arguments:
|
||||
* @param [options] - The `options` parameter in the `override map` function is of type
|
||||
* `AVLTreeCounterOptions<MK, MV, MR>`. This parameter allows you to provide additional
|
||||
* configuration options when creating a new `AVLTreeCounter` instance within the `map` function.
|
||||
* These options
|
||||
* @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify
|
||||
* the value of `this` when executing the `callback` function. It allows you to set the context
|
||||
* (value of `this`) for the callback function. This can be useful when you want to access properties
|
||||
* or
|
||||
* @returns The `map` method is returning a new `AVLTreeCounter` instance with the entries
|
||||
* transformed by the provided `callback` function. Each entry in the original tree is passed to the
|
||||
* `callback` function along with the index and the original tree itself. The transformed entries are
|
||||
* then added to the new `AVLTreeCounter` instance, which is returned at the end.
|
||||
*/
|
||||
override map<MK, MV, MR>(
|
||||
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
||||
options?: AVLTreeCounterOptions<MK, MV, MR>,
|
||||
thisArg?: any
|
||||
): AVLTreeCounter<MK, MV, MR> {
|
||||
const newTree = new AVLTreeCounter<MK, MV, MR>([], options);
|
||||
let index = 0;
|
||||
for (const [key, value] of this) {
|
||||
newTree.add(callback.call(thisArg, key, value, index++, this));
|
||||
}
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into
|
||||
* a node object.
|
||||
* @param {BTNRep<K, V, AVLTreeCounterNode<K, V>>} keyNodeOrEntry - The
|
||||
* `keyNodeOrEntry` parameter can be of type `R` or `BTNRep<K, V, AVLTreeCounterNode<K, V>>`.
|
||||
* @param {V} [value] - The `value` parameter is an optional value that can be passed to the
|
||||
* `override` function. It represents the value associated with the key in the data structure. If no
|
||||
* value is provided, it will default to `undefined`.
|
||||
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
|
||||
* times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
|
||||
* @returns either a AVLTreeCounterNode<K, V> object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeOrEntryToNodeAndValue(
|
||||
keyNodeOrEntry: BTNRep<K, V, AVLTreeCounterNode<K, V>>,
|
||||
value?: V,
|
||||
count = 1
|
||||
): [AVLTreeCounterNode<K, V> | undefined, V | undefined] {
|
||||
if (keyNodeOrEntry === undefined || keyNodeOrEntry === null) return [undefined, undefined];
|
||||
if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value];
|
||||
|
||||
if (this.isEntry(keyNodeOrEntry)) {
|
||||
const [key, entryValue] = keyNodeOrEntry;
|
||||
if (key === undefined || key === null) return [undefined, undefined];
|
||||
const finalValue = value ?? entryValue;
|
||||
return [this.createNode(key, finalValue, count), finalValue];
|
||||
}
|
||||
|
||||
return [this.createNode(keyNodeOrEntry, value, count), value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `_swapProperties` function swaps the properties (key, value, count, height) between two nodes
|
||||
* in a binary search tree.
|
||||
* @param {BSTNOptKeyOrNode<K, AVLTreeCounterNode<K, V>>} srcNode - The `srcNode` parameter represents the source node
|
||||
* that will be swapped with the `destNode`.
|
||||
* @param {BSTNOptKeyOrNode<K, AVLTreeCounterNode<K, V>>} destNode - The `destNode` parameter represents the destination
|
||||
* node where the properties will be swapped with the source node.
|
||||
* @returns The method is returning the `destNode` after swapping its properties with the `srcNode`.
|
||||
* If either `srcNode` or `destNode` is undefined, it returns `undefined`.
|
||||
*/
|
||||
protected override _swapProperties(
|
||||
srcNode: BSTNOptKeyOrNode<K, AVLTreeCounterNode<K, V>>,
|
||||
destNode: BSTNOptKeyOrNode<K, AVLTreeCounterNode<K, V>>
|
||||
): AVLTreeCounterNode<K, V> | undefined {
|
||||
srcNode = this.ensureNode(srcNode);
|
||||
destNode = this.ensureNode(destNode);
|
||||
if (srcNode && destNode) {
|
||||
const { key, value, count, height } = destNode;
|
||||
const tempNode = this.createNode(key, value, count);
|
||||
if (tempNode) {
|
||||
tempNode.height = height;
|
||||
|
||||
destNode.key = srcNode.key;
|
||||
if (!this._isMapMode) destNode.value = srcNode.value;
|
||||
destNode.count = srcNode.count;
|
||||
destNode.height = srcNode.height;
|
||||
|
||||
srcNode.key = tempNode.key;
|
||||
if (!this._isMapMode) srcNode.value = tempNode.value;
|
||||
srcNode.count = tempNode.count;
|
||||
srcNode.height = tempNode.height;
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function replaces an old node with a new node and updates the count property of the new node.
|
||||
* @param {AVLTreeCounterNode<K, V>} oldNode - The oldNode parameter represents the node that needs to be replaced in the
|
||||
* data structure. It is of type AVLTreeCounterNode<K, V>.
|
||||
* @param {AVLTreeCounterNode<K, V>} newNode - The `newNode` parameter is an instance of the `AVLTreeCounterNode<K, V>` class.
|
||||
* @returns The method is returning the result of calling the `_replaceNode` method from the
|
||||
* superclass, which is of type `AVLTreeCounterNode<K, V>`.
|
||||
*/
|
||||
protected override _replaceNode(
|
||||
oldNode: AVLTreeCounterNode<K, V>,
|
||||
newNode: AVLTreeCounterNode<K, V>
|
||||
): AVLTreeCounterNode<K, V> {
|
||||
newNode.count = oldNode.count + newNode.count;
|
||||
return super._replaceNode(oldNode, newNode);
|
||||
}
|
||||
}
|
|
@ -5,32 +5,21 @@
|
|||
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
import type {
|
||||
AVLTreeMultiMapOptions,
|
||||
BinaryTreeDeleteResult,
|
||||
BSTNOptKeyOrNode,
|
||||
BTNRep,
|
||||
EntryCallback,
|
||||
IterationType,
|
||||
OptNodeOrNull
|
||||
} from '../../types';
|
||||
import { IBinaryTree } from '../../interfaces';
|
||||
import { AVLTreeMultiMapOptions, BTNOptKeyOrNull, BTNRep, OptNodeOrNull } from '../../types';
|
||||
import { AVLTree, AVLTreeNode } from './avl-tree';
|
||||
|
||||
export class AVLTreeMultiMapNode<K = any, V = any> extends AVLTreeNode<K, V> {
|
||||
export class AVLTreeMultiMapNode<K = any, V = any> extends AVLTreeNode<K, V[]> {
|
||||
/**
|
||||
* The constructor function initializes a BinaryTreeNode object with a key, value, and count.
|
||||
* @param {K} key - The `key` parameter is of type `K` and represents the unique identifier
|
||||
* of the binary tree node.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter of type `V`. It represents the value of the binary
|
||||
* tree node. If no value is provided, it will be `undefined`.
|
||||
* @param {number} [count=1] - The `count` parameter is a number that represents the number of times a particular value
|
||||
* occurs in a binary tree node. It has a default value of 1, which means that if no value is provided for the `count`
|
||||
* parameter when creating a new instance of the `BinaryTreeNode` class.
|
||||
* This TypeScript constructor initializes an object with a key of type K and an array of values of
|
||||
* type V.
|
||||
* @param {K} key - The `key` parameter is typically used to store a unique identifier or key for the
|
||||
* data being stored in the data structure. It helps in quickly accessing or retrieving the
|
||||
* associated value in the data structure.
|
||||
* @param {V[]} value - The `value` parameter in the constructor represents an array of values of
|
||||
* type `V`.
|
||||
*/
|
||||
constructor(key: K, value?: V, count = 1) {
|
||||
constructor(key: K, value: V[]) {
|
||||
super(key, value);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
override parent?: AVLTreeMultiMapNode<K, V> = undefined;
|
||||
|
@ -63,79 +52,52 @@ export class AVLTreeMultiMapNode<K = any, V = any> extends AVLTreeNode<K, V> {
|
|||
}
|
||||
|
||||
/**
|
||||
* The only distinction between a AVLTreeMultiMap and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters.
|
||||
*
|
||||
*/
|
||||
export class AVLTreeMultiMap<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
||||
extends AVLTree<K, V, R, MK, MV, MR>
|
||||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
export class AVLTreeMultiMap<K = any, V = any, R = object, MK = any, MV = any, MR = object> extends AVLTree<
|
||||
K,
|
||||
V[],
|
||||
R,
|
||||
MK,
|
||||
MV,
|
||||
MR
|
||||
> {
|
||||
/**
|
||||
* The constructor initializes a new AVLTreeMultiMap object with optional initial elements.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an
|
||||
* iterable object that can contain either keys, nodes, entries, or raw elements.
|
||||
* @param [options] - The `options` parameter is an optional object that can be used to customize the
|
||||
* behavior of the AVLTreeMultiMap. It can include properties such as `compareKeys` and
|
||||
* `compareValues` functions to define custom comparison logic for keys and values, respectively.
|
||||
* The constructor initializes an AVLTreeMultiMap with the provided keys, nodes, entries, or raw data
|
||||
* and options.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an
|
||||
* iterable that can contain either key-value pairs represented as `BTNRep<K, V[],
|
||||
* AVLTreeMultiMapNode<K, V>>` or raw data represented as `R`. This parameter is used to initialize
|
||||
* the AVLTreeMulti
|
||||
* @param [options] - The `options` parameter in the constructor is of type
|
||||
* `AVLTreeMultiMapOptions<K, V[], R>`. It is an optional parameter that allows you to specify
|
||||
* additional options for configuring the AVLTreeMultiMap instance.
|
||||
*/
|
||||
constructor(
|
||||
keysNodesEntriesOrRaws: Iterable<R | BTNRep<K, V, AVLTreeMultiMapNode<K, V>>> = [],
|
||||
options?: AVLTreeMultiMapOptions<K, V, R>
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V[], AVLTreeMultiMapNode<K, V>> | R> = [],
|
||||
options?: AVLTreeMultiMapOptions<K, V[], R>
|
||||
) {
|
||||
super([], options);
|
||||
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
||||
}
|
||||
|
||||
protected _count = 0;
|
||||
|
||||
/**
|
||||
* The function calculates the sum of the count property of all nodes in a tree using depth-first
|
||||
* search.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
get count(): number {
|
||||
return this._count;
|
||||
super([], { ...options, isMapMode: true });
|
||||
if (keysNodesEntriesOrRaws) {
|
||||
this.addMany(keysNodesEntriesOrRaws);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function calculates the sum of the count property of all nodes in a tree using depth-first
|
||||
* search.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
* The function `createTree` in TypeScript overrides the creation of an AVLTreeMultiMap with
|
||||
* specified options.
|
||||
* @param [options] - The `options` parameter in the `createTree` function is of type
|
||||
* `AVLTreeMultiMapOptions<K, V[], R>`. This means it is an object that can have properties of type
|
||||
* `K`, `V[]`, and `R`. The function creates a new `AVL
|
||||
* @returns The `createTree` method is returning a new instance of `AVLTreeMultiMap` with the
|
||||
* provided options.
|
||||
*/
|
||||
getComputedCount(): number {
|
||||
let sum = 0;
|
||||
this.dfs(node => (sum += node.count));
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new AVLTreeMultiMapNode with the specified key, value, and count.
|
||||
* @param {K} key - The key parameter represents the key of the node being created. It is of type K,
|
||||
* which is a generic type that can be replaced with any specific type when using the function.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the node. It is of type `V`, which can be any data type.
|
||||
* @param {number} [count] - The `count` parameter represents the number of occurrences of a
|
||||
* key-value pair in the AVLTreeMultiMapNode. It is an optional parameter, so it can be omitted when
|
||||
* calling the `createNode` method. If provided, it specifies the initial count for the node.
|
||||
* @returns a new instance of the AVLTreeMultiMapNode class, casted as AVLTreeMultiMapNode<K, V>.
|
||||
*/
|
||||
override createNode(key: K, value?: V, count?: number): AVLTreeMultiMapNode<K, V> {
|
||||
return new AVLTreeMultiMapNode(key, this._isMapMode ? undefined : value, count) as AVLTreeMultiMapNode<K, V>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new AVLTreeMultiMap object with the specified options and returns it.
|
||||
* @param [options] - The `options` parameter is an optional object that contains additional
|
||||
* configuration options for creating the AVLTreeMultiMap. It can have the following properties:
|
||||
* @returns a new instance of the AVLTreeMultiMap class, with the specified options, as a TREE
|
||||
* object.
|
||||
*/
|
||||
override createTree(options?: AVLTreeMultiMapOptions<K, V, R>) {
|
||||
override createTree(options?: AVLTreeMultiMapOptions<K, V[], R>) {
|
||||
return new AVLTreeMultiMap<K, V, R, MK, MV, MR>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
specifyComparable: this._specifyComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
isReverse: this._isReverse,
|
||||
|
@ -143,331 +105,121 @@ export class AVLTreeMultiMap<K = any, V = any, R = object, MK = any, MV = any, M
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if the input is an instance of AVLTreeMultiMapNode.
|
||||
* @param {BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, AVLTreeMultiMapNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is
|
||||
* an instance of the `AVLTreeMultiMapNode` class.
|
||||
*/
|
||||
override isNode(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R
|
||||
): keyNodeEntryOrRaw is AVLTreeMultiMapNode<K, V> {
|
||||
return keyNodeEntryOrRaw instanceof AVLTreeMultiMapNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides the add method of a TypeScript class to add a new node to a data structure
|
||||
* and update the count.
|
||||
* @param {BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The
|
||||
* `keyNodeEntryOrRaw` parameter can accept a value of type `R`, which can be any type. It
|
||||
* can also accept a value of type `BTNRep<K, V, AVLTreeMultiMapNode<K, V>>`, which represents a key, node,
|
||||
* entry, or raw element
|
||||
* @param {V} [value] - The `value` parameter represents the value associated with the key in the
|
||||
* data structure. It is an optional parameter, so it can be omitted if not needed.
|
||||
* @param [count=1] - The `count` parameter represents the number of times the key-value pair should
|
||||
* be added to the data structure. By default, it is set to 1, meaning that the key-value pair will
|
||||
* be added once. However, you can specify a different value for `count` if you want to add
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R, value?: V, count = 1): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
|
||||
if (newNode === undefined) return false;
|
||||
|
||||
const orgNodeCount = newNode?.count || 0;
|
||||
const inserted = super.add(newNode, newValue);
|
||||
if (inserted) {
|
||||
this._count += orgNodeCount;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides the delete method in a binary tree data structure, handling deletion of
|
||||
* nodes and maintaining balance in the tree.
|
||||
* @param {BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The `predicate`
|
||||
* parameter in the `delete` method is used to specify the condition for deleting a node from the
|
||||
* binary tree. It can be a key, node, or entry that determines which
|
||||
* node(s) should be deleted.
|
||||
* @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a
|
||||
* boolean flag that determines whether to ignore the count of the node being deleted. If
|
||||
* `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If
|
||||
* `ignoreCount` is set to
|
||||
* @returns The `delete` method overrides the default delete behavior in a binary tree data
|
||||
* structure. It takes a predicate or node to be deleted and an optional flag to ignore count. The
|
||||
* method returns an array of `BinaryTreeDeleteResult` objects, each containing information about the
|
||||
* deleted node and whether balancing is needed in the tree.
|
||||
*/
|
||||
override delete(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R,
|
||||
ignoreCount = false
|
||||
): BinaryTreeDeleteResult<AVLTreeMultiMapNode<K, V>>[] {
|
||||
const deletedResult: BinaryTreeDeleteResult<AVLTreeMultiMapNode<K, V>>[] = [];
|
||||
if (!this.root) return deletedResult;
|
||||
|
||||
const curr: AVLTreeMultiMapNode<K, V> | undefined = this.getNode(keyNodeEntryOrRaw) ?? undefined;
|
||||
if (!curr) return deletedResult;
|
||||
|
||||
const parent: AVLTreeMultiMapNode<K, V> | undefined = curr?.parent ? curr.parent : undefined;
|
||||
let needBalanced: AVLTreeMultiMapNode<K, V> | undefined = undefined,
|
||||
orgCurrent: AVLTreeMultiMapNode<K, V> | undefined = curr;
|
||||
|
||||
if (curr.count > 1 && !ignoreCount) {
|
||||
curr.count--;
|
||||
this._count--;
|
||||
} else {
|
||||
if (!curr.left) {
|
||||
if (!parent) {
|
||||
if (curr.right !== undefined && curr.right !== null) this._setRoot(curr.right);
|
||||
} else {
|
||||
const { familyPosition: fp } = curr;
|
||||
if (fp === 'LEFT' || fp === 'ROOT_LEFT') {
|
||||
parent.left = curr.right;
|
||||
} else if (fp === 'RIGHT' || fp === 'ROOT_RIGHT') {
|
||||
parent.right = curr.right;
|
||||
}
|
||||
needBalanced = parent;
|
||||
}
|
||||
} else {
|
||||
const leftSubTreeRightMost = curr.left ? this.getRightMost(node => node, curr.left) : undefined;
|
||||
if (leftSubTreeRightMost) {
|
||||
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
|
||||
orgCurrent = this._swapProperties(curr, leftSubTreeRightMost);
|
||||
if (parentOfLeftSubTreeMax) {
|
||||
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) {
|
||||
parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
|
||||
} else {
|
||||
parentOfLeftSubTreeMax.left = leftSubTreeRightMost.left;
|
||||
}
|
||||
needBalanced = parentOfLeftSubTreeMax;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._size = this._size - 1;
|
||||
// TODO How to handle when the count of target node is lesser than current node's count
|
||||
if (orgCurrent) this._count -= orgCurrent.count;
|
||||
}
|
||||
|
||||
deletedResult.push({ deleted: orgCurrent, needBalanced });
|
||||
|
||||
if (needBalanced) {
|
||||
this._balancePath(needBalanced);
|
||||
}
|
||||
|
||||
return deletedResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The "clear" function overrides the parent class's "clear" function and also resets the count to
|
||||
* zero.
|
||||
* The function `createNode` overrides the method to create a new AVLTreeMultiMapNode with a
|
||||
* specified key and an empty array of values.
|
||||
* @param {K} key - The `key` parameter in the `createNode` method represents the key of the node
|
||||
* that will be created in the AVLTreeMultiMap.
|
||||
* @returns An AVLTreeMultiMapNode object is being returned, initialized with the provided key and an
|
||||
* empty array.
|
||||
*/
|
||||
override clear() {
|
||||
super.clear();
|
||||
this._count = 0;
|
||||
override createNode(key: K): AVLTreeMultiMapNode<K, V> {
|
||||
return new AVLTreeMultiMapNode<K, V>(key, []);
|
||||
}
|
||||
|
||||
override add(node: BTNRep<K, V[], AVLTreeMultiMapNode<K, V>>): boolean;
|
||||
|
||||
override add(key: K, value: V): boolean;
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function `add` in TypeScript overrides the superclass method to add key-value pairs to an AVL
|
||||
* tree multi-map.
|
||||
* @param {BTNRep<K, V[], AVLTreeMultiMapNode<K, V>> | K} keyNodeOrEntry - The `keyNodeOrEntry`
|
||||
* parameter in the `override add` method can be either a key-value pair entry or just a key. If it
|
||||
* is a key-value pair entry, it will be in the format `[key, values]`, where `key` is the key and
|
||||
* `values`
|
||||
* @param {V} [value] - The `value` parameter in the `override add` method represents the value that
|
||||
* you want to add to the AVLTreeMultiMap. It can be a single value or an array of values associated
|
||||
* with a specific key.
|
||||
* @returns The `override add` method is returning a boolean value, which indicates whether the
|
||||
* addition operation was successful or not.
|
||||
*/
|
||||
override add(keyNodeOrEntry: BTNRep<K, V[], AVLTreeMultiMapNode<K, V>> | K, value?: V): boolean {
|
||||
if (this.isRealNode(keyNodeOrEntry)) return super.add(keyNodeOrEntry);
|
||||
|
||||
const _commonAdd = (key?: BTNOptKeyOrNull<K>, values?: V[]) => {
|
||||
if (key === undefined || key === null) return false;
|
||||
|
||||
const existingValues = this.get(key);
|
||||
if (existingValues !== undefined && values !== undefined) {
|
||||
for (const value of values) existingValues.push(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
const existingNode = this.getNode(key);
|
||||
if (this.isRealNode(existingNode)) {
|
||||
if (existingValues === undefined) {
|
||||
super.add(key, values);
|
||||
return true;
|
||||
}
|
||||
if (values !== undefined) {
|
||||
for (const value of values) existingValues.push(value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return super.add(key, values);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.isEntry(keyNodeOrEntry)) {
|
||||
const [key, values] = keyNodeOrEntry;
|
||||
return _commonAdd(key, value !== undefined ? [value] : values);
|
||||
}
|
||||
|
||||
return _commonAdd(keyNodeOrEntry, value !== undefined ? [value] : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n log n)
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(log n)
|
||||
* The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search
|
||||
* tree using either a recursive or iterative approach.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that
|
||||
* specifies the type of iteration to use when building the balanced binary search tree. It has a
|
||||
* default value of `this.iterationType`, which means it will use the iteration type currently set in
|
||||
* the object.
|
||||
* @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the
|
||||
* balancing operation is successful, and `false` if there are no nodes to balance.
|
||||
*
|
||||
* The function `deleteValue` removes a specific value from a key in an AVLTreeMultiMap data
|
||||
* structure and deletes the entire node if no values are left for that key.
|
||||
* @param {BTNRep<K, V[], AVLTreeMultiMapNode<K, V>> | K} keyNodeOrEntry - The `keyNodeOrEntry`
|
||||
* parameter in the `deleteValue` function can be either a `BTNRep` object representing a key-value
|
||||
* pair in the AVLTreeMultiMapNode, or just the key itself.
|
||||
* @param {V} value - The `value` parameter in the `deleteValue` function represents the specific
|
||||
* value that you want to delete from the multi-map data structure associated with a particular key.
|
||||
* The function checks if the value exists in the array of values associated with the key, and if
|
||||
* found, removes it from the array.
|
||||
* @returns The `deleteValue` function returns a boolean value. It returns `true` if the specified
|
||||
* `value` was successfully deleted from the array of values associated with the `keyNodeOrEntry`. If
|
||||
* the value was not found in the array, it returns `false`.
|
||||
*/
|
||||
override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'IN'),
|
||||
n = sorted.length;
|
||||
if (sorted.length < 1) return false;
|
||||
deleteValue(keyNodeOrEntry: BTNRep<K, V[], AVLTreeMultiMapNode<K, V>> | K, value: V): boolean {
|
||||
const values = this.get(keyNodeOrEntry);
|
||||
if (Array.isArray(values)) {
|
||||
const index = values.indexOf(value);
|
||||
if (index === -1) return false;
|
||||
values.splice(index, 1);
|
||||
|
||||
this.clear();
|
||||
// If no values left, remove the entire node
|
||||
if (values.length === 0) this.delete(keyNodeOrEntry);
|
||||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const buildBalanceBST = (l: number, r: number) => {
|
||||
if (l > r) return;
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
buildBalanceBST(l, m - 1);
|
||||
buildBalanceBST(m + 1, r);
|
||||
};
|
||||
|
||||
buildBalanceBST(0, n - 1);
|
||||
return true;
|
||||
} else {
|
||||
const stack: [[number, number]] = [[0, n - 1]];
|
||||
while (stack.length > 0) {
|
||||
const popped = stack.pop();
|
||||
if (popped) {
|
||||
const [l, r] = popped;
|
||||
if (l <= r) {
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
stack.push([m + 1, r]);
|
||||
stack.push([l, m - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* The function overrides the clone method to create a deep copy of a tree object.
|
||||
* @returns The `clone()` method is returning a cloned instance of the `TREE` object.
|
||||
* The function `clone` overrides the default cloning behavior to create a deep copy of a tree
|
||||
* structure.
|
||||
* @returns A cloned tree object is being returned.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
if (this._isMapMode) this.bfs(node => cloned.add(node.key, undefined, node.count));
|
||||
else this.bfs(node => cloned.add(node.key, node.value, node.count));
|
||||
if (this._isMapMode) cloned._store = this._store;
|
||||
this._clone(cloned);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `map` function in TypeScript overrides the default behavior to create a new AVLTreeMultiMap
|
||||
* with modified entries based on a provided callback.
|
||||
* @param callback - The `callback` parameter is a function that will be called for each entry in the
|
||||
* AVLTreeMultiMap. It takes four arguments:
|
||||
* @param [options] - The `options` parameter in the `override map` function is of type
|
||||
* `AVLTreeMultiMapOptions<MK, MV, MR>`. This parameter allows you to provide additional
|
||||
* configuration options when creating a new `AVLTreeMultiMap` instance within the `map` function.
|
||||
* These options
|
||||
* @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify
|
||||
* the value of `this` when executing the `callback` function. It allows you to set the context
|
||||
* (value of `this`) for the callback function. This can be useful when you want to access properties
|
||||
* or
|
||||
* @returns The `map` method is returning a new `AVLTreeMultiMap` instance with the entries
|
||||
* transformed by the provided `callback` function. Each entry in the original tree is passed to the
|
||||
* `callback` function along with the index and the original tree itself. The transformed entries are
|
||||
* then added to the new `AVLTreeMultiMap` instance, which is returned at the end.
|
||||
*/
|
||||
override map<MK, MV, MR>(
|
||||
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
||||
options?: AVLTreeMultiMapOptions<MK, MV, MR>,
|
||||
thisArg?: any
|
||||
): AVLTreeMultiMap<MK, MV, MR> {
|
||||
const newTree = new AVLTreeMultiMap<MK, MV, MR>([], options);
|
||||
let index = 0;
|
||||
for (const [key, value] of this) {
|
||||
newTree.add(callback.call(thisArg, key, value, index++, this));
|
||||
}
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into
|
||||
* a node object.
|
||||
* @param {BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The
|
||||
* `keyNodeEntryOrRaw` parameter can be of type `R` or `BTNRep<K, V, AVLTreeMultiMapNode<K, V>>`.
|
||||
* @param {V} [value] - The `value` parameter is an optional value that can be passed to the
|
||||
* `override` function. It represents the value associated with the key in the data structure. If no
|
||||
* value is provided, it will default to `undefined`.
|
||||
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
|
||||
* times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
|
||||
* @returns either a AVLTreeMultiMapNode<K, V> object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeMultiMapNode<K, V>> | R,
|
||||
value?: V,
|
||||
count = 1
|
||||
): [AVLTreeMultiMapNode<K, V> | undefined, V | undefined] {
|
||||
if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined];
|
||||
if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value];
|
||||
|
||||
if (this.isEntry(keyNodeEntryOrRaw)) {
|
||||
const [key, entryValue] = keyNodeEntryOrRaw;
|
||||
if (key === undefined || key === null) return [undefined, undefined];
|
||||
const finalValue = value ?? entryValue;
|
||||
return [this.createNode(key, finalValue, count), finalValue];
|
||||
}
|
||||
|
||||
if (this.isRaw(keyNodeEntryOrRaw)) {
|
||||
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue];
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value];
|
||||
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `_swapProperties` function swaps the properties (key, value, count, height) between two nodes
|
||||
* in a binary search tree.
|
||||
* @param {R | BSTNOptKeyOrNode<K, AVLTreeMultiMapNode<K, V>>} srcNode - The `srcNode` parameter represents the source node
|
||||
* that will be swapped with the `destNode`.
|
||||
* @param {R | BSTNOptKeyOrNode<K, AVLTreeMultiMapNode<K, V>>} destNode - The `destNode` parameter represents the destination
|
||||
* node where the properties will be swapped with the source node.
|
||||
* @returns The method is returning the `destNode` after swapping its properties with the `srcNode`.
|
||||
* If either `srcNode` or `destNode` is undefined, it returns `undefined`.
|
||||
*/
|
||||
protected override _swapProperties(
|
||||
srcNode: R | BSTNOptKeyOrNode<K, AVLTreeMultiMapNode<K, V>>,
|
||||
destNode: R | BSTNOptKeyOrNode<K, AVLTreeMultiMapNode<K, V>>
|
||||
): AVLTreeMultiMapNode<K, V> | undefined {
|
||||
srcNode = this.ensureNode(srcNode);
|
||||
destNode = this.ensureNode(destNode);
|
||||
if (srcNode && destNode) {
|
||||
const { key, value, count, height } = destNode;
|
||||
const tempNode = this.createNode(key, value, count);
|
||||
if (tempNode) {
|
||||
tempNode.height = height;
|
||||
|
||||
destNode.key = srcNode.key;
|
||||
if (!this._isMapMode) destNode.value = srcNode.value;
|
||||
destNode.count = srcNode.count;
|
||||
destNode.height = srcNode.height;
|
||||
|
||||
srcNode.key = tempNode.key;
|
||||
if (!this._isMapMode) srcNode.value = tempNode.value;
|
||||
srcNode.count = tempNode.count;
|
||||
srcNode.height = tempNode.height;
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function replaces an old node with a new node and updates the count property of the new node.
|
||||
* @param {AVLTreeMultiMapNode<K, V>} oldNode - The oldNode parameter represents the node that needs to be replaced in the
|
||||
* data structure. It is of type AVLTreeMultiMapNode<K, V>.
|
||||
* @param {AVLTreeMultiMapNode<K, V>} newNode - The `newNode` parameter is an instance of the `AVLTreeMultiMapNode<K, V>` class.
|
||||
* @returns The method is returning the result of calling the `_replaceNode` method from the
|
||||
* superclass, which is of type `AVLTreeMultiMapNode<K, V>`.
|
||||
*/
|
||||
protected override _replaceNode(
|
||||
oldNode: AVLTreeMultiMapNode<K, V>,
|
||||
newNode: AVLTreeMultiMapNode<K, V>
|
||||
): AVLTreeMultiMapNode<K, V> {
|
||||
newNode.count = oldNode.count + newNode.count;
|
||||
return super._replaceNode(oldNode, newNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,13 @@ import { IBinaryTree } from '../../interfaces';
|
|||
|
||||
export class AVLTreeNode<K = any, V = any> extends BSTNode<K, V> {
|
||||
/**
|
||||
* The constructor function initializes a new instance of a class with a key and an optional value,
|
||||
* and sets the height property to 0.
|
||||
* @param {K} key - The "key" parameter is of type K, which represents the type of the key for the
|
||||
* constructor. It is used to initialize the key property of the object being created.
|
||||
* @param {V} [value] - The "value" parameter is an optional parameter of type V. It represents the
|
||||
* value associated with the key in the constructor.
|
||||
* This TypeScript constructor function initializes an instance with a key and an optional value.
|
||||
* @param {K} key - The `key` parameter is typically used to uniquely identify an object or element
|
||||
* within a data structure. It serves as a reference or identifier for accessing or manipulating the
|
||||
* associated value or data.
|
||||
* @param {V} [value] - The `value` parameter in the constructor is optional, meaning it does not
|
||||
* have to be provided when creating an instance of the class. If a value is not provided, it will
|
||||
* default to `undefined`.
|
||||
*/
|
||||
constructor(key: K, value?: V) {
|
||||
super(key, value);
|
||||
|
@ -72,18 +73,18 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
/**
|
||||
* This is a constructor function for an AVLTree class that initializes the tree with keys, nodes,
|
||||
* entries, or raw elements.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an
|
||||
* iterable object that can contain either keys, nodes, entries, or raw elements. These elements will
|
||||
* be used to initialize the AVLTree.
|
||||
* @param [options] - The `options` parameter is an optional object that can be used to customize the
|
||||
* behavior of the AVLTree. It can include properties such as `compareFn` (a function used to compare
|
||||
* keys), `allowDuplicates` (a boolean indicating whether duplicate keys are allowed), and
|
||||
* `nodeBuilder` (
|
||||
* This TypeScript constructor initializes an AVLTree with keys, nodes, entries, or raw data provided
|
||||
* in an iterable format.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an
|
||||
* iterable that can contain either `BTNRep<K, V, AVLTreeNode<K, V>>` objects or `R` objects. It is
|
||||
* used to initialize the AVLTree with key-value pairs or raw data entries. If provided
|
||||
* @param [options] - The `options` parameter in the constructor is of type `AVLTreeOptions<K, V,
|
||||
* R>`. It is an optional parameter that allows you to specify additional options for configuring the
|
||||
* AVL tree. These options could include things like custom comparators, initial capacity, or any
|
||||
* other configuration settings specific
|
||||
*/
|
||||
constructor(
|
||||
keysNodesEntriesOrRaws: Iterable<R | BTNRep<K, V, AVLTreeNode<K, V>>> = [],
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V, AVLTreeNode<K, V>> | R> = [],
|
||||
options?: AVLTreeOptions<K, V, R>
|
||||
) {
|
||||
super([], options);
|
||||
|
@ -91,6 +92,9 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function creates a new AVL tree node with the given key and value.
|
||||
* @param {K} key - The key parameter is of type K, which represents the key of the node being
|
||||
* created.
|
||||
|
@ -104,6 +108,9 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function creates a new AVL tree with the specified options and returns it.
|
||||
* @param {AVLTreeOptions} [options] - The `options` parameter is an optional object that can be
|
||||
* passed to the `createTree` function. It is used to customize the behavior of the AVL tree that is
|
||||
|
@ -122,51 +129,53 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function checks if the input is an instance of AVLTreeNode.
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, AVLTreeNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can be of type `R` or `BTNRep<K, V, AVLTreeNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is
|
||||
* an instance of the `AVLTreeNode` class.
|
||||
*/
|
||||
override isNode(keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeNode<K, V>> | R): keyNodeEntryOrRaw is AVLTreeNode<K, V> {
|
||||
return keyNodeEntryOrRaw instanceof AVLTreeNode;
|
||||
override isNode(keyNodeOrEntry: BTNRep<K, V, AVLTreeNode<K, V>>): keyNodeOrEntry is AVLTreeNode<K, V> {
|
||||
return keyNodeOrEntry instanceof AVLTreeNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function overrides the add method of a class and inserts a key-value pair into a data
|
||||
* structure, then balances the path.
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can accept values of type `R`, `BTNRep<K, V, AVLTreeNode<K, V>>`, or
|
||||
* `RawElement`.
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can accept values of type `R`, `BTNRep<K, V, AVLTreeNode<K, V>>`
|
||||
* @param {V} [value] - The `value` parameter is an optional value that you want to associate with
|
||||
* the key or node being added to the data structure.
|
||||
* @returns The method is returning a boolean value.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeNode<K, V>> | R, value?: V): boolean {
|
||||
if (keyNodeEntryOrRaw === null) return false;
|
||||
const inserted = super.add(keyNodeEntryOrRaw, value);
|
||||
if (inserted) this._balancePath(keyNodeEntryOrRaw);
|
||||
override add(keyNodeOrEntry: BTNRep<K, V, AVLTreeNode<K, V>>, value?: V): boolean {
|
||||
if (keyNodeOrEntry === null) return false;
|
||||
const inserted = super.add(keyNodeOrEntry, value);
|
||||
if (inserted) this._balancePath(keyNodeOrEntry);
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function overrides the delete method in a TypeScript class, performs deletion, and then
|
||||
* balances the tree if necessary.
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw`
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>>} keyNodeOrEntry - The `keyNodeOrEntry`
|
||||
* parameter in the `override delete` method can be one of the following types:
|
||||
* @returns The `delete` method is being overridden in this code snippet. It first calls the `delete`
|
||||
* method from the superclass (presumably a parent class) with the provided `predicate`, which could
|
||||
* be a key, node, entry, or a custom predicate. The result of this deletion operation is stored in
|
||||
* `deletedResults`, which is an array of `BinaryTreeDeleteResult` objects.
|
||||
*/
|
||||
override delete(keyNodeEntryOrRaw: BTNRep<K, V, AVLTreeNode<K, V>> | R): BinaryTreeDeleteResult<AVLTreeNode<K, V>>[] {
|
||||
const deletedResults = super.delete(keyNodeEntryOrRaw);
|
||||
override delete(keyNodeOrEntry: BTNRep<K, V, AVLTreeNode<K, V>>): BinaryTreeDeleteResult<AVLTreeNode<K, V>>[] {
|
||||
const deletedResults = super.delete(keyNodeOrEntry);
|
||||
for (const { needBalanced } of deletedResults) {
|
||||
if (needBalanced) {
|
||||
this._balancePath(needBalanced);
|
||||
|
@ -175,6 +184,26 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
return deletedResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* The `map` function in TypeScript overrides the default map behavior of an AVLTree data structure
|
||||
* by applying a callback function to each entry and creating a new AVLTree with the results.
|
||||
* @param callback - A function that will be called for each entry in the AVLTree. It takes four
|
||||
* arguments: the key, the value (which can be undefined), the index of the entry, and a reference to
|
||||
* the AVLTree itself.
|
||||
* @param [options] - The `options` parameter in the `override map` function is of type
|
||||
* `AVLTreeOptions<MK, MV, MR>`. It is an optional parameter that allows you to specify additional
|
||||
* options for the AVL tree being created during the mapping process. These options could include
|
||||
* custom comparators, initial
|
||||
* @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify
|
||||
* the value of `this` when executing the `callback` function. It allows you to set the context
|
||||
* (value of `this`) within the callback function. This can be useful when you want to access
|
||||
* properties or
|
||||
* @returns The `map` method is returning a new AVLTree instance (`newTree`) with the entries
|
||||
* modified by the provided callback function.
|
||||
*/
|
||||
override map(
|
||||
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
||||
options?: AVLTreeOptions<MK, MV, MR>,
|
||||
|
@ -188,6 +217,14 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* The function `clone` overrides the default cloning behavior to create a deep copy of a tree
|
||||
* structure.
|
||||
* @returns A cloned tree object is being returned.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
this._clone(cloned);
|
||||
|
@ -200,16 +237,16 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
*
|
||||
* The `_swapProperties` function swaps the key, value, and height properties between two nodes in a
|
||||
* binary search tree.
|
||||
* @param {R | BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>} srcNode - The `srcNode` parameter represents either a node
|
||||
* @param {BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>} srcNode - The `srcNode` parameter represents either a node
|
||||
* object (`AVLTreeNode<K, V>`) or a key-value pair (`R`) that is being swapped with another node.
|
||||
* @param {R | BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>} destNode - The `destNode` parameter is either an instance of
|
||||
* @param {BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>} destNode - The `destNode` parameter is either an instance of
|
||||
* `R` or an instance of `BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>`.
|
||||
* @returns The method is returning the `destNodeEnsured` object if both `srcNodeEnsured` and
|
||||
* `destNodeEnsured` are truthy. Otherwise, it returns `undefined`.
|
||||
*/
|
||||
protected override _swapProperties(
|
||||
srcNode: R | BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>,
|
||||
destNode: R | BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>
|
||||
srcNode: BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>,
|
||||
destNode: BSTNOptKeyOrNode<K, AVLTreeNode<K, V>>
|
||||
): AVLTreeNode<K, V> | undefined {
|
||||
const srcNodeEnsured = this.ensureNode(srcNode);
|
||||
const destNodeEnsured = this.ensureNode(destNode);
|
||||
|
@ -450,10 +487,10 @@ export class AVLTree<K = any, V = any, R = object, MK = any, MV = any, MR = obje
|
|||
*
|
||||
* The `_balancePath` function is used to update the heights of nodes and perform rotation operations
|
||||
* to restore balance in an AVL tree after inserting a node.
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>> | R} node - The `node` parameter can be of type `R` or
|
||||
* @param {BTNRep<K, V, AVLTreeNode<K, V>>} node - The `node` parameter can be of type `R` or
|
||||
* `BTNRep<K, V, AVLTreeNode<K, V>>`.
|
||||
*/
|
||||
protected _balancePath(node: BTNRep<K, V, AVLTreeNode<K, V>> | R): void {
|
||||
protected _balancePath(node: BTNRep<K, V, AVLTreeNode<K, V>>): void {
|
||||
node = this.ensureNode(node);
|
||||
const path = this.getPathToRoot(node, node => node, false); // first O(log n) + O(log n)
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
*/
|
||||
import { getMSB } from '../../utils';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class BinaryIndexedTree {
|
||||
protected readonly _freq: number;
|
||||
protected readonly _max: number;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,15 @@ import { isComparable } from '../../utils';
|
|||
import { Range } from '../../common';
|
||||
|
||||
export class BSTNode<K = any, V = any> extends BinaryTreeNode<K, V> {
|
||||
/**
|
||||
* This TypeScript constructor function initializes an instance with a key and an optional value.
|
||||
* @param {K} key - The `key` parameter is typically used to uniquely identify an object or element
|
||||
* within a data structure. It serves as a reference or identifier for accessing or manipulating the
|
||||
* associated value.
|
||||
* @param {V} [value] - The `value` parameter in the constructor is optional, meaning it does not
|
||||
* have to be provided when creating an instance of the class. If a value is not provided, it will
|
||||
* default to `undefined`.
|
||||
*/
|
||||
constructor(key: K, value?: V) {
|
||||
super(key, value);
|
||||
}
|
||||
|
@ -130,14 +139,15 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
/**
|
||||
* This is the constructor function for a Binary Search Tree class in TypeScript.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an
|
||||
* iterable that can contain either keys, nodes, entries, or raw elements. These elements will be
|
||||
* added to the binary search tree during the construction of the object.
|
||||
* @param [options] - An optional object that contains additional options for the Binary Search Tree.
|
||||
* It can include a comparator function that defines the order of the elements in the tree.
|
||||
* This TypeScript constructor initializes a binary search tree with optional options and adds
|
||||
* elements if provided.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an
|
||||
* iterable that can contain elements of type `BTNRep<K, V, BSTNode<K, V>>` or `R`. It is used to
|
||||
* initialize the binary search tree with keys, nodes, entries, or raw data.
|
||||
* @param [options] - The `options` parameter is an optional object that can contain the following
|
||||
* properties:
|
||||
*/
|
||||
constructor(keysNodesEntriesOrRaws: Iterable<R | BTNRep<K, V, BSTNode<K, V>>> = [], options?: BSTOptions<K, V, R>) {
|
||||
constructor(keysNodesEntriesOrRaws: Iterable<BTNRep<K, V, BSTNode<K, V>> | R> = [], options?: BSTOptions<K, V, R>) {
|
||||
super([], options);
|
||||
|
||||
if (options) {
|
||||
|
@ -151,21 +161,12 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
|
||||
protected override _root?: BSTNode<K, V> = undefined;
|
||||
|
||||
/**
|
||||
* The function returns the root node of a tree structure.
|
||||
* @returns The `_root` property of the object, which is of type `BSTNode<K, V>` or `undefined`.
|
||||
*/
|
||||
override get root(): OptNode<BSTNode<K, V>> {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
protected _isReverse: boolean = false;
|
||||
|
||||
/**
|
||||
* The above function is a getter method in TypeScript that returns the value of the private property
|
||||
* `_isReverse`.
|
||||
* @returns The `isReverse` property of the object, which is a boolean value.
|
||||
*/
|
||||
get isReverse(): boolean {
|
||||
return this._isReverse;
|
||||
}
|
||||
|
@ -190,26 +191,20 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The function returns the value of the _comparator property.
|
||||
* @returns The `_comparator` property is being returned.
|
||||
*/
|
||||
get comparator() {
|
||||
return this._comparator;
|
||||
}
|
||||
|
||||
protected _specifyComparable?: (key: K) => Comparable;
|
||||
|
||||
/**
|
||||
* This function returns the value of the `_specifyComparable` property.
|
||||
* @returns The method `specifyComparable()` is being returned, which is a getter method for the
|
||||
* `_specifyComparable` property.
|
||||
*/
|
||||
get specifyComparable() {
|
||||
return this._specifyComparable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function creates a new BSTNode with the given key and value and returns it.
|
||||
* @param {K} key - The key parameter is of type K, which represents the type of the key for the node
|
||||
* being created.
|
||||
|
@ -218,10 +213,13 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* @returns The method is returning a new instance of the BSTNode class, casted as the BSTNode<K, V> type.
|
||||
*/
|
||||
override createNode(key: K, value?: V): BSTNode<K, V> {
|
||||
return new BSTNode<K, V>(key, this._isMapMode ? undefined : value) as BSTNode<K, V>;
|
||||
return new BSTNode<K, V>(key, this._isMapMode ? undefined : value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function creates a new binary search tree with the specified options.
|
||||
* @param [options] - The `options` parameter is an optional object that allows you to customize the
|
||||
* behavior of the `createTree` method. It accepts a partial `BSTOptions` object, which has the
|
||||
|
@ -245,8 +243,8 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
*
|
||||
* The function ensures the existence of a node in a data structure and returns it, or undefined if
|
||||
* it doesn't exist.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can accept a value of type `R`, which represents the key, node,
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can accept a value of type `R`, which represents the key, node,
|
||||
* entry, or raw element that needs to be ensured in the tree.
|
||||
* @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter is an optional
|
||||
* parameter that specifies the type of iteration to be used when ensuring a node. It has a default
|
||||
|
@ -255,48 +253,54 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* not be ensured.
|
||||
*/
|
||||
override ensureNode(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, BSTNode<K, V>> | R,
|
||||
keyNodeOrEntry: BTNRep<K, V, BSTNode<K, V>>,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): OptNode<BSTNode<K, V>> {
|
||||
return super.ensureNode(keyNodeEntryOrRaw, iterationType) ?? undefined;
|
||||
return super.ensureNode(keyNodeOrEntry, iterationType) ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function checks if the input is an instance of the BSTNode class.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, BSTNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can be of type `R` or `BTNRep<K, V, BSTNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is
|
||||
* an instance of the `BSTNode` class.
|
||||
*/
|
||||
override isNode(keyNodeEntryOrRaw: BTNRep<K, V, BSTNode<K, V>> | R): keyNodeEntryOrRaw is BSTNode<K, V> {
|
||||
return keyNodeEntryOrRaw instanceof BSTNode;
|
||||
override isNode(keyNodeOrEntry: BTNRep<K, V, BSTNode<K, V>>): keyNodeOrEntry is BSTNode<K, V> {
|
||||
return keyNodeOrEntry instanceof BSTNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function "override isKey" checks if a key is comparable based on a given comparator.
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function "override isValidKey" checks if a key is comparable based on a given comparator.
|
||||
* @param {any} key - The `key` parameter is a value that will be checked to determine if it is of
|
||||
* type `K`.
|
||||
* @returns The `override isKey(key: any): key is K` function is returning a boolean value based on
|
||||
* @returns The `override isValidKey(key: any): key is K` function is returning a boolean value based on
|
||||
* the result of the `isComparable` function with the condition `this._compare !==
|
||||
* this._DEFAULT_COMPARATOR`.
|
||||
*/
|
||||
override isKey(key: any): key is K {
|
||||
override isValidKey(key: any): key is K {
|
||||
return isComparable(key, this._specifyComparable !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The `add` function in TypeScript adds a new node to a binary search tree based on the key value.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep<K, V, BSTNode<K, V>>`.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can accept a value of type `R` or `BTNRep<K, V, BSTNode<K, V>>`.
|
||||
* @param {V} [value] - The `value` parameter is an optional value that can be associated with the
|
||||
* key in the binary search tree. If provided, it will be stored in the node along with the key.
|
||||
* @returns a boolean value.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, BSTNode<K, V>> | R, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
override add(keyNodeOrEntry: BTNRep<K, V, BSTNode<K, V>>, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
||||
if (newNode === undefined) return false;
|
||||
|
||||
if (this._root === undefined) {
|
||||
|
@ -370,8 +374,9 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
}
|
||||
|
||||
if (!isBalanceAdd) {
|
||||
for (const kve of keysNodesEntriesOrRaws) {
|
||||
for (let kve of keysNodesEntriesOrRaws) {
|
||||
const value = valuesIterator?.next().value;
|
||||
if (this.isRaw(kve)) kve = this._toEntryFn!(kve);
|
||||
inserted.push(this.add(kve, value));
|
||||
}
|
||||
return inserted;
|
||||
|
@ -393,19 +398,17 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
|
||||
sorted = realBTNExemplars.sort(({ key: a }, { key: b }) => {
|
||||
let keyA: K | undefined | null, keyB: K | undefined | null;
|
||||
if (this.isEntry(a)) keyA = a[0];
|
||||
if (this.isRaw(a)) keyA = this._toEntryFn!(a)[0];
|
||||
else if (this.isEntry(a)) keyA = a[0];
|
||||
else if (this.isRealNode(a)) keyA = a.key;
|
||||
else if (this._toEntryFn) {
|
||||
keyA = this._toEntryFn(a as R)[0];
|
||||
} else {
|
||||
else {
|
||||
keyA = a as K;
|
||||
}
|
||||
|
||||
if (this.isEntry(b)) keyB = b[0];
|
||||
if (this.isRaw(b)) keyB = this._toEntryFn!(b)[0];
|
||||
else if (this.isEntry(b)) keyB = b[0];
|
||||
else if (this.isRealNode(b)) keyB = b.key;
|
||||
else if (this._toEntryFn) {
|
||||
keyB = this._toEntryFn(b as R)[0];
|
||||
} else {
|
||||
else {
|
||||
keyB = b as K;
|
||||
}
|
||||
|
||||
|
@ -419,7 +422,13 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
if (arr.length === 0) return;
|
||||
|
||||
const mid = Math.floor((arr.length - 1) / 2);
|
||||
const { key, value, orgIndex } = arr[mid];
|
||||
let { key, value } = arr[mid];
|
||||
const { orgIndex } = arr[mid];
|
||||
if (this.isRaw(key)) {
|
||||
const entry = this._toEntryFn!(key);
|
||||
key = entry[0];
|
||||
value = entry[1] ?? value;
|
||||
}
|
||||
inserted[orgIndex] = this.add(key, value);
|
||||
_dfs(arr.slice(0, mid));
|
||||
_dfs(arr.slice(mid + 1));
|
||||
|
@ -434,7 +443,13 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
const [l, r] = popped;
|
||||
if (l <= r) {
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const { key, value, orgIndex } = sorted[m];
|
||||
let { key, value } = sorted[m];
|
||||
const { orgIndex } = sorted[m];
|
||||
if (this.isRaw(key)) {
|
||||
const entry = this._toEntryFn!(key);
|
||||
key = entry[0];
|
||||
value = entry[1] ?? value;
|
||||
}
|
||||
inserted[orgIndex] = this.add(key, value);
|
||||
stack.push([m + 1, r]);
|
||||
stack.push([l, m - 1]);
|
||||
|
@ -458,8 +473,8 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
*
|
||||
* The function `search` in TypeScript overrides the search behavior in a binary tree structure based
|
||||
* on specified criteria.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R | NodePredicate<BSTNode<K, V>>} keyNodeEntryRawOrPredicate - The
|
||||
* `keyNodeEntryRawOrPredicate` parameter in the `override search` method can accept one of the
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | NodePredicate<BSTNode<K, V>>} keyNodeEntryOrPredicate - The
|
||||
* `keyNodeEntryOrPredicate` parameter in the `override search` method can accept one of the
|
||||
* following types:
|
||||
* @param [onlyOne=false] - The `onlyOne` parameter is a boolean flag that determines whether the
|
||||
* search should stop after finding the first matching node. If `onlyOne` is set to `true`, the
|
||||
|
@ -468,7 +483,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* that will be called on each node that matches the search criteria. It is of type `C`, which
|
||||
* extends `NodeCallback<BSTNode<K, V>>`. The callback function should accept a node of type `BSTNode<K, V>` as its
|
||||
* argument and
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} startNode - The `startNode` parameter in the `override search`
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} startNode - The `startNode` parameter in the `override search`
|
||||
* method represents the node from which the search operation will begin. It is the starting point
|
||||
* for searching within the tree data structure. The method ensures that the `startNode` is a valid
|
||||
* node before proceeding with the search operation. If the `
|
||||
|
@ -481,28 +496,28 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* collected in an array and returned as the output of the method.
|
||||
*/
|
||||
override search<C extends NodeCallback<BSTNode<K, V>>>(
|
||||
keyNodeEntryRawOrPredicate: BTNRep<K, V, BSTNode<K, V>> | R | NodePredicate<BSTNode<K, V>> | Range<K>,
|
||||
keyNodeEntryOrPredicate: BTNRep<K, V, BSTNode<K, V>> | NodePredicate<BSTNode<K, V>> | Range<K>,
|
||||
onlyOne = false,
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> | R = this._root,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
if (keyNodeEntryRawOrPredicate === undefined) return [];
|
||||
if (keyNodeEntryRawOrPredicate === null) return [];
|
||||
if (keyNodeEntryOrPredicate === undefined) return [];
|
||||
if (keyNodeEntryOrPredicate === null) return [];
|
||||
startNode = this.ensureNode(startNode);
|
||||
if (!startNode) return [];
|
||||
let predicate: NodePredicate<BSTNode<K, V>>;
|
||||
|
||||
const isRange = this.isRange(keyNodeEntryRawOrPredicate);
|
||||
const isRange = this.isRange(keyNodeEntryOrPredicate);
|
||||
// Set predicate based on parameter type
|
||||
if (isRange) {
|
||||
predicate = node => keyNodeEntryRawOrPredicate.isInRange(node.key, this._comparator);
|
||||
predicate = node => keyNodeEntryOrPredicate.isInRange(node.key, this._comparator);
|
||||
} else {
|
||||
predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
|
||||
predicate = this._ensurePredicate(keyNodeEntryOrPredicate);
|
||||
}
|
||||
const isToLeftByRange = (cur: BSTNode<K, V>) => {
|
||||
if (isRange) {
|
||||
const range = keyNodeEntryRawOrPredicate;
|
||||
const range = keyNodeEntryOrPredicate;
|
||||
const leftS = this.isReverse ? range.high : range.low;
|
||||
const leftI = this.isReverse ? range.includeHigh : range.includeLow;
|
||||
return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0);
|
||||
|
@ -512,7 +527,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
|
||||
const isToRightByRange = (cur: BSTNode<K, V>) => {
|
||||
if (isRange) {
|
||||
const range = keyNodeEntryRawOrPredicate;
|
||||
const range = keyNodeEntryOrPredicate;
|
||||
const rightS = this.isReverse ? range.low : range.high;
|
||||
const rightI = this.isReverse ? range.includeLow : range.includeLow;
|
||||
|
||||
|
@ -533,8 +548,8 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
if (isRange) {
|
||||
if (this.isRealNode(cur.left) && isToLeftByRange(cur)) dfs(cur.left);
|
||||
if (this.isRealNode(cur.right) && isToRightByRange(cur)) dfs(cur.right);
|
||||
} else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
|
||||
const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
|
||||
} else if (!this._isPredicate(keyNodeEntryOrPredicate)) {
|
||||
const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate);
|
||||
if (
|
||||
this.isRealNode(cur.left) &&
|
||||
benchmarkKey !== null &&
|
||||
|
@ -567,8 +582,8 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
if (isRange) {
|
||||
if (this.isRealNode(cur.left) && isToLeftByRange(cur)) stack.push(cur.left);
|
||||
if (this.isRealNode(cur.right) && isToRightByRange(cur)) stack.push(cur.right);
|
||||
} else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
|
||||
const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
|
||||
} else if (!this._isPredicate(keyNodeEntryOrPredicate)) {
|
||||
const benchmarkKey = this._extractKey(keyNodeEntryOrPredicate);
|
||||
if (
|
||||
this.isRealNode(cur.right) &&
|
||||
benchmarkKey !== null &&
|
||||
|
@ -595,7 +610,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(n)
|
||||
* Space Complexity: O(k + log n)
|
||||
*
|
||||
* The `rangeSearch` function searches for nodes within a specified range in a binary search tree.
|
||||
* @param {Range<K> | [K, K]} range - The `range` parameter in the `rangeSearch` function can be
|
||||
|
@ -604,7 +619,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* function that is used to process each node that is found within the specified range during the
|
||||
* search operation. It is of type `NodeCallback<BSTNode<K, V>>`, where `BSTNode<K, V>` is the type of nodes in the
|
||||
* data structure.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} startNode - The `startNode` parameter in the `rangeSearch`
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} startNode - The `startNode` parameter in the `rangeSearch`
|
||||
* function represents the node from which the search for nodes within the specified range will
|
||||
* begin. It is the starting point for the range search operation.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter in the `rangeSearch` function
|
||||
|
@ -617,7 +632,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
rangeSearch<C extends NodeCallback<BSTNode<K, V>>>(
|
||||
range: Range<K> | [K, K],
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> | R = this._root,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
) {
|
||||
const searchRange: Range<K> = range instanceof Range ? range : new Range(range[0], range[1]);
|
||||
|
@ -626,12 +641,12 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* This function retrieves a node based on a given keyNodeEntryRawOrPredicate within a binary search tree structure.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R | NodePredicate<BSTNode<K, V>>} keyNodeEntryRawOrPredicate - The `keyNodeEntryRawOrPredicate`
|
||||
* This function retrieves a node based on a given keyNodeEntryOrPredicate within a binary search tree structure.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | NodePredicate<BSTNode<K, V>>} keyNodeEntryOrPredicate - The `keyNodeEntryOrPredicate`
|
||||
* parameter can be of type `BTNRep<K, V, BSTNode<K, V>>`, `R`, or `NodePredicate<BSTNode<K, V>>`.
|
||||
* @param {R | BSTNOptKeyOrNode<K, BSTNode<K, V>>} startNode - The `startNode` parameter in the `getNode` method
|
||||
* @param {BSTNOptKeyOrNode<K, BSTNode<K, V>>} startNode - The `startNode` parameter in the `getNode` method
|
||||
* is used to specify the starting point for searching nodes in the binary search tree. If no
|
||||
* specific starting point is provided, the default value is set to `this._root`, which is the root
|
||||
* node of the binary search tree.
|
||||
|
@ -640,16 +655,16 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* `this.iterationType`, which means it will use the iteration type defined in the class instance if
|
||||
* no value is provided when calling the method.
|
||||
* @returns The `getNode` method is returning an optional binary search tree node (`OptNode<BSTNode<K, V>>`).
|
||||
* It is using the `getNodes` method to find the node based on the provided keyNodeEntryRawOrPredicate, beginning at
|
||||
* It is using the `getNodes` method to find the node based on the provided keyNodeEntryOrPredicate, beginning at
|
||||
* the specified root node (`startNode`) and using the specified iteration type. The method then
|
||||
* returns the first node found or `undefined` if no node is found.
|
||||
*/
|
||||
override getNode(
|
||||
keyNodeEntryRawOrPredicate: BTNRep<K, V, BSTNode<K, V>> | R | NodePredicate<BSTNode<K, V>>,
|
||||
startNode: R | BSTNOptKeyOrNode<K, BSTNode<K, V>> = this._root,
|
||||
keyNodeEntryOrPredicate: BTNRep<K, V, BSTNode<K, V>> | NodePredicate<BSTNode<K, V>>,
|
||||
startNode: BSTNOptKeyOrNode<K, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): OptNode<BSTNode<K, V>> {
|
||||
return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined;
|
||||
return this.getNodes(keyNodeEntryOrPredicate, true, startNode, iterationType)[0] ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -664,7 +679,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* @param {DFSOrderPattern} [pattern=IN] - The "pattern" parameter in the code snippet refers to the
|
||||
* order in which the Depth-First Search (DFS) algorithm visits the nodes in a tree or graph. It can
|
||||
* take one of the following values:
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} startNode - The `startNode` parameter is the starting
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} startNode - The `startNode` parameter is the starting
|
||||
* point for the depth-first search traversal. It can be either a root node, a key-value pair, or a
|
||||
* node entry. If not specified, the default value is the root of the tree.
|
||||
* @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter specifies the
|
||||
|
@ -675,7 +690,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
override dfs<C extends NodeCallback<BSTNode<K, V>>>(
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
pattern: DFSOrderPattern = 'IN',
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> | R = this._root,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
return super.dfs(callback, pattern, startNode, iterationType);
|
||||
|
@ -690,7 +705,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* @param {C} callback - The `callback` parameter is a function that will be called for each node
|
||||
* visited during the breadth-first search. It should take a single argument, which is the current
|
||||
* node being visited, and it can return a value of any type.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} startNode - The `startNode` parameter is the starting
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} startNode - The `startNode` parameter is the starting
|
||||
* point for the breadth-first search. It can be either a root node, a key-value pair, or an entry
|
||||
* object. If no value is provided, the default value is the root of the tree.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter is used to specify the type
|
||||
|
@ -700,7 +715,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
*/
|
||||
override bfs<C extends NodeCallback<BSTNode<K, V>>>(
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> | R = this._root,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
return super.bfs(callback, startNode, iterationType, false);
|
||||
|
@ -715,7 +730,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* @param {C} callback - The `callback` parameter is a generic type `C` that extends
|
||||
* `NodeCallback<BSTNode<K, V>>`. It represents a callback function that will be called for each node in the
|
||||
* tree during the iteration process.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} startNode - The `startNode` parameter is the starting
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} startNode - The `startNode` parameter is the starting
|
||||
* point for listing the levels of the binary tree. It can be either a root node of the tree, a
|
||||
* key-value pair representing a node in the tree, or a key representing a node in the tree. If no
|
||||
* value is provided, the root of
|
||||
|
@ -726,7 +741,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
*/
|
||||
override listLevels<C extends NodeCallback<BSTNode<K, V>>>(
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> | R = this._root,
|
||||
startNode: BTNRep<K, V, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[][] {
|
||||
return super.listLevels(callback, startNode, iterationType, false);
|
||||
|
@ -744,7 +759,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
* @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to
|
||||
* traverse nodes that are lesser, greater, or both than the `targetNode`. It accepts the values -1,
|
||||
* 0, or 1, where:
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} targetNode - The `targetNode` parameter is the node in
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} targetNode - The `targetNode` parameter is the node in
|
||||
* the binary tree that you want to start traversing from. It can be specified either by providing
|
||||
* the key of the node, the node itself, or an entry containing the key and value of the node. If no
|
||||
* `targetNode` is provided,
|
||||
|
@ -756,7 +771,7 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
lesserOrGreaterTraverse<C extends NodeCallback<BSTNode<K, V>>>(
|
||||
callback: C = this._DEFAULT_NODE_CALLBACK as C,
|
||||
lesserOrGreater: CP = -1,
|
||||
targetNode: BTNRep<K, V, BSTNode<K, V>> | R = this._root,
|
||||
targetNode: BTNRep<K, V, BSTNode<K, V>> = this._root,
|
||||
iterationType: IterationType = this.iterationType
|
||||
): ReturnType<C>[] {
|
||||
const targetNodeEnsured = this.ensureNode(targetNode);
|
||||
|
@ -900,6 +915,25 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
return balanced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
*
|
||||
* The `map` function in TypeScript overrides the default map behavior for a binary search tree by
|
||||
* applying a callback function to each entry and creating a new tree with the results.
|
||||
* @param callback - A function that will be called for each entry in the BST. It takes four
|
||||
* arguments: the key, the value (which can be undefined), the index of the entry, and a reference to
|
||||
* the BST itself.
|
||||
* @param [options] - The `options` parameter in the `override map` method is of type `BSTOptions<MK,
|
||||
* MV, MR>`. It is an optional parameter that allows you to specify additional options for the Binary
|
||||
* Search Tree (BST) being created in the `map` method. These options could include configuration
|
||||
* @param {any} [thisArg] - The `thisArg` parameter in the `override map` method is used to specify
|
||||
* the value of `this` that should be used when executing the `callback` function. It allows you to
|
||||
* set the context or scope in which the callback function will be called. This can be useful when
|
||||
* you want
|
||||
* @returns The `map` method is returning a new Binary Search Tree (`BST`) instance with the entries
|
||||
* transformed by the provided callback function.
|
||||
*/
|
||||
override map(
|
||||
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
||||
options?: BSTOptions<MK, MV, MR>,
|
||||
|
@ -913,6 +947,14 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
*
|
||||
* The function `clone` overrides the default cloning behavior to create a deep copy of a tree
|
||||
* structure.
|
||||
* @returns The `cloned` object is being returned.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
this._clone(cloned);
|
||||
|
@ -920,24 +962,30 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides a method and converts a key, value pair or entry or raw element to a node.
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>> | R} keyNodeEntryOrRaw - A variable that can be of
|
||||
* @param {BTNRep<K, V, BSTNode<K, V>>} keyNodeOrEntry - A variable that can be of
|
||||
* type R or BTNRep<K, V, BSTNode<K, V>>. It represents either a key, a node, an entry, or a raw
|
||||
* element.
|
||||
* @param {V} [value] - The `value` parameter is an optional value of type `V`. It represents the
|
||||
* value associated with a key in a key-value pair.
|
||||
* @returns either a BSTNode<K, V> object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, BSTNode<K, V>> | R,
|
||||
protected override _keyValueNodeOrEntryToNodeAndValue(
|
||||
keyNodeOrEntry: BTNRep<K, V, BSTNode<K, V>>,
|
||||
value?: V
|
||||
): [OptNode<BSTNode<K, V>>, V | undefined] {
|
||||
const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
const [node, entryValue] = super._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
||||
if (node === null) return [undefined, undefined];
|
||||
return [node, value ?? entryValue];
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function sets the root of a tree-like structure and updates the parent property of the new
|
||||
* root.
|
||||
* @param {OptNode<BSTNode<K, V>>} v - v is a parameter of type BSTNode<K, V> or undefined.
|
||||
|
@ -949,6 +997,20 @@ export class BST<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
|||
this._root = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The _compare function compares two values using a specified comparator function and optionally
|
||||
* reverses the result.
|
||||
* @param {K} a - The parameter `a` is of type `K`, which is used as an input for comparison in the
|
||||
* `_compare` method.
|
||||
* @param {K} b - The parameter `b` in the `_compare` function is of type `K`.
|
||||
* @returns The `_compare` method is returning the result of the ternary expression. If `_isReverse`
|
||||
* is true, it returns the negation of the result of calling the `_comparator` function with
|
||||
* arguments `a` and `b`. If `_isReverse` is false, it returns the result of calling the
|
||||
* `_comparator` function with arguments `a` and `b`.
|
||||
*/
|
||||
protected _compare(a: K, b: K) {
|
||||
return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b);
|
||||
}
|
||||
|
|
|
@ -6,3 +6,5 @@ export * from './avl-tree';
|
|||
export * from './red-black-tree';
|
||||
export * from './avl-tree-multi-map';
|
||||
export * from './tree-multi-map';
|
||||
export * from './tree-counter';
|
||||
export * from './avl-tree-counter';
|
||||
|
|
|
@ -13,15 +13,14 @@ import { IBinaryTree } from '../../interfaces';
|
|||
|
||||
export class RedBlackTreeNode<K = any, V = any> extends BSTNode<K, V> {
|
||||
/**
|
||||
* The constructor function initializes a Red-Black Tree Node with a key, an optional value, and a
|
||||
* color.
|
||||
* @param {K} key - The key parameter is of type K and represents the key of the node in the
|
||||
* Red-Black Tree.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the Red-Black Tree Node. It is not required and can be omitted when
|
||||
* creating a new instance of the Red-Black Tree Node.
|
||||
* @param {RBTNColor} color - The `color` parameter is used to specify the color of the Red-Black
|
||||
* Tree Node. It is an optional parameter with a default value of `'BLACK'`.
|
||||
* The constructor initializes a node with a key, value, and color for a Red-Black Tree.
|
||||
* @param {K} key - The `key` parameter is a key of type `K` that is used to identify the node in a
|
||||
* Red-Black Tree data structure.
|
||||
* @param {V} [value] - The `value` parameter in the constructor is an optional parameter of type
|
||||
* `V`. It represents the value associated with the key in the data structure being constructed.
|
||||
* @param {RBTNColor} [color=BLACK] - The `color` parameter in the constructor is used to specify the
|
||||
* color of the node in a Red-Black Tree. It has a default value of 'BLACK' if not provided
|
||||
* explicitly.
|
||||
*/
|
||||
constructor(key: K, value?: V, color: RBTNColor = 'BLACK') {
|
||||
super(key, value);
|
||||
|
@ -115,17 +114,18 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
/**
|
||||
* This is the constructor function for a Red-Black Tree data structure in TypeScript.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an
|
||||
* iterable object that can contain either keys, nodes, entries, or raw elements. It is used to
|
||||
* initialize the RBTree with the provided elements.
|
||||
* @param [options] - The `options` parameter is an optional object that can be passed to the
|
||||
* constructor. It is of type `RedBlackTreeOptions<K, V, R>`. This object can contain various options for
|
||||
* configuring the behavior of the Red-Black Tree. The specific properties and their meanings would
|
||||
* depend on the implementation
|
||||
* This TypeScript constructor initializes a Red-Black Tree with optional keys, nodes, entries, or
|
||||
* raw data.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an
|
||||
* iterable that can contain either `BTNRep<K, V, RedBlackTreeNode<K, V>>` objects or `R` objects. It
|
||||
* is used to initialize the Red-Black Tree with keys, nodes, entries, or
|
||||
* @param [options] - The `options` parameter in the constructor is of type `RedBlackTreeOptions<K,
|
||||
* V, R>`. It is an optional parameter that allows you to specify additional options for the
|
||||
* RedBlackTree class. These options could include configuration settings, behavior customization, or
|
||||
* any other parameters that are specific to
|
||||
*/
|
||||
constructor(
|
||||
keysNodesEntriesOrRaws: Iterable<R | BTNRep<K, V, RedBlackTreeNode<K, V>>> = [],
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V, RedBlackTreeNode<K, V>> | R> = [],
|
||||
options?: RedBlackTreeOptions<K, V, R>
|
||||
) {
|
||||
super([], options);
|
||||
|
@ -139,15 +139,14 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
|
||||
protected override _root: RedBlackTreeNode<K, V> | undefined;
|
||||
|
||||
/**
|
||||
* The function returns the root node of a tree or undefined if there is no root.
|
||||
* @returns The root node of the tree structure, or undefined if there is no root node.
|
||||
*/
|
||||
override get root(): RedBlackTreeNode<K, V> | undefined {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function creates a new Red-Black Tree node with the specified key, value, and color.
|
||||
* @param {K} key - The key parameter represents the key value of the node being created. It is of
|
||||
* type K, which is a generic type that can be replaced with any specific type when using the
|
||||
|
@ -162,10 +161,13 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
* returned.
|
||||
*/
|
||||
override createNode(key: K, value?: V, color: RBTNColor = 'BLACK'): RedBlackTreeNode<K, V> {
|
||||
return new RedBlackTreeNode<K, V>(key, this._isMapMode ? undefined : value, color) as RedBlackTreeNode<K, V>;
|
||||
return new RedBlackTreeNode<K, V>(key, this._isMapMode ? undefined : value, color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function creates a new Red-Black Tree with the specified options.
|
||||
* @param [options] - The `options` parameter is an optional object that contains additional
|
||||
* configuration options for creating the Red-Black Tree. It has the following properties:
|
||||
|
@ -186,15 +188,13 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function checks if the input is an instance of the RedBlackTreeNode class.
|
||||
* @param {BTNRep<K, V, RedBlackTreeNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, RedBlackTreeNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is
|
||||
* @param {BTNRep<K, V, RedBlackTreeNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can be of type `R` or `BTNRep<K, V, RedBlackTreeNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is
|
||||
* an instance of the `RedBlackTreeNode` class.
|
||||
*/
|
||||
override isNode(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, RedBlackTreeNode<K, V>> | R
|
||||
): keyNodeEntryOrRaw is RedBlackTreeNode<K, V> {
|
||||
return keyNodeEntryOrRaw instanceof RedBlackTreeNode;
|
||||
override isNode(keyNodeOrEntry: BTNRep<K, V, RedBlackTreeNode<K, V>>): keyNodeOrEntry is RedBlackTreeNode<K, V> {
|
||||
return keyNodeOrEntry instanceof RedBlackTreeNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,12 +211,12 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function adds a new node to a binary search tree and returns true if the node was successfully
|
||||
* added.
|
||||
* @param {BTNRep<K, V, RedBlackTreeNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can accept a value of type `R` or `BTNRep<K, V, RedBlackTreeNode<K, V>>`.
|
||||
* @param {BTNRep<K, V, RedBlackTreeNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can accept a value of type `R` or `BTNRep<K, V, RedBlackTreeNode<K, V>>`.
|
||||
* @param {V} [value] - The `value` parameter is an optional value that you want to associate with
|
||||
* the key in the data structure. It represents the value that you want to add or update in the data
|
||||
* structure.
|
||||
|
@ -224,8 +224,8 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
* the method returns true. If the node already exists and its value is updated, the method also
|
||||
* returns true. If the node cannot be added or updated, the method returns false.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, RedBlackTreeNode<K, V>> | R, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
override add(keyNodeOrEntry: BTNRep<K, V, RedBlackTreeNode<K, V>>, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value);
|
||||
if (!this.isRealNode(newNode)) return false;
|
||||
|
||||
const insertStatus = this._insert(newNode);
|
||||
|
@ -250,11 +250,11 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function overrides the delete method in a binary tree data structure to remove a node based on
|
||||
* a given predicate and maintain the binary search tree properties.
|
||||
* @param {BTNRep<K, V, RedBlackTreeNode<K, V>> | R} keyNodeEntryOrRaw - The `keyNodeEntryOrRaw`
|
||||
* @param {BTNRep<K, V, RedBlackTreeNode<K, V>>} keyNodeOrEntry - The `keyNodeOrEntry`
|
||||
* parameter in the `override delete` method is used to specify the condition or key based on which a
|
||||
* node should be deleted from the binary tree. It can be a key, a node, an entry, or a predicate
|
||||
* function that determines which node(s) should be deleted.
|
||||
|
@ -263,14 +263,14 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
* balancing is needed.
|
||||
*/
|
||||
override delete(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, RedBlackTreeNode<K, V>> | R
|
||||
keyNodeOrEntry: BTNRep<K, V, RedBlackTreeNode<K, V>>
|
||||
): BinaryTreeDeleteResult<RedBlackTreeNode<K, V>>[] {
|
||||
if (keyNodeEntryOrRaw === null) return [];
|
||||
if (keyNodeOrEntry === null) return [];
|
||||
|
||||
const results: BinaryTreeDeleteResult<RedBlackTreeNode<K, V>>[] = [];
|
||||
let nodeToDelete: OptNode<RedBlackTreeNode<K, V>>;
|
||||
if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw);
|
||||
else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw);
|
||||
if (this._isPredicate(keyNodeOrEntry)) nodeToDelete = this.getNode(keyNodeOrEntry);
|
||||
else nodeToDelete = this.isRealNode(keyNodeOrEntry) ? keyNodeOrEntry : this.getNode(keyNodeOrEntry);
|
||||
|
||||
if (!nodeToDelete) {
|
||||
return results;
|
||||
|
@ -361,6 +361,14 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* The function `clone` overrides the default cloning behavior to create a deep copy of a tree
|
||||
* structure.
|
||||
* @returns The `cloned` object is being returned.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
this._clone(cloned);
|
||||
|
@ -405,7 +413,7 @@ export class RedBlackTree<K = any, V = any, R = object, MK = any, MV = any, MR =
|
|||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The `_insert` function inserts a node into a binary search tree and performs necessary fix-ups to
|
||||
* maintain the red-black tree properties.
|
||||
|
|
504
src/data-structures/binary-tree/tree-counter.ts
Normal file
504
src/data-structures/binary-tree/tree-counter.ts
Normal file
|
@ -0,0 +1,504 @@
|
|||
/**
|
||||
* data-structure-typed
|
||||
*
|
||||
* @author Pablo Zeng
|
||||
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
import type {
|
||||
BinaryTreeDeleteResult,
|
||||
BSTNOptKeyOrNode,
|
||||
BTNRep,
|
||||
EntryCallback,
|
||||
IterationType,
|
||||
OptNode,
|
||||
OptNodeOrNull,
|
||||
RBTNColor,
|
||||
TreeCounterOptions
|
||||
} from '../../types';
|
||||
import { IBinaryTree } from '../../interfaces';
|
||||
import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
|
||||
|
||||
export class TreeCounterNode<K = any, V = any> extends RedBlackTreeNode<K, V> {
|
||||
/**
|
||||
* The constructor function initializes a Red-Black Tree node with a key, value, count, and color.
|
||||
* @param {K} key - The key parameter represents the key of the node in the Red-Black Tree. It is
|
||||
* used to identify and locate the node within the tree.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the Red-Black Tree node. It is not required and can be omitted when
|
||||
* creating a new node.
|
||||
* @param [count=1] - The `count` parameter represents the number of occurrences of a particular key
|
||||
* in the Red-Black Tree. It is an optional parameter with a default value of 1.
|
||||
* @param {RBTNColor} [color=BLACK] - The `color` parameter is used to specify the color of the node
|
||||
* in a Red-Black Tree. It is optional and has a default value of `'BLACK'`.
|
||||
*/
|
||||
constructor(key: K, value?: V, count = 1, color: RBTNColor = 'BLACK') {
|
||||
super(key, value, color);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
override parent?: TreeCounterNode<K, V> = undefined;
|
||||
|
||||
override _left?: OptNodeOrNull<TreeCounterNode<K, V>> = undefined;
|
||||
|
||||
override get left(): OptNodeOrNull<TreeCounterNode<K, V>> {
|
||||
return this._left;
|
||||
}
|
||||
|
||||
override set left(v: OptNodeOrNull<TreeCounterNode<K, V>>) {
|
||||
if (v) {
|
||||
v.parent = this;
|
||||
}
|
||||
this._left = v;
|
||||
}
|
||||
|
||||
override _right?: OptNodeOrNull<TreeCounterNode<K, V>> = undefined;
|
||||
|
||||
override get right(): OptNodeOrNull<TreeCounterNode<K, V>> {
|
||||
return this._right;
|
||||
}
|
||||
|
||||
override set right(v: OptNodeOrNull<TreeCounterNode<K, V>>) {
|
||||
if (v) {
|
||||
v.parent = this;
|
||||
}
|
||||
this._right = v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class TreeCounter<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
||||
extends RedBlackTree<K, V, R, MK, MV, MR>
|
||||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
/**
|
||||
* The constructor function initializes a TreeCounter object with optional initial data.
|
||||
* @param keysNodesEntriesOrRaws - The parameter `keysNodesEntriesOrRaws` is an
|
||||
* iterable that can contain keys, nodes, entries, or raw elements. It is used to initialize the
|
||||
* TreeCounter with initial data.
|
||||
* @param [options] - The `options` parameter is an optional object that can be used to customize the
|
||||
* behavior of the `TreeCounter` constructor. It can include properties such as `compareKeys` and
|
||||
* `compareValues`, which are functions used to compare keys and values respectively.
|
||||
*/
|
||||
constructor(
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V, TreeCounterNode<K, V>> | R> = [],
|
||||
options?: TreeCounterOptions<K, V, R>
|
||||
) {
|
||||
super([], options);
|
||||
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
||||
}
|
||||
|
||||
protected _count = 0;
|
||||
|
||||
// TODO the _count is not accurate after nodes count modified
|
||||
/**
|
||||
* The function calculates the sum of the count property of all nodes in a tree structure.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
get count(): number {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function calculates the sum of the count property of all nodes in a tree using depth-first
|
||||
* search.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
getComputedCount(): number {
|
||||
let sum = 0;
|
||||
this.dfs(node => (sum += node.count));
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new TreeCounterNode with the specified key, value, color, and count.
|
||||
* @param {K} key - The key parameter represents the key of the node being created. It is of type K,
|
||||
* which is a generic type representing the type of keys in the tree.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the node. It is of type `V`, which can be any data type.
|
||||
* @param {RBTNColor} [color=BLACK] - The color parameter is used to specify the color of the node in
|
||||
* a Red-Black Tree. It can have two possible values: 'RED' or 'BLACK'. The default value is 'BLACK'.
|
||||
* @param {number} [count] - The `count` parameter represents the number of occurrences of a key in
|
||||
* the tree. It is an optional parameter and is used to keep track of the number of values associated
|
||||
* with a key in the tree.
|
||||
* @returns A new instance of the TreeCounterNode class, casted as TreeCounterNode<K, V>.
|
||||
*/
|
||||
override createNode(key: K, value?: V, color: RBTNColor = 'BLACK', count?: number): TreeCounterNode<K, V> {
|
||||
return new TreeCounterNode(key, this._isMapMode ? undefined : value, count, color) as TreeCounterNode<K, V>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new instance of a TreeCounter with the specified options and returns it.
|
||||
* @param [options] - The `options` parameter is an optional object that contains additional
|
||||
* configuration options for creating the `TreeCounter`. It is of type `TreeCounterOptions<K, V,
|
||||
* R>`.
|
||||
* @returns a new instance of the `TreeCounter` class, with the provided options merged with the
|
||||
* existing `iterationType` property. The returned value is casted as `TREE`.
|
||||
*/
|
||||
override createTree(options?: TreeCounterOptions<K, V, R>) {
|
||||
return new TreeCounter<K, V, R, MK, MV, MR>([], {
|
||||
iterationType: this.iterationType,
|
||||
specifyComparable: this._specifyComparable,
|
||||
isMapMode: this._isMapMode,
|
||||
toEntryFn: this._toEntryFn,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if the input is an instance of the TreeCounterNode class.
|
||||
* @param {BTNRep<K, V, TreeCounterNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can be of type `R` or `BTNRep<K, V, TreeCounterNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeOrEntry` is
|
||||
* an instance of the `TreeCounterNode` class.
|
||||
*/
|
||||
override isNode(keyNodeOrEntry: BTNRep<K, V, TreeCounterNode<K, V>>): keyNodeOrEntry is TreeCounterNode<K, V> {
|
||||
return keyNodeOrEntry instanceof TreeCounterNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides the add method of a class and adds a new node to a data structure, updating
|
||||
* the count and returning a boolean indicating success.
|
||||
* @param {BTNRep<K, V, TreeCounterNode<K, V>>} keyNodeOrEntry - The
|
||||
* `keyNodeOrEntry` parameter can accept one of the following types:
|
||||
* @param {V} [value] - The `value` parameter represents the value associated with the key in the
|
||||
* data structure. It is an optional parameter, so it can be omitted if not needed.
|
||||
* @param [count=1] - The `count` parameter represents the number of times the key-value pair should
|
||||
* be added to the data structure. By default, it is set to 1, meaning that if no value is provided
|
||||
* for `count`, the key-value pair will be added once.
|
||||
* @returns The method is returning a boolean value. It returns true if the addition of the new node
|
||||
* was successful, and false otherwise.
|
||||
*/
|
||||
override add(keyNodeOrEntry: BTNRep<K, V, TreeCounterNode<K, V>>, value?: V, count = 1): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeOrEntryToNodeAndValue(keyNodeOrEntry, value, count);
|
||||
const orgCount = newNode?.count || 0;
|
||||
const isSuccessAdded = super.add(newNode, newValue);
|
||||
|
||||
if (isSuccessAdded) {
|
||||
this._count += orgCount;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function `delete` in TypeScript overrides the deletion operation in a binary tree data
|
||||
* structure, handling cases where nodes have children and maintaining balance in the tree.
|
||||
* @param {BTNRep<K, V, TreeCounterNode<K, V>>} keyNodeOrEntry - The `predicate`
|
||||
* parameter in the `delete` method is used to specify the condition or key based on which a node
|
||||
* should be deleted from the binary tree. It can be a key, a node, or an entry.
|
||||
* @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a
|
||||
* boolean flag that determines whether to ignore the count of nodes when performing deletion. If
|
||||
* `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If
|
||||
* `ignoreCount` is `false
|
||||
* @returns The `override delete` method returns an array of `BinaryTreeDeleteResult<TreeCounterNode<K, V>>` objects.
|
||||
*/
|
||||
override delete(
|
||||
keyNodeOrEntry: BTNRep<K, V, TreeCounterNode<K, V>>,
|
||||
ignoreCount = false
|
||||
): BinaryTreeDeleteResult<TreeCounterNode<K, V>>[] {
|
||||
if (keyNodeOrEntry === null) return [];
|
||||
|
||||
const results: BinaryTreeDeleteResult<TreeCounterNode<K, V>>[] = [];
|
||||
|
||||
let nodeToDelete: OptNode<TreeCounterNode<K, V>>;
|
||||
if (this._isPredicate(keyNodeOrEntry)) nodeToDelete = this.getNode(keyNodeOrEntry);
|
||||
else nodeToDelete = this.isRealNode(keyNodeOrEntry) ? keyNodeOrEntry : this.getNode(keyNodeOrEntry);
|
||||
|
||||
if (!nodeToDelete) {
|
||||
return results;
|
||||
}
|
||||
|
||||
let originalColor = nodeToDelete.color;
|
||||
let replacementNode: TreeCounterNode<K, V> | undefined;
|
||||
|
||||
if (!this.isRealNode(nodeToDelete.left)) {
|
||||
if (nodeToDelete.right !== null) replacementNode = nodeToDelete.right;
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
if (nodeToDelete.right !== null) {
|
||||
this._transplant(nodeToDelete, nodeToDelete.right);
|
||||
this._count -= nodeToDelete.count;
|
||||
}
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
} else if (!this.isRealNode(nodeToDelete.right)) {
|
||||
replacementNode = nodeToDelete.left;
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, nodeToDelete.left);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
} else {
|
||||
const successor = this.getLeftMost(node => node, nodeToDelete.right);
|
||||
if (successor) {
|
||||
originalColor = successor.color;
|
||||
if (successor.right !== null) replacementNode = successor.right;
|
||||
|
||||
if (successor.parent === nodeToDelete) {
|
||||
if (this.isRealNode(replacementNode)) {
|
||||
replacementNode.parent = successor;
|
||||
}
|
||||
} else {
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
if (successor.right !== null) {
|
||||
this._transplant(successor, successor.right);
|
||||
this._count -= nodeToDelete.count;
|
||||
}
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
successor.right = nodeToDelete.right;
|
||||
if (this.isRealNode(successor.right)) {
|
||||
successor.right.parent = successor;
|
||||
}
|
||||
}
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, successor);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
successor.left = nodeToDelete.left;
|
||||
if (this.isRealNode(successor.left)) {
|
||||
successor.left.parent = successor;
|
||||
}
|
||||
successor.color = nodeToDelete.color;
|
||||
}
|
||||
}
|
||||
this._size--;
|
||||
|
||||
// If the original color was black, fix the tree
|
||||
if (originalColor === 'BLACK') {
|
||||
this._deleteFixup(replacementNode);
|
||||
}
|
||||
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The "clear" function overrides the parent class's "clear" function and also resets the count to
|
||||
* zero.
|
||||
*/
|
||||
override clear() {
|
||||
super.clear();
|
||||
this._count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n log n)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search
|
||||
* tree using either a recursive or iterative approach.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that
|
||||
* specifies the type of iteration to use when building the balanced binary search tree. It has a
|
||||
* default value of `this.iterationType`, which means it will use the iteration type specified by the
|
||||
* `iterationType` property of the current object.
|
||||
* @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the
|
||||
* balancing operation is successful, and `false` if there are no nodes to balance.
|
||||
*/
|
||||
override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'IN'),
|
||||
n = sorted.length;
|
||||
if (sorted.length < 1) return false;
|
||||
|
||||
this.clear();
|
||||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const buildBalanceBST = (l: number, r: number) => {
|
||||
if (l > r) return;
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
buildBalanceBST(l, m - 1);
|
||||
buildBalanceBST(m + 1, r);
|
||||
};
|
||||
|
||||
buildBalanceBST(0, n - 1);
|
||||
return true;
|
||||
} else {
|
||||
const stack: [[number, number]] = [[0, n - 1]];
|
||||
while (stack.length > 0) {
|
||||
const popped = stack.pop();
|
||||
if (popped) {
|
||||
const [l, r] = popped;
|
||||
if (l <= r) {
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
stack.push([m + 1, r]);
|
||||
stack.push([l, m - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
*
|
||||
* The function overrides the clone method to create a deep copy of a tree object.
|
||||
* @returns The `clone()` method is returning a cloned instance of the `TREE` object.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
this.bfs(node => cloned.add(node.key, undefined, node.count));
|
||||
if (this._isMapMode) cloned._store = this._store;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `map` function in TypeScript overrides the default behavior to create a new TreeCounter with
|
||||
* modified entries based on a provided callback.
|
||||
* @param callback - The `callback` parameter is a function that will be called for each entry in the
|
||||
* map. It takes four arguments:
|
||||
* @param [options] - The `options` parameter in the `override map` function is of type
|
||||
* `TreeCounterOptions<MK, MV, MR>`. This parameter allows you to provide additional configuration
|
||||
* options when creating a new `TreeCounter` instance within the `map` function. These options could
|
||||
* include things like
|
||||
* @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify
|
||||
* the value of `this` when executing the `callback` function. It allows you to set the context
|
||||
* (value of `this`) for the callback function when it is called within the `map` function. This
|
||||
* @returns A new TreeCounter instance is being returned, which is populated with entries generated
|
||||
* by the provided callback function.
|
||||
*/
|
||||
override map(
|
||||
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
||||
options?: TreeCounterOptions<MK, MV, MR>,
|
||||
thisArg?: any
|
||||
): TreeCounter<MK, MV, MR> {
|
||||
const newTree = new TreeCounter<MK, MV, MR>([], options);
|
||||
let index = 0;
|
||||
for (const [key, value] of this) {
|
||||
newTree.add(callback.call(thisArg, key, value, index++, this));
|
||||
}
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a
|
||||
* node based on the input.
|
||||
* @param {BTNRep<K, V, TreeCounterNode<K, V>>} keyNodeOrEntry - The parameter
|
||||
* `keyNodeOrEntry` can be of type `R` or `BTNRep<K, V, TreeCounterNode<K, V>>`.
|
||||
* @param {V} [value] - The `value` parameter is an optional value that represents the value
|
||||
* associated with the key in the node. It is used when creating a new node or updating the value of
|
||||
* an existing node.
|
||||
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
|
||||
* times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
|
||||
* @returns either a TreeCounterNode<K, V> object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeOrEntryToNodeAndValue(
|
||||
keyNodeOrEntry: BTNRep<K, V, TreeCounterNode<K, V>>,
|
||||
value?: V,
|
||||
count = 1
|
||||
): [TreeCounterNode<K, V> | undefined, V | undefined] {
|
||||
if (keyNodeOrEntry === undefined || keyNodeOrEntry === null) return [undefined, undefined];
|
||||
|
||||
if (this.isNode(keyNodeOrEntry)) return [keyNodeOrEntry, value];
|
||||
|
||||
if (this.isEntry(keyNodeOrEntry)) {
|
||||
const [key, entryValue] = keyNodeOrEntry;
|
||||
if (key === undefined || key === null) return [undefined, undefined];
|
||||
const finalValue = value ?? entryValue;
|
||||
return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
|
||||
}
|
||||
|
||||
return [this.createNode(keyNodeOrEntry, value, 'BLACK', count), value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes
|
||||
* in a binary search tree.
|
||||
* @param {R | BSTNOptKeyOrNode<K, TreeCounterNode<K, V>>} srcNode - The `srcNode` parameter represents the source node
|
||||
* that will be swapped with the `destNode`. It can be either an instance of the `R` class or an
|
||||
* instance of the `BSTNOptKeyOrNode<K, TreeCounterNode<K, V>>` class.
|
||||
* @param {R | BSTNOptKeyOrNode<K, TreeCounterNode<K, V>>} destNode - The `destNode` parameter represents the destination
|
||||
* node where the properties will be swapped with the source node.
|
||||
* @returns The method is returning the `destNode` after swapping its properties with the `srcNode`.
|
||||
* If either `srcNode` or `destNode` is undefined, it returns undefined.
|
||||
*/
|
||||
protected override _swapProperties(
|
||||
srcNode: BSTNOptKeyOrNode<K, TreeCounterNode<K, V>>,
|
||||
destNode: BSTNOptKeyOrNode<K, TreeCounterNode<K, V>>
|
||||
): TreeCounterNode<K, V> | undefined {
|
||||
srcNode = this.ensureNode(srcNode);
|
||||
destNode = this.ensureNode(destNode);
|
||||
if (srcNode && destNode) {
|
||||
const { key, value, count, color } = destNode;
|
||||
const tempNode = this.createNode(key, value, color, count);
|
||||
if (tempNode) {
|
||||
tempNode.color = color;
|
||||
|
||||
destNode.key = srcNode.key;
|
||||
if (!this._isMapMode) destNode.value = srcNode.value;
|
||||
destNode.count = srcNode.count;
|
||||
destNode.color = srcNode.color;
|
||||
|
||||
srcNode.key = tempNode.key;
|
||||
if (!this._isMapMode) srcNode.value = tempNode.value;
|
||||
srcNode.count = tempNode.count;
|
||||
srcNode.color = tempNode.color;
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function replaces an old node with a new node and updates the count property of the new node.
|
||||
* @param {TreeCounterNode<K, V>} oldNode - The `oldNode` parameter is the node that you want to replace in the data
|
||||
* structure.
|
||||
* @param {TreeCounterNode<K, V>} newNode - The `newNode` parameter is an instance of the `TreeCounterNode<K, V>` class.
|
||||
* @returns The method is returning the result of calling the `_replaceNode` method from the
|
||||
* superclass, which is of type `TreeCounterNode<K, V>`.
|
||||
*/
|
||||
protected override _replaceNode(
|
||||
oldNode: TreeCounterNode<K, V>,
|
||||
newNode: TreeCounterNode<K, V>
|
||||
): TreeCounterNode<K, V> {
|
||||
newNode.count = oldNode.count + newNode.count;
|
||||
return super._replaceNode(oldNode, newNode);
|
||||
}
|
||||
}
|
|
@ -5,36 +5,21 @@
|
|||
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
||||
* @license MIT License
|
||||
*/
|
||||
import type {
|
||||
BinaryTreeDeleteResult,
|
||||
BSTNOptKeyOrNode,
|
||||
BTNRep,
|
||||
EntryCallback,
|
||||
IterationType,
|
||||
OptNode,
|
||||
OptNodeOrNull,
|
||||
RBTNColor,
|
||||
TreeMultiMapOptions
|
||||
} from '../../types';
|
||||
import { IBinaryTree } from '../../interfaces';
|
||||
import type { BTNOptKeyOrNull, BTNRep, OptNodeOrNull, TreeMultiMapOptions } from '../../types';
|
||||
import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
|
||||
|
||||
export class TreeMultiMapNode<K = any, V = any> extends RedBlackTreeNode<K, V> {
|
||||
export class TreeMultiMapNode<K = any, V = any> extends RedBlackTreeNode<K, V[]> {
|
||||
/**
|
||||
* The constructor function initializes a Red-Black Tree node with a key, value, count, and color.
|
||||
* @param {K} key - The key parameter represents the key of the node in the Red-Black Tree. It is
|
||||
* used to identify and locate the node within the tree.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the Red-Black Tree node. It is not required and can be omitted when
|
||||
* creating a new node.
|
||||
* @param [count=1] - The `count` parameter represents the number of occurrences of a particular key
|
||||
* in the Red-Black Tree. It is an optional parameter with a default value of 1.
|
||||
* @param {RBTNColor} [color=BLACK] - The `color` parameter is used to specify the color of the node
|
||||
* in a Red-Black Tree. It is optional and has a default value of `'BLACK'`.
|
||||
* This TypeScript constructor initializes an object with a key of type K and an array of values of
|
||||
* type V.
|
||||
* @param {K} key - The `key` parameter is typically used to store a unique identifier or key for the
|
||||
* data being stored in the data structure. It helps in quickly accessing or retrieving the
|
||||
* associated value in the data structure.
|
||||
* @param {V[]} value - The `value` parameter in the constructor represents an array of values of
|
||||
* type `V`.
|
||||
*/
|
||||
constructor(key: K, value?: V, count = 1, color: RBTNColor = 'BLACK') {
|
||||
super(key, value, color);
|
||||
this.count = count;
|
||||
constructor(key: K, value: V[]) {
|
||||
super(key, value);
|
||||
}
|
||||
|
||||
override parent?: TreeMultiMapNode<K, V> = undefined;
|
||||
|
@ -66,446 +51,180 @@ export class TreeMultiMapNode<K = any, V = any> extends RedBlackTreeNode<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
export class TreeMultiMap<K = any, V = any, R = object, MK = any, MV = any, MR = object>
|
||||
extends RedBlackTree<K, V, R, MK, MV, MR>
|
||||
implements IBinaryTree<K, V, R, MK, MV, MR>
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @example
|
||||
* // Find elements in a range
|
||||
* const tmm = new TreeMultiMap<number>([10, 5, 15, 3, 7, 12, 18]);
|
||||
* console.log(tmm.search(new Range(5, 10))); // [5, 10, 7]
|
||||
* console.log(tmm.search(new Range(4, 12))); // [5, 10, 12, 7]
|
||||
* console.log(tmm.search(new Range(15, 20))); // [15, 18]
|
||||
*/
|
||||
export class TreeMultiMap<K = any, V = any, R = object, MK = any, MV = any, MR = object> extends RedBlackTree<
|
||||
K,
|
||||
V[],
|
||||
R,
|
||||
MK,
|
||||
MV,
|
||||
MR
|
||||
> {
|
||||
/**
|
||||
* The constructor function initializes a TreeMultiMap object with optional initial data.
|
||||
* @param keysNodesEntriesOrRaws - The parameter `keysNodesEntriesOrRaws` is an
|
||||
* iterable that can contain keys, nodes, entries, or raw elements. It is used to initialize the
|
||||
* TreeMultiMap with initial data.
|
||||
* @param [options] - The `options` parameter is an optional object that can be used to customize the
|
||||
* behavior of the `TreeMultiMap` constructor. It can include properties such as `compareKeys` and
|
||||
* `compareValues`, which are functions used to compare keys and values respectively.
|
||||
* The constructor initializes an TreeMultiMap with the provided keys, nodes, entries, or raw data
|
||||
* and options.
|
||||
* @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter in the constructor is an
|
||||
* iterable that can contain either key-value pairs represented as `BTNRep<K, V[],
|
||||
* TreeMultiMapNode<K, V>>` or raw data represented as `R`. This parameter is used to initialize
|
||||
* the RedBlackTreeMulti
|
||||
* @param [options] - The `options` parameter in the constructor is of type
|
||||
* `TreeMultiMapOptions<K, V[], R>`. It is an optional parameter that allows you to specify
|
||||
* additional options for configuring the TreeMultiMap instance.
|
||||
*/
|
||||
constructor(
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V, TreeMultiMapNode<K, V>>> = [],
|
||||
options?: TreeMultiMapOptions<K, V, R>
|
||||
keysNodesEntriesOrRaws: Iterable<BTNRep<K, V[], TreeMultiMapNode<K, V>> | R> = [],
|
||||
options?: TreeMultiMapOptions<K, V[], R>
|
||||
) {
|
||||
super([], options);
|
||||
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
||||
}
|
||||
|
||||
protected _count = 0;
|
||||
|
||||
// TODO the _count is not accurate after nodes count modified
|
||||
/**
|
||||
* The function calculates the sum of the count property of all nodes in a tree structure.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
*/
|
||||
get count(): number {
|
||||
return this._count;
|
||||
super([], { ...options, isMapMode: true });
|
||||
if (keysNodesEntriesOrRaws) {
|
||||
this.addMany(keysNodesEntriesOrRaws);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function calculates the sum of the count property of all nodes in a tree using depth-first
|
||||
* search.
|
||||
* @returns the sum of the count property of all nodes in the tree.
|
||||
* The `createTree` function in TypeScript overrides the default implementation to create a new
|
||||
* TreeMultiMap with specified options.
|
||||
* @param [options] - The `options` parameter in the `createTree` method is of type
|
||||
* `TreeMultiMapOptions<K, V[], R>`. This parameter allows you to pass additional configuration
|
||||
* options when creating a new `TreeMultiMap` instance. It includes properties such as
|
||||
* `iterationType`, `specifyComparable
|
||||
* @returns A new instance of `TreeMultiMap` is being returned, with an empty array as the initial
|
||||
* data and the provided options merged with the existing properties of the current object.
|
||||
*/
|
||||
getComputedCount(): number {
|
||||
let sum = 0;
|
||||
this.dfs(node => (sum += node.count));
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new TreeMultiMapNode with the specified key, value, color, and count.
|
||||
* @param {K} key - The key parameter represents the key of the node being created. It is of type K,
|
||||
* which is a generic type representing the type of keys in the tree.
|
||||
* @param {V} [value] - The `value` parameter is an optional parameter that represents the value
|
||||
* associated with the key in the node. It is of type `V`, which can be any data type.
|
||||
* @param {RBTNColor} [color=BLACK] - The color parameter is used to specify the color of the node in
|
||||
* a Red-Black Tree. It can have two possible values: 'RED' or 'BLACK'. The default value is 'BLACK'.
|
||||
* @param {number} [count] - The `count` parameter represents the number of occurrences of a key in
|
||||
* the tree. It is an optional parameter and is used to keep track of the number of values associated
|
||||
* with a key in the tree.
|
||||
* @returns A new instance of the TreeMultiMapNode class, casted as TreeMultiMapNode<K, V>.
|
||||
*/
|
||||
override createNode(key: K, value?: V, color: RBTNColor = 'BLACK', count?: number): TreeMultiMapNode<K, V> {
|
||||
return new TreeMultiMapNode(key, this._isMapMode ? undefined : value, count, color) as TreeMultiMapNode<K, V>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function creates a new instance of a TreeMultiMap with the specified options and returns it.
|
||||
* @param [options] - The `options` parameter is an optional object that contains additional
|
||||
* configuration options for creating the `TreeMultiMap`. It is of type `TreeMultiMapOptions<K, V,
|
||||
* R>`.
|
||||
* @returns a new instance of the `TreeMultiMap` class, with the provided options merged with the
|
||||
* existing `iterationType` property. The returned value is casted as `TREE`.
|
||||
*/
|
||||
override createTree(options?: TreeMultiMapOptions<K, V, R>) {
|
||||
override createTree(options?: TreeMultiMapOptions<K, V[], R>) {
|
||||
return new TreeMultiMap<K, V, R, MK, MV, MR>([], {
|
||||
iterationType: this.iterationType,
|
||||
isMapMode: this._isMapMode,
|
||||
specifyComparable: this._specifyComparable,
|
||||
toEntryFn: this._toEntryFn,
|
||||
isReverse: this._isReverse,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The function checks if the input is an instance of the TreeMultiMapNode class.
|
||||
* @param {BTNRep<K, V, TreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, TreeMultiMapNode<K, V>>`.
|
||||
* @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is
|
||||
* an instance of the `TreeMultiMapNode` class.
|
||||
*/
|
||||
override isNode(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, TreeMultiMapNode<K, V>> | R
|
||||
): keyNodeEntryOrRaw is TreeMultiMapNode<K, V> {
|
||||
return keyNodeEntryOrRaw instanceof TreeMultiMapNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function overrides the add method of a class and adds a new node to a data structure, updating
|
||||
* the count and returning a boolean indicating success.
|
||||
* @param {BTNRep<K, V, TreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The
|
||||
* `keyNodeEntryOrRaw` parameter can accept one of the following types:
|
||||
* @param {V} [value] - The `value` parameter represents the value associated with the key in the
|
||||
* data structure. It is an optional parameter, so it can be omitted if not needed.
|
||||
* @param [count=1] - The `count` parameter represents the number of times the key-value pair should
|
||||
* be added to the data structure. By default, it is set to 1, meaning that if no value is provided
|
||||
* for `count`, the key-value pair will be added once.
|
||||
* @returns The method is returning a boolean value. It returns true if the addition of the new node
|
||||
* was successful, and false otherwise.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, TreeMultiMapNode<K, V>> | R, value?: V, count = 1): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
|
||||
const orgCount = newNode?.count || 0;
|
||||
const isSuccessAdded = super.add(newNode, newValue);
|
||||
|
||||
if (isSuccessAdded) {
|
||||
this._count += orgCount;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function `delete` in TypeScript overrides the deletion operation in a binary tree data
|
||||
* structure, handling cases where nodes have children and maintaining balance in the tree.
|
||||
* @param {BTNRep<K, V, TreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The `predicate`
|
||||
* parameter in the `delete` method is used to specify the condition or key based on which a node
|
||||
* should be deleted from the binary tree. It can be a key, a node, or an entry.
|
||||
* @param [ignoreCount=false] - The `ignoreCount` parameter in the `override delete` method is a
|
||||
* boolean flag that determines whether to ignore the count of nodes when performing deletion. If
|
||||
* `ignoreCount` is set to `true`, the method will delete the node regardless of its count. If
|
||||
* `ignoreCount` is `false
|
||||
* @returns The `override delete` method returns an array of `BinaryTreeDeleteResult<TreeMultiMapNode<K, V>>` objects.
|
||||
*/
|
||||
override delete(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, TreeMultiMapNode<K, V>> | R,
|
||||
ignoreCount = false
|
||||
): BinaryTreeDeleteResult<TreeMultiMapNode<K, V>>[] {
|
||||
if (keyNodeEntryOrRaw === null) return [];
|
||||
|
||||
const results: BinaryTreeDeleteResult<TreeMultiMapNode<K, V>>[] = [];
|
||||
|
||||
let nodeToDelete: OptNode<TreeMultiMapNode<K, V>>;
|
||||
if (this._isPredicate(keyNodeEntryOrRaw)) nodeToDelete = this.getNode(keyNodeEntryOrRaw);
|
||||
else nodeToDelete = this.isRealNode(keyNodeEntryOrRaw) ? keyNodeEntryOrRaw : this.getNode(keyNodeEntryOrRaw);
|
||||
|
||||
if (!nodeToDelete) {
|
||||
return results;
|
||||
}
|
||||
|
||||
let originalColor = nodeToDelete.color;
|
||||
let replacementNode: TreeMultiMapNode<K, V> | undefined;
|
||||
|
||||
if (!this.isRealNode(nodeToDelete.left)) {
|
||||
if (nodeToDelete.right !== null) replacementNode = nodeToDelete.right;
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
if (nodeToDelete.right !== null) {
|
||||
this._transplant(nodeToDelete, nodeToDelete.right);
|
||||
this._count -= nodeToDelete.count;
|
||||
}
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
} else if (!this.isRealNode(nodeToDelete.right)) {
|
||||
replacementNode = nodeToDelete.left;
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, nodeToDelete.left);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
} else {
|
||||
const successor = this.getLeftMost(node => node, nodeToDelete.right);
|
||||
if (successor) {
|
||||
originalColor = successor.color;
|
||||
if (successor.right !== null) replacementNode = successor.right;
|
||||
|
||||
if (successor.parent === nodeToDelete) {
|
||||
if (this.isRealNode(replacementNode)) {
|
||||
replacementNode.parent = successor;
|
||||
}
|
||||
} else {
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
if (successor.right !== null) {
|
||||
this._transplant(successor, successor.right);
|
||||
this._count -= nodeToDelete.count;
|
||||
}
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
successor.right = nodeToDelete.right;
|
||||
if (this.isRealNode(successor.right)) {
|
||||
successor.right.parent = successor;
|
||||
}
|
||||
}
|
||||
if (ignoreCount || nodeToDelete.count <= 1) {
|
||||
this._transplant(nodeToDelete, successor);
|
||||
this._count -= nodeToDelete.count;
|
||||
} else {
|
||||
nodeToDelete.count--;
|
||||
this._count--;
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
return results;
|
||||
}
|
||||
successor.left = nodeToDelete.left;
|
||||
if (this.isRealNode(successor.left)) {
|
||||
successor.left.parent = successor;
|
||||
}
|
||||
successor.color = nodeToDelete.color;
|
||||
}
|
||||
}
|
||||
this._size--;
|
||||
|
||||
// If the original color was black, fix the tree
|
||||
if (originalColor === 'BLACK') {
|
||||
this._deleteFixup(replacementNode);
|
||||
}
|
||||
|
||||
results.push({ deleted: nodeToDelete, needBalanced: undefined });
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The "clear" function overrides the parent class's "clear" function and also resets the count to
|
||||
* zero.
|
||||
* The function `createNode` overrides the method to create a new `TreeMultiMapNode` with a specified
|
||||
* key and an empty array of values.
|
||||
* @param {K} key - The `key` parameter in the `createNode` method represents the key of the node
|
||||
* that will be created in the TreeMultiMap data structure.
|
||||
* @returns A new instance of `TreeMultiMapNode<K, V>` is being returned, with the specified key and
|
||||
* an empty array as its value.
|
||||
*/
|
||||
override clear() {
|
||||
super.clear();
|
||||
this._count = 0;
|
||||
override createNode(key: K): TreeMultiMapNode<K, V> {
|
||||
return new TreeMultiMapNode<K, V>(key, []);
|
||||
}
|
||||
|
||||
override add(node: BTNRep<K, V[], TreeMultiMapNode<K, V>>): boolean;
|
||||
|
||||
override add(key: K, value: V): boolean;
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n log n)
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The `perfectlyBalance` function takes a sorted array of nodes and builds a balanced binary search
|
||||
* tree using either a recursive or iterative approach.
|
||||
* @param {IterationType} iterationType - The `iterationType` parameter is an optional parameter that
|
||||
* specifies the type of iteration to use when building the balanced binary search tree. It has a
|
||||
* default value of `this.iterationType`, which means it will use the iteration type specified by the
|
||||
* `iterationType` property of the current object.
|
||||
* @returns The function `perfectlyBalance` returns a boolean value. It returns `true` if the
|
||||
* balancing operation is successful, and `false` if there are no nodes to balance.
|
||||
* The function `add` in TypeScript overrides the superclass method to add key-value pairs to a
|
||||
* TreeMultiMapNode, handling different input types and scenarios.
|
||||
* @param {BTNRep<K, V[], TreeMultiMapNode<K, V>> | K} keyNodeOrEntry - The `keyNodeOrEntry`
|
||||
* parameter in the `override add` method can be either a `BTNRep` object containing a key, an array
|
||||
* of values, and a `TreeMultiMapNode`, or just a key.
|
||||
* @param {V} [value] - The `value` parameter in the `override add` method represents the value that
|
||||
* you want to add to the TreeMultiMap. If the key is already present in the map, the new value will
|
||||
* be added to the existing list of values associated with that key. If the key is not present,
|
||||
* @returns The `add` method is returning a boolean value, which indicates whether the operation was
|
||||
* successful or not.
|
||||
*/
|
||||
override perfectlyBalance(iterationType: IterationType = this.iterationType): boolean {
|
||||
const sorted = this.dfs(node => node, 'IN'),
|
||||
n = sorted.length;
|
||||
if (sorted.length < 1) return false;
|
||||
override add(keyNodeOrEntry: BTNRep<K, V[], TreeMultiMapNode<K, V>> | K, value?: V): boolean {
|
||||
if (this.isRealNode(keyNodeOrEntry)) return super.add(keyNodeOrEntry);
|
||||
|
||||
this.clear();
|
||||
const _commonAdd = (key?: BTNOptKeyOrNull<K>, values?: V[]) => {
|
||||
if (key === undefined || key === null) return false;
|
||||
|
||||
if (iterationType === 'RECURSIVE') {
|
||||
const buildBalanceBST = (l: number, r: number) => {
|
||||
if (l > r) return;
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
buildBalanceBST(l, m - 1);
|
||||
buildBalanceBST(m + 1, r);
|
||||
};
|
||||
|
||||
buildBalanceBST(0, n - 1);
|
||||
return true;
|
||||
} else {
|
||||
const stack: [[number, number]] = [[0, n - 1]];
|
||||
while (stack.length > 0) {
|
||||
const popped = stack.pop();
|
||||
if (popped) {
|
||||
const [l, r] = popped;
|
||||
if (l <= r) {
|
||||
const m = l + Math.floor((r - l) / 2);
|
||||
const midNode = sorted[m];
|
||||
if (this._isMapMode) this.add(midNode.key, undefined, midNode.count);
|
||||
else this.add(midNode.key, midNode.value, midNode.count);
|
||||
stack.push([m + 1, r]);
|
||||
stack.push([l, m - 1]);
|
||||
}
|
||||
}
|
||||
const existingValues = this.get(key);
|
||||
if (existingValues !== undefined && values !== undefined) {
|
||||
for (const value of values) existingValues.push(value);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
const existingNode = this.getNode(key);
|
||||
if (this.isRealNode(existingNode)) {
|
||||
if (existingValues === undefined) {
|
||||
super.add(key, values);
|
||||
return true;
|
||||
}
|
||||
if (values !== undefined) {
|
||||
for (const value of values) existingValues.push(value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return super.add(key, values);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.isEntry(keyNodeOrEntry)) {
|
||||
const [key, values] = keyNodeOrEntry;
|
||||
return _commonAdd(key, value !== undefined ? [value] : values);
|
||||
}
|
||||
|
||||
return _commonAdd(keyNodeOrEntry, value !== undefined ? [value] : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time complexity: O(n)
|
||||
* Space complexity: O(n)
|
||||
* Time Complexity: O(log n)
|
||||
* Space Complexity: O(log n)
|
||||
*
|
||||
* The function overrides the clone method to create a deep copy of a tree object.
|
||||
* @returns The `clone()` method is returning a cloned instance of the `TREE` object.
|
||||
* The function `deleteValue` removes a specific value from a key in a TreeMultiMap data structure
|
||||
* and deletes the entire node if no values are left for that key.
|
||||
* @param {BTNRep<K, V[], TreeMultiMapNode<K, V>> | K} keyNodeOrEntry - The `keyNodeOrEntry`
|
||||
* parameter in the `deleteValue` function can be either a `BTNRep` object containing a key and an
|
||||
* array of values, or just a key itself.
|
||||
* @param {V} value - The `value` parameter in the `deleteValue` function represents the specific
|
||||
* value that you want to remove from the multi-map data structure associated with a particular key.
|
||||
* The function checks if the value exists in the array of values associated with the key, and if
|
||||
* found, removes it from the array.
|
||||
* @returns The `deleteValue` function returns a boolean value - `true` if the specified `value` was
|
||||
* successfully deleted from the values associated with the `keyNodeOrEntry`, and `false` otherwise.
|
||||
*/
|
||||
deleteValue(keyNodeOrEntry: BTNRep<K, V[], TreeMultiMapNode<K, V>> | K, value: V): boolean {
|
||||
const values = this.get(keyNodeOrEntry);
|
||||
if (Array.isArray(values)) {
|
||||
const index = values.indexOf(value);
|
||||
if (index === -1) return false;
|
||||
values.splice(index, 1);
|
||||
|
||||
// If no values left, remove the entire node
|
||||
if (values.length === 0) this.delete(keyNodeOrEntry);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n)
|
||||
* Space Complexity: O(n)
|
||||
*
|
||||
* The function `clone` overrides the default cloning behavior to create a deep copy of a tree
|
||||
* structure.
|
||||
* @returns The `cloned` object is being returned.
|
||||
*/
|
||||
override clone() {
|
||||
const cloned = this.createTree();
|
||||
this.bfs(node => cloned.add(node.key, undefined, node.count));
|
||||
if (this._isMapMode) cloned._store = this._store;
|
||||
this._clone(cloned);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `map` function in TypeScript overrides the default behavior to create a new TreeMultiMap with
|
||||
* modified entries based on a provided callback.
|
||||
* @param callback - The `callback` parameter is a function that will be called for each entry in the
|
||||
* map. It takes four arguments:
|
||||
* @param [options] - The `options` parameter in the `override map` function is of type
|
||||
* `TreeMultiMapOptions<MK, MV, MR>`. This parameter allows you to provide additional configuration
|
||||
* options when creating a new `TreeMultiMap` instance within the `map` function. These options could
|
||||
* include things like
|
||||
* @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify
|
||||
* the value of `this` when executing the `callback` function. It allows you to set the context
|
||||
* (value of `this`) for the callback function when it is called within the `map` function. This
|
||||
* @returns A new TreeMultiMap instance is being returned, which is populated with entries generated
|
||||
* by the provided callback function.
|
||||
*/
|
||||
override map(
|
||||
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
||||
options?: TreeMultiMapOptions<MK, MV, MR>,
|
||||
thisArg?: any
|
||||
): TreeMultiMap<MK, MV, MR> {
|
||||
const newTree = new TreeMultiMap<MK, MV, MR>([], options);
|
||||
let index = 0;
|
||||
for (const [key, value] of this) {
|
||||
newTree.add(callback.call(thisArg, key, value, index++, this));
|
||||
}
|
||||
return newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a
|
||||
* node based on the input.
|
||||
* @param {BTNRep<K, V, TreeMultiMapNode<K, V>> | R} keyNodeEntryOrRaw - The parameter
|
||||
* `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, TreeMultiMapNode<K, V>>`.
|
||||
* @param {V} [value] - The `value` parameter is an optional value that represents the value
|
||||
* associated with the key in the node. It is used when creating a new node or updating the value of
|
||||
* an existing node.
|
||||
* @param [count=1] - The `count` parameter is an optional parameter that specifies the number of
|
||||
* times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
|
||||
* @returns either a TreeMultiMapNode<K, V> object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, TreeMultiMapNode<K, V>> | R,
|
||||
value?: V,
|
||||
count = 1
|
||||
): [TreeMultiMapNode<K, V> | undefined, V | undefined] {
|
||||
if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined];
|
||||
|
||||
if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value];
|
||||
|
||||
if (this.isEntry(keyNodeEntryOrRaw)) {
|
||||
const [key, entryValue] = keyNodeEntryOrRaw;
|
||||
if (key === undefined || key === null) return [undefined, undefined];
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
|
||||
}
|
||||
|
||||
if (this.isRaw(keyNodeEntryOrRaw)) {
|
||||
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
|
||||
const finalValue = value ?? entryValue;
|
||||
if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
|
||||
}
|
||||
|
||||
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value];
|
||||
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `_swapProperties` function swaps the properties (key, value, count, color) between two nodes
|
||||
* in a binary search tree.
|
||||
* @param {R | BSTNOptKeyOrNode<K, TreeMultiMapNode<K, V>>} srcNode - The `srcNode` parameter represents the source node
|
||||
* that will be swapped with the `destNode`. It can be either an instance of the `R` class or an
|
||||
* instance of the `BSTNOptKeyOrNode<K, TreeMultiMapNode<K, V>>` class.
|
||||
* @param {R | BSTNOptKeyOrNode<K, TreeMultiMapNode<K, V>>} destNode - The `destNode` parameter represents the destination
|
||||
* node where the properties will be swapped with the source node.
|
||||
* @returns The method is returning the `destNode` after swapping its properties with the `srcNode`.
|
||||
* If either `srcNode` or `destNode` is undefined, it returns undefined.
|
||||
*/
|
||||
protected override _swapProperties(
|
||||
srcNode: R | BSTNOptKeyOrNode<K, TreeMultiMapNode<K, V>>,
|
||||
destNode: R | BSTNOptKeyOrNode<K, TreeMultiMapNode<K, V>>
|
||||
): TreeMultiMapNode<K, V> | undefined {
|
||||
srcNode = this.ensureNode(srcNode);
|
||||
destNode = this.ensureNode(destNode);
|
||||
if (srcNode && destNode) {
|
||||
const { key, value, count, color } = destNode;
|
||||
const tempNode = this.createNode(key, value, color, count);
|
||||
if (tempNode) {
|
||||
tempNode.color = color;
|
||||
|
||||
destNode.key = srcNode.key;
|
||||
if (!this._isMapMode) destNode.value = srcNode.value;
|
||||
destNode.count = srcNode.count;
|
||||
destNode.color = srcNode.color;
|
||||
|
||||
srcNode.key = tempNode.key;
|
||||
if (!this._isMapMode) srcNode.value = tempNode.value;
|
||||
srcNode.count = tempNode.count;
|
||||
srcNode.color = tempNode.color;
|
||||
}
|
||||
|
||||
return destNode;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(1)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The function replaces an old node with a new node and updates the count property of the new node.
|
||||
* @param {TreeMultiMapNode<K, V>} oldNode - The `oldNode` parameter is the node that you want to replace in the data
|
||||
* structure.
|
||||
* @param {TreeMultiMapNode<K, V>} newNode - The `newNode` parameter is an instance of the `TreeMultiMapNode<K, V>` class.
|
||||
* @returns The method is returning the result of calling the `_replaceNode` method from the
|
||||
* superclass, which is of type `TreeMultiMapNode<K, V>`.
|
||||
*/
|
||||
protected override _replaceNode(
|
||||
oldNode: TreeMultiMapNode<K, V>,
|
||||
newNode: TreeMultiMapNode<K, V>
|
||||
): TreeMultiMapNode<K, V> {
|
||||
newNode.count = oldNode.count + newNode.count;
|
||||
return super._replaceNode(oldNode, newNode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ export class DirectedEdge<E = any> extends AbstractEdge<E> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class DirectedGraph<
|
||||
V = any,
|
||||
E = any,
|
||||
|
|
|
@ -40,6 +40,9 @@ export class MapEdge<E = any> extends DirectedEdge<E> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class MapGraph<
|
||||
V = any,
|
||||
E = any,
|
||||
|
|
|
@ -42,6 +42,9 @@ export class UndirectedEdge<E = number> extends AbstractEdge<E> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class UndirectedGraph<
|
||||
V = any,
|
||||
E = any,
|
||||
|
|
|
@ -59,6 +59,9 @@ export class SinglyLinkedListNode<E = any> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class SinglyLinkedList<E = any, R = any> extends IterableElementBase<E, R, SinglyLinkedList<E, R>> {
|
||||
constructor(
|
||||
elements: Iterable<E> | Iterable<R> | Iterable<SinglyLinkedListNode<E>> = [],
|
||||
|
|
|
@ -19,6 +19,9 @@ export class SkipListNode<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class SkipList<K, V> {
|
||||
/**
|
||||
* The constructor function initializes a SkipLinkedList object with optional options and elements.
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
*/
|
||||
import type { MatrixOptions } from '../../types';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class Matrix {
|
||||
/**
|
||||
* The constructor function initializes a matrix object with the provided data and options, or with
|
||||
|
|
|
@ -25,6 +25,9 @@ export class Character {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class Navigator<T = number> {
|
||||
onMove: (cur: [number, number]) => void;
|
||||
protected readonly _matrix: T[][];
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
import type { Comparator, ElementCallback, PriorityQueueOptions } from '../../types';
|
||||
import { PriorityQueue } from './priority-queue';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class MaxPriorityQueue<E = any, R = any> extends PriorityQueue<E, R> {
|
||||
/**
|
||||
* The constructor initializes a PriorityQueue with optional elements and options, including a
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
import type { Comparator, ElementCallback, PriorityQueueOptions } from '../../types';
|
||||
import { PriorityQueue } from './priority-queue';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class MinPriorityQueue<E = any, R = any> extends PriorityQueue<E, R> {
|
||||
/**
|
||||
* The constructor initializes a PriorityQueue with optional elements and options, including a
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
import type { ElementCallback, TrieOptions } from '../../types';
|
||||
import { IterableElementBase } from '../base';
|
||||
|
||||
/**
|
||||
* TrieNode represents a node in the Trie data structure. It holds a character key, a map of children nodes,
|
||||
* and a flag indicating whether it's the end of a word.
|
||||
*/
|
||||
export class TrieNode {
|
||||
constructor(key: string) {
|
||||
this._key = key;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import { AVLTreeOptions } from './avl-tree';
|
||||
|
||||
export type AVLTreeCounterOptions<K, V, R> = AVLTreeOptions<K, V, R> & {};
|
|
@ -1,3 +1,3 @@
|
|||
import type { AVLTreeOptions } from './avl-tree';
|
||||
|
||||
export type AVLTreeMultiMapOptions<K, V, R> = AVLTreeOptions<K, V, R> & {}
|
||||
export type AVLTreeMultiMapOptions<K, V, R> = Omit<AVLTreeOptions<K, V, R>, 'isMapMode'> & {}
|
||||
|
|
|
@ -5,3 +5,5 @@ export * from './segment-tree';
|
|||
export * from './avl-tree-multi-map';
|
||||
export * from './rb-tree';
|
||||
export * from './tree-multi-map';
|
||||
export * from './tree-counter';
|
||||
export * from './avl-tree-counter';
|
||||
|
|
3
src/types/data-structures/binary-tree/tree-counter.ts
Normal file
3
src/types/data-structures/binary-tree/tree-counter.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import type { RedBlackTreeOptions } from './rb-tree';
|
||||
|
||||
export type TreeCounterOptions<K, V, R> = RedBlackTreeOptions<K, V, R> & {};
|
|
@ -1,3 +1,3 @@
|
|||
import type { RedBlackTreeOptions } from './rb-tree';
|
||||
|
||||
export type TreeMultiMapOptions<K, V, R> = RedBlackTreeOptions<K, V, R> & {}
|
||||
export type TreeMultiMapOptions<K, V, R> = Omit<RedBlackTreeOptions<K, V, R>, 'isMapMode'> & {}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
var data_structure_typed_1 = require('data-structure-typed');
|
||||
|
||||
var orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8];
|
||||
var orgStrArr = ['trie', 'trial', 'trick', 'trip', 'tree', 'trend', 'triangle', 'track', 'trace', 'transmit'];
|
||||
var entries = [
|
||||
|
|
|
@ -8,14 +8,14 @@ const { HUNDRED_THOUSAND } = magnitude;
|
|||
const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true);
|
||||
|
||||
suite
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
||||
avlTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) avlTree.add(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => {
|
||||
avlTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) avlTree.add(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
||||
avlTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) avlTree.add(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]);
|
||||
})
|
||||
|
|
|
@ -7,19 +7,20 @@ import { isCompetitor } from '../../../config';
|
|||
const suite = new Benchmark.Suite();
|
||||
const rbTree = new RedBlackTree<number>();
|
||||
const rbTreeNodeMode = new RedBlackTree<number>([], { isMapMode: false });
|
||||
|
||||
const { HUNDRED_THOUSAND } = magnitude;
|
||||
const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true);
|
||||
const cOrderedMap = new OrderedMap<number, number>();
|
||||
|
||||
suite
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add randomly`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
|
||||
rbTree.clear();
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.add(i);
|
||||
})
|
||||
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
||||
for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]);
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PriorityQueue as MJSPriorityQueue } from '../../../../dist/cjs';
|
||||
import { PriorityQueue as MJSPriorityQueue } from '../../../../dist/esm';
|
||||
import { PriorityQueue as SRCPriorityQueue } from '../../../../src';
|
||||
import { PriorityQueue as CJSPriorityQueue } from '../../../../dist/mjs';
|
||||
import { PriorityQueue as CJSPriorityQueue } from '../../../../dist/cjs';
|
||||
import {
|
||||
Deque as CDeque,
|
||||
HashMap as CHashMap,
|
||||
|
|
|
@ -49,7 +49,7 @@ if (args.length > 0) {
|
|||
testFiles = allFiles.filter(file => args.every(word => file.includes(word)));
|
||||
isIndividual = true;
|
||||
console.log(
|
||||
`${testFiles.map(file => coloredLabeled('Matched', file)).join(`
|
||||
`${testFiles.map(file => coloredLabeled('Found', file)).join(`
|
||||
`)}`
|
||||
);
|
||||
} else {
|
||||
|
@ -229,9 +229,11 @@ const sortedPerformanceTests = (
|
|||
return 0;
|
||||
});
|
||||
|
||||
console.log(`${GREEN} Found tests (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`);
|
||||
console.log(
|
||||
`${GREEN} Running tests (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
||||
`${GREEN} Matched Suites (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`
|
||||
);
|
||||
console.log(
|
||||
`${GREEN} Running Suites (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
||||
);
|
||||
|
||||
sortedPerformanceTests.forEach(item => {
|
||||
|
|
877
test/unit/data-structures/binary-tree/avl-tree-counter.test.ts
Normal file
877
test/unit/data-structures/binary-tree/avl-tree-counter.test.ts
Normal file
|
@ -0,0 +1,877 @@
|
|||
import { AVLTreeCounter, AVLTreeCounterNode, AVLTreeNode, BinaryTreeNode, BSTNode } from '../../../../src';
|
||||
import { isDebugTest } from '../../../config';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
|
||||
describe('AVLTreeCounter count', () => {
|
||||
let avlCounter: AVLTreeCounter<number>;
|
||||
beforeEach(() => {
|
||||
avlCounter = new AVLTreeCounter<number>();
|
||||
});
|
||||
it('Should added isolated node count ', () => {
|
||||
avlCounter.addMany([
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3],
|
||||
[4, 4],
|
||||
[5, 5]
|
||||
]);
|
||||
const newNode = new AVLTreeCounterNode(3, 33, 10);
|
||||
avlCounter.add(newNode);
|
||||
expect(avlCounter.count).toBe(15);
|
||||
});
|
||||
|
||||
it('Should count', () => {
|
||||
avlCounter.addMany([
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3]
|
||||
]);
|
||||
avlCounter.add([2, 2], undefined, 10);
|
||||
avlCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 1);
|
||||
avlCounter.delete(2);
|
||||
expect(avlCounter.count).toBe(12);
|
||||
expect(avlCounter.getComputedCount()).toBe(16);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter operations test1', () => {
|
||||
it('should perform various operations on a AVLTreeCounter with numeric values1', () => {
|
||||
const avlCounter = new AVLTreeCounter<number>();
|
||||
|
||||
expect(avlCounter instanceof AVLTreeCounter);
|
||||
avlCounter.add([11, 11]);
|
||||
avlCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
avlCounter.addMany(idAndValues);
|
||||
expect(avlCounter.root instanceof AVLTreeCounterNode);
|
||||
|
||||
if (avlCounter.root) expect(avlCounter.root.key == 11);
|
||||
|
||||
expect(avlCounter.size).toBe(16);
|
||||
expect(avlCounter.count).toBe(18);
|
||||
|
||||
expect(avlCounter.has(6));
|
||||
|
||||
expect(avlCounter.getHeight(6)).toBe(4);
|
||||
expect(avlCounter.getDepth(6)).toBe(0);
|
||||
const nodeId10 = avlCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = avlCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
|
||||
const nodesByCount1 = avlCounter.getNodes(node => node.count === 1);
|
||||
expect(nodesByCount1.length).toBe(14);
|
||||
|
||||
const nodesByCount2 = avlCounter.getNodes(node => node.count === 2);
|
||||
expect(nodesByCount2.length).toBe(2);
|
||||
const leftMost = avlCounter.getLeftMost();
|
||||
expect(leftMost).toBe(1);
|
||||
|
||||
const node15 = avlCounter.getNode(15);
|
||||
const minNodeBySpecificNode = node15 && avlCounter.getLeftMost(node => node, node15);
|
||||
expect(minNodeBySpecificNode?.key).toBe(15);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15);
|
||||
expect(subTreeSum).toBe(31);
|
||||
let lesserSum = 0;
|
||||
avlCounter.lesserOrGreaterTraverse((node: AVLTreeCounterNode<number>) => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
expect(node15 instanceof AVLTreeCounterNode);
|
||||
if (node15 instanceof AVLTreeCounterNode) {
|
||||
const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', 15);
|
||||
expect(subTreeAdd);
|
||||
}
|
||||
const node11 = avlCounter.getNode(11);
|
||||
expect(node11 instanceof AVLTreeCounterNode);
|
||||
if (node11 instanceof AVLTreeCounterNode) {
|
||||
const allGreaterNodesAdded = avlCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11);
|
||||
expect(allGreaterNodesAdded);
|
||||
}
|
||||
|
||||
const dfsInorderNodes = avlCounter.dfs(node => node, 'IN');
|
||||
expect(dfsInorderNodes[0].key).toBe(1);
|
||||
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
|
||||
expect(avlCounter.isPerfectlyBalanced()).toBe(false);
|
||||
|
||||
avlCounter.perfectlyBalance();
|
||||
|
||||
expect(avlCounter.isPerfectlyBalanced()).toBe(true);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsNodesAfterBalanced = avlCounter.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(8);
|
||||
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
|
||||
|
||||
const removed11 = avlCounter.delete(11, true);
|
||||
expect(removed11 instanceof Array);
|
||||
expect(removed11[0]);
|
||||
expect(removed11[0].deleted);
|
||||
|
||||
if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight(15)).toBe(1);
|
||||
|
||||
const removed1 = avlCounter.delete(1, true);
|
||||
expect(removed1 instanceof Array);
|
||||
expect(removed1[0]);
|
||||
expect(removed1[0].deleted);
|
||||
if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight()).toBe(4);
|
||||
|
||||
const removed4 = avlCounter.delete(4, true);
|
||||
expect(removed4 instanceof Array);
|
||||
expect(removed4[0]);
|
||||
expect(removed4[0].deleted);
|
||||
if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(4);
|
||||
|
||||
const removed10 = avlCounter.delete(10, true);
|
||||
expect(removed10 instanceof Array);
|
||||
expect(removed10[0]);
|
||||
expect(removed10[0].deleted);
|
||||
if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed15 = avlCounter.delete(15, true);
|
||||
expect(removed15 instanceof Array);
|
||||
expect(removed15[0]);
|
||||
expect(removed15[0].deleted);
|
||||
if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed5 = avlCounter.delete(5, true);
|
||||
expect(removed5 instanceof Array);
|
||||
expect(removed5[0]);
|
||||
expect(removed5[0].deleted);
|
||||
if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed13 = avlCounter.delete(13, true);
|
||||
expect(removed13 instanceof Array);
|
||||
expect(removed13[0]);
|
||||
expect(removed13[0].deleted);
|
||||
if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed3 = avlCounter.delete(3, true);
|
||||
expect(removed3 instanceof Array);
|
||||
expect(removed3[0]);
|
||||
expect(removed3[0].deleted);
|
||||
if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed8 = avlCounter.delete(8, true);
|
||||
expect(removed8 instanceof Array);
|
||||
expect(removed8[0]);
|
||||
expect(removed8[0].deleted);
|
||||
if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed6 = avlCounter.delete(6, true);
|
||||
expect(removed6 instanceof Array);
|
||||
expect(removed6[0]);
|
||||
expect(removed6[0].deleted);
|
||||
if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6);
|
||||
expect(avlCounter.delete(6, true).length).toBe(0);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed7 = avlCounter.delete(7, true);
|
||||
expect(removed7 instanceof Array);
|
||||
expect(removed7[0]);
|
||||
expect(removed7[0].deleted);
|
||||
if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed9 = avlCounter.delete(9, true);
|
||||
expect(removed9 instanceof Array);
|
||||
expect(removed9[0]);
|
||||
expect(removed9[0].deleted);
|
||||
if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed14 = avlCounter.delete(14, true);
|
||||
expect(removed14 instanceof Array);
|
||||
expect(removed14[0]);
|
||||
expect(removed14[0].deleted);
|
||||
if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(1);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsIDs = avlCounter.bfs(node => node.key);
|
||||
|
||||
expect(bfsIDs[0]).toBe(12);
|
||||
expect(bfsIDs[1]).toBe(2);
|
||||
expect(bfsIDs[2]).toBe(16);
|
||||
|
||||
const bfsNodes = avlCounter.bfs(node => node);
|
||||
|
||||
expect(bfsNodes[0].key).toBe(12);
|
||||
expect(bfsNodes[1].key).toBe(2);
|
||||
expect(bfsNodes[2].key).toBe(16);
|
||||
|
||||
expect(avlCounter.count).toBe(8);
|
||||
});
|
||||
|
||||
it('should perform various operations on a AVLTreeCounter with object values', () => {
|
||||
const objAvlCounter = new AVLTreeCounter<number, { key: number; keyA: number }>();
|
||||
expect(objAvlCounter).toBeInstanceOf(AVLTreeCounter);
|
||||
objAvlCounter.add([11, { key: 11, keyA: 11 }]);
|
||||
objAvlCounter.add([3, { key: 3, keyA: 3 }]);
|
||||
const values: [number, { key: number; keyA: number }][] = [
|
||||
[15, { key: 15, keyA: 15 }],
|
||||
[1, { key: 1, keyA: 1 }],
|
||||
[8, { key: 8, keyA: 8 }],
|
||||
[13, { key: 13, keyA: 13 }],
|
||||
[16, { key: 16, keyA: 16 }],
|
||||
[2, { key: 2, keyA: 2 }],
|
||||
[6, { key: 6, keyA: 6 }],
|
||||
[9, { key: 9, keyA: 9 }],
|
||||
[12, { key: 12, keyA: 12 }],
|
||||
[14, { key: 14, keyA: 14 }],
|
||||
[4, { key: 4, keyA: 4 }],
|
||||
[7, { key: 7, keyA: 7 }],
|
||||
[10, { key: 10, keyA: 10 }],
|
||||
[5, { key: 5, keyA: 5 }]
|
||||
];
|
||||
|
||||
objAvlCounter.addMany(values);
|
||||
|
||||
expect(objAvlCounter.root).toBeInstanceOf(AVLTreeCounterNode);
|
||||
|
||||
if (objAvlCounter.root) expect(objAvlCounter.root.key).toBe(6);
|
||||
|
||||
expect(objAvlCounter.count).toBe(16);
|
||||
|
||||
expect(objAvlCounter.has(6)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter operations test recursively1', () => {
|
||||
it('should perform various operations on a AVLTreeCounter with numeric values1', () => {
|
||||
const avlCounter = new AVLTreeCounter<number>([], {
|
||||
iterationType: 'RECURSIVE'
|
||||
});
|
||||
|
||||
expect(avlCounter instanceof AVLTreeCounter);
|
||||
avlCounter.add([11, 11]);
|
||||
avlCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
avlCounter.addMany(idAndValues);
|
||||
expect(avlCounter.root).toBeInstanceOf(AVLTreeCounterNode);
|
||||
|
||||
if (avlCounter.root) expect(avlCounter.root.key).toBe(6);
|
||||
|
||||
expect(avlCounter.size).toBe(16);
|
||||
expect(avlCounter.count).toBe(18);
|
||||
|
||||
expect(avlCounter.has(6));
|
||||
|
||||
expect(avlCounter.getHeight(6)).toBe(4);
|
||||
expect(avlCounter.getDepth(6)).toBe(0);
|
||||
const nodeId10 = avlCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = avlCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
|
||||
const nodesByCount1 = avlCounter.getNodes(node => node.count === 1);
|
||||
expect(nodesByCount1.length).toBe(14);
|
||||
|
||||
const nodesByCount2 = avlCounter.getNodes(node => node.count === 2);
|
||||
expect(nodesByCount2.length).toBe(2);
|
||||
const leftMost = avlCounter.getLeftMost();
|
||||
expect(leftMost).toBe(1);
|
||||
|
||||
const node15 = avlCounter.getNode(15);
|
||||
const minNodeBySpecificNode = node15 && avlCounter.getLeftMost(node => node, node15);
|
||||
expect(minNodeBySpecificNode?.key).toBe(15);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) avlCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15);
|
||||
expect(subTreeSum).toBe(31);
|
||||
let lesserSum = 0;
|
||||
avlCounter.lesserOrGreaterTraverse((node: AVLTreeCounterNode<number>) => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
expect(node15 instanceof AVLTreeCounterNode);
|
||||
if (node15 instanceof AVLTreeCounterNode) {
|
||||
const subTreeAdd = avlCounter.dfs(node => (node.count += 1), 'PRE', 15);
|
||||
expect(subTreeAdd);
|
||||
}
|
||||
const node11 = avlCounter.getNode(11);
|
||||
expect(node11 instanceof AVLTreeCounterNode);
|
||||
if (node11 instanceof AVLTreeCounterNode) {
|
||||
const allGreaterNodesAdded = avlCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11);
|
||||
expect(allGreaterNodesAdded);
|
||||
}
|
||||
|
||||
const dfsInorderNodes = avlCounter.dfs(node => node, 'IN');
|
||||
expect(dfsInorderNodes[0].key).toBe(1);
|
||||
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
|
||||
expect(avlCounter.isPerfectlyBalanced()).toBe(true);
|
||||
|
||||
avlCounter.perfectlyBalance();
|
||||
|
||||
expect(avlCounter.isPerfectlyBalanced()).toBe(true);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsNodesAfterBalanced = avlCounter.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(8);
|
||||
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
|
||||
|
||||
const removed11 = avlCounter.delete(11, true);
|
||||
expect(removed11 instanceof Array);
|
||||
expect(removed11[0]);
|
||||
expect(removed11[0].deleted);
|
||||
|
||||
if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight(15)).toBe(1);
|
||||
|
||||
const removed1 = avlCounter.delete(1, true);
|
||||
expect(removed1 instanceof Array);
|
||||
expect(removed1[0]);
|
||||
expect(removed1[0].deleted);
|
||||
if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight()).toBe(4);
|
||||
|
||||
const removed4 = avlCounter.delete(4, true);
|
||||
expect(removed4 instanceof Array);
|
||||
expect(removed4[0]);
|
||||
expect(removed4[0].deleted);
|
||||
if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(4);
|
||||
|
||||
const removed10 = avlCounter.delete(10, true);
|
||||
expect(removed10 instanceof Array);
|
||||
expect(removed10[0]);
|
||||
expect(removed10[0].deleted);
|
||||
if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed15 = avlCounter.delete(15, true);
|
||||
expect(removed15 instanceof Array);
|
||||
expect(removed15[0]);
|
||||
expect(removed15[0].deleted);
|
||||
if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed5 = avlCounter.delete(5, true);
|
||||
expect(removed5 instanceof Array);
|
||||
expect(removed5[0]);
|
||||
expect(removed5[0].deleted);
|
||||
if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed13 = avlCounter.delete(13, true);
|
||||
expect(removed13 instanceof Array);
|
||||
expect(removed13[0]);
|
||||
expect(removed13[0].deleted);
|
||||
if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed3 = avlCounter.delete(3, true);
|
||||
expect(removed3 instanceof Array);
|
||||
expect(removed3[0]);
|
||||
expect(removed3[0].deleted);
|
||||
if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed8 = avlCounter.delete(8, true);
|
||||
expect(removed8 instanceof Array);
|
||||
expect(removed8[0]);
|
||||
expect(removed8[0].deleted);
|
||||
if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed6 = avlCounter.delete(6, true);
|
||||
expect(removed6 instanceof Array);
|
||||
expect(removed6[0]);
|
||||
expect(removed6[0].deleted);
|
||||
if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6);
|
||||
expect(avlCounter.delete(6, true).length).toBe(0);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(avlCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed7 = avlCounter.delete(7, true);
|
||||
expect(removed7 instanceof Array);
|
||||
expect(removed7[0]);
|
||||
expect(removed7[0].deleted);
|
||||
if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed9 = avlCounter.delete(9, true);
|
||||
expect(removed9 instanceof Array);
|
||||
expect(removed9[0]);
|
||||
expect(removed9[0].deleted);
|
||||
if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed14 = avlCounter.delete(14, true);
|
||||
expect(removed14 instanceof Array);
|
||||
expect(removed14[0]);
|
||||
expect(removed14[0].deleted);
|
||||
if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14);
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
expect(avlCounter.getHeight()).toBe(1);
|
||||
|
||||
expect(avlCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsIDs = avlCounter.bfs(node => node.key);
|
||||
|
||||
expect(bfsIDs[0]).toBe(12);
|
||||
expect(bfsIDs[1]).toBe(2);
|
||||
expect(bfsIDs[2]).toBe(16);
|
||||
|
||||
const bfsNodes = avlCounter.bfs(node => node);
|
||||
|
||||
expect(bfsNodes[0].key).toBe(12);
|
||||
expect(bfsNodes[1].key).toBe(2);
|
||||
expect(bfsNodes[2].key).toBe(16);
|
||||
|
||||
expect(avlCounter.count).toBe(8);
|
||||
});
|
||||
|
||||
it('should perform various operations on a AVLTreeCounter with object values', () => {
|
||||
const objAvlCounter = new AVLTreeCounter<number, { key: number; keyA: number }>();
|
||||
expect(objAvlCounter).toBeInstanceOf(AVLTreeCounter);
|
||||
objAvlCounter.add([11, { key: 11, keyA: 11 }]);
|
||||
objAvlCounter.add([3, { key: 3, keyA: 3 }]);
|
||||
const values: [number, { key: number; keyA: number }][] = [
|
||||
[15, { key: 15, keyA: 15 }],
|
||||
[1, { key: 1, keyA: 1 }],
|
||||
[8, { key: 8, keyA: 8 }],
|
||||
[13, { key: 13, keyA: 13 }],
|
||||
[16, { key: 16, keyA: 16 }],
|
||||
[2, { key: 2, keyA: 2 }],
|
||||
[6, { key: 6, keyA: 6 }],
|
||||
[9, { key: 9, keyA: 9 }],
|
||||
[12, { key: 12, keyA: 12 }],
|
||||
[14, { key: 14, keyA: 14 }],
|
||||
[4, { key: 4, keyA: 4 }],
|
||||
[7, { key: 7, keyA: 7 }],
|
||||
[10, { key: 10, keyA: 10 }],
|
||||
[5, { key: 5, keyA: 5 }]
|
||||
];
|
||||
|
||||
objAvlCounter.addMany(values);
|
||||
|
||||
expect(objAvlCounter.root).toBeInstanceOf(AVLTreeCounterNode);
|
||||
|
||||
if (objAvlCounter.root) expect(objAvlCounter.root.key).toBe(6);
|
||||
|
||||
expect(objAvlCounter.count).toBe(16);
|
||||
|
||||
expect(objAvlCounter.has(6)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter Performance test', function () {
|
||||
const avlCounter = new AVLTreeCounter<number, number>();
|
||||
const inputSize = 100000; // Adjust input sizes as needed
|
||||
|
||||
beforeEach(() => {
|
||||
avlCounter.clear();
|
||||
});
|
||||
|
||||
it(`Observe the time consumption of AVLTreeCounter.dfs be good`, function () {
|
||||
const startDFS = performance.now();
|
||||
const dfs = avlCounter.dfs(node => node);
|
||||
if (isDebug) console.log('---bfs', performance.now() - startDFS, dfs.length);
|
||||
});
|
||||
|
||||
it('Should the time consumption of lesserOrGreaterTraverse fitting O(n log n)', function () {
|
||||
const start = performance.now();
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
avlCounter.add(i);
|
||||
}
|
||||
if (isDebug) console.log('---add', performance.now() - start);
|
||||
const startL = performance.now();
|
||||
avlCounter.lesserOrGreaterTraverse(node => (node.count += 1), -1, inputSize / 2);
|
||||
if (isDebug) console.log('---lesserOrGreaterTraverse', performance.now() - startL);
|
||||
});
|
||||
|
||||
it('should the clone method', () => {
|
||||
function checkTreeStructure(avlCounter: AVLTreeCounter<string, number>) {
|
||||
expect(avlCounter.size).toBe(4);
|
||||
expect(avlCounter.root?.key).toBe('2');
|
||||
expect(avlCounter.root?.left?.key).toBe('1');
|
||||
expect(avlCounter.root?.left?.left?.key).toBe(undefined);
|
||||
expect(avlCounter.root?.left?.right?.key).toBe(undefined);
|
||||
expect(avlCounter.root?.right?.key).toBe('4');
|
||||
expect(avlCounter.root?.right?.left?.key).toBe(undefined);
|
||||
expect(avlCounter.root?.right?.right?.key).toBe('5');
|
||||
}
|
||||
|
||||
const avlCounter = new AVLTreeCounter<string, number>();
|
||||
avlCounter.addMany([
|
||||
['2', 2],
|
||||
['4', 4],
|
||||
['5', 5],
|
||||
['3', 3],
|
||||
['1', 1]
|
||||
]);
|
||||
expect(avlCounter.size).toBe(5);
|
||||
expect(avlCounter.root?.key).toBe('2');
|
||||
expect(avlCounter.root?.left?.key).toBe('1');
|
||||
expect(avlCounter.root?.left?.left?.key).toBe(undefined);
|
||||
expect(avlCounter.root?.left?.right?.key).toBe(undefined);
|
||||
expect(avlCounter.root?.right?.key).toBe('4');
|
||||
expect(avlCounter.root?.right?.left?.key).toBe('3');
|
||||
expect(avlCounter.root?.right?.right?.key).toBe('5');
|
||||
avlCounter.delete('3');
|
||||
checkTreeStructure(avlCounter);
|
||||
const cloned = avlCounter.clone();
|
||||
checkTreeStructure(cloned);
|
||||
cloned.delete('1');
|
||||
expect(avlCounter.size).toBe(4);
|
||||
expect(cloned.size).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter iterative methods test', () => {
|
||||
let avlCounter: AVLTreeCounter<number, string>;
|
||||
beforeEach(() => {
|
||||
avlCounter = new AVLTreeCounter<number, string>();
|
||||
avlCounter.add(1, 'a', 10);
|
||||
avlCounter.add([2, 'b'], undefined, 10);
|
||||
avlCounter.add([3, 'c'], undefined, 1);
|
||||
});
|
||||
|
||||
it('The node obtained by get Node should match the node type', () => {
|
||||
const node3 = avlCounter.getNode(3);
|
||||
expect(node3).toBeInstanceOf(BinaryTreeNode);
|
||||
expect(node3).toBeInstanceOf(BSTNode);
|
||||
expect(node3).toBeInstanceOf(AVLTreeNode);
|
||||
});
|
||||
|
||||
it('forEach should iterate over all elements', () => {
|
||||
const mockCallback = jest.fn();
|
||||
avlCounter.forEach((key, value) => {
|
||||
mockCallback(key, value);
|
||||
});
|
||||
|
||||
expect(mockCallback.mock.calls.length).toBe(3);
|
||||
expect(mockCallback.mock.calls[0]).toEqual([1, 'a']);
|
||||
expect(mockCallback.mock.calls[1]).toEqual([2, 'b']);
|
||||
expect(mockCallback.mock.calls[2]).toEqual([3, 'c']);
|
||||
});
|
||||
|
||||
it('filter should return a new avlCounter with filtered elements', () => {
|
||||
const filteredTree = avlCounter.filter(key => key > 1);
|
||||
expect(filteredTree.size).toBe(2);
|
||||
expect([...filteredTree]).toEqual([
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('map should return a new avlCounter with modified elements', () => {
|
||||
const avlCounterMapped = avlCounter.map((key, value) => [(key * 2).toString(), value]);
|
||||
expect(avlCounterMapped.size).toBe(3);
|
||||
expect([...avlCounterMapped]).toEqual([
|
||||
['2', 'a'],
|
||||
['4', 'b'],
|
||||
['6', 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('reduce should accumulate values', () => {
|
||||
const sum = avlCounter.reduce((acc, value, key) => acc + key, 0);
|
||||
expect(sum).toBe(6);
|
||||
});
|
||||
|
||||
it('[Symbol.iterator] should provide an iterator', () => {
|
||||
const entries = [];
|
||||
for (const entry of avlCounter) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
expect(entries.length).toBe(3);
|
||||
expect(entries).toEqual([
|
||||
[1, 'a'],
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('should clone work well', () => {
|
||||
expect(avlCounter.count).toBe(21);
|
||||
const cloned = avlCounter.clone();
|
||||
expect(cloned.root?.left?.key).toBe(1);
|
||||
expect(cloned.get(cloned.root?.right)).toBe('c');
|
||||
});
|
||||
|
||||
it('should keys', () => {
|
||||
const keys = avlCounter.keys();
|
||||
expect([...keys]).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should values', () => {
|
||||
const values = avlCounter.values();
|
||||
expect([...values]).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter toEntryFn', () => {
|
||||
it('should toEntryFn 1', () => {
|
||||
const avlCounter = new AVLTreeCounter<number, number, { obj: { id: number } }>([], {
|
||||
toEntryFn: ele => [ele.obj.id, ele.obj.id]
|
||||
});
|
||||
avlCounter.addMany([
|
||||
{ obj: { id: 1 } },
|
||||
{ obj: { id: 2 } },
|
||||
{ obj: { id: 3 } },
|
||||
{ obj: { id: 4 } },
|
||||
{ obj: { id: 5 } }
|
||||
]);
|
||||
|
||||
const expected = [1, 2, 3, 4, 5];
|
||||
|
||||
expect(avlCounter.morris(node => node.key, 'IN')).toEqual(expected);
|
||||
expect(avlCounter.dfs(node => node.key, 'IN')).toEqual(expected);
|
||||
expect(avlCounter.dfs(node => node.key, 'IN', avlCounter.root, 'RECURSIVE')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should toEntryFn 2', () => {
|
||||
const avlCounter = new AVLTreeCounter<number, number, { obj: { id: number } }>(
|
||||
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
||||
{
|
||||
toEntryFn: ele => [ele.obj.id, ele.obj.id]
|
||||
}
|
||||
);
|
||||
|
||||
const expected = [1, 2, 3, 4, 5];
|
||||
|
||||
expect(avlCounter.morris(node => node.key, 'IN')).toEqual(expected);
|
||||
expect(avlCounter.dfs(node => node.key, 'IN')).toEqual(expected);
|
||||
expect(avlCounter.dfs(node => node.key, 'IN', avlCounter.root, 'RECURSIVE')).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should toEntryFn throw error', () => {
|
||||
expect(
|
||||
() =>
|
||||
new AVLTreeCounter<{ obj: { id: number } }, number>([
|
||||
{ obj: { id: 1 } },
|
||||
{ obj: { id: 2 } },
|
||||
{ obj: { id: 3 } },
|
||||
{ obj: { id: 4 } },
|
||||
{ obj: { id: 5 } }
|
||||
])
|
||||
).toThrowError(
|
||||
`When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.`
|
||||
);
|
||||
});
|
||||
|
||||
it('should toEntryFn 3', () => {
|
||||
const avlCounter = new AVLTreeCounter<{ obj: { id: number } }, number>(
|
||||
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
||||
{
|
||||
specifyComparable: key => key.obj.id
|
||||
}
|
||||
);
|
||||
|
||||
const expected = [
|
||||
{ obj: { id: 1 } },
|
||||
{ obj: { id: 2 } },
|
||||
{ obj: { id: 3 } },
|
||||
{ obj: { id: 4 } },
|
||||
{ obj: { id: 5 } }
|
||||
];
|
||||
|
||||
expect(avlCounter.morris(node => node.key, 'IN')).toEqual(expected);
|
||||
expect(avlCounter.dfs(node => node.key, 'IN')).toEqual(expected);
|
||||
expect(avlCounter.dfs(node => node.key, 'IN', avlCounter.root, 'RECURSIVE')).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter not map mode count', () => {
|
||||
let avlCounter: AVLTreeCounter<number>;
|
||||
beforeEach(() => {
|
||||
avlCounter = new AVLTreeCounter<number>([], { isMapMode: false });
|
||||
});
|
||||
it('Should added isolated node count ', () => {
|
||||
avlCounter.addMany([
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3],
|
||||
[4, 4],
|
||||
[5, 5]
|
||||
]);
|
||||
const newNode = new AVLTreeCounterNode(3, undefined, 10);
|
||||
avlCounter.add(newNode, 33);
|
||||
expect(avlCounter.count).toBe(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter not map mode operations test1', () => {
|
||||
it('should perform various operations on a AVLTreeCounter with numeric values1', () => {
|
||||
const avlCounter = new AVLTreeCounter<number>([], { isMapMode: false });
|
||||
|
||||
expect(avlCounter instanceof AVLTreeCounter);
|
||||
avlCounter.add([11, 11]);
|
||||
avlCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
avlCounter.addMany(idAndValues);
|
||||
expect(avlCounter.root instanceof AVLTreeCounterNode);
|
||||
|
||||
if (avlCounter.root) expect(avlCounter.root.key == 11);
|
||||
|
||||
expect(avlCounter.size).toBe(16);
|
||||
expect(avlCounter.count).toBe(18);
|
||||
|
||||
expect(avlCounter.has(6));
|
||||
|
||||
expect(avlCounter.getHeight(6)).toBe(4);
|
||||
expect(avlCounter.getDepth(6)).toBe(0);
|
||||
const nodeId10 = avlCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = avlCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTreeCounter not map mode operations test recursively1', () => {
|
||||
it('should perform various operations on a AVLTreeCounter with numeric values1', () => {
|
||||
const avlCounter = new AVLTreeCounter<number>([], {
|
||||
iterationType: 'RECURSIVE',
|
||||
isMapMode: false
|
||||
});
|
||||
|
||||
expect(avlCounter instanceof AVLTreeCounter);
|
||||
avlCounter.add([11, 11]);
|
||||
avlCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
avlCounter.addMany(idAndValues);
|
||||
expect(avlCounter.root).toBeInstanceOf(AVLTreeCounterNode);
|
||||
|
||||
if (avlCounter.root) expect(avlCounter.root.key).toBe(6);
|
||||
|
||||
expect(avlCounter.size).toBe(16);
|
||||
expect(avlCounter.count).toBe(18);
|
||||
|
||||
expect(avlCounter.has(6));
|
||||
|
||||
expect(avlCounter.getHeight(6)).toBe(4);
|
||||
expect(avlCounter.getDepth(6)).toBe(0);
|
||||
const nodeId10 = avlCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = avlCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load diff
|
@ -3,119 +3,119 @@ import { AVLTree, AVLTreeNode, BinaryTreeNode, BSTNode } from '../../../../src';
|
|||
describe('AVL Tree Test', () => {
|
||||
it('should perform various operations on a AVL Tree', () => {
|
||||
const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
|
||||
const tree = new AVLTree<number>();
|
||||
const avlTree = new AVLTree<number>();
|
||||
|
||||
for (const i of arr) tree.add([i, i]);
|
||||
for (const i of arr) avlTree.add([i, i]);
|
||||
|
||||
tree.add(null);
|
||||
const node6 = tree.getNode(6);
|
||||
avlTree.add(null);
|
||||
const node6 = avlTree.getNode(6);
|
||||
|
||||
expect(node6 && tree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && tree.getDepth(node6)).toBe(1);
|
||||
expect(node6 && avlTree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && avlTree.getDepth(node6)).toBe(1);
|
||||
|
||||
const getNodeById = tree.getNode(10);
|
||||
const getNodeById = avlTree.getNode(10);
|
||||
expect(getNodeById?.key).toBe(10);
|
||||
|
||||
const getMinNodeByRoot = tree.getLeftMost();
|
||||
const getMinNodeByRoot = avlTree.getLeftMost();
|
||||
expect(getMinNodeByRoot).toBe(1);
|
||||
|
||||
const node15 = tree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15);
|
||||
const node15 = avlTree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15);
|
||||
expect(getMinNodeBySpecificNode?.key).toBe(12);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
expect(subTreeSum).toBe(70);
|
||||
|
||||
let lesserSum = 0;
|
||||
tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
// node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
|
||||
expect(node15?.value).toBe(undefined);
|
||||
const dfs = tree.dfs(node => node, 'IN');
|
||||
const dfs = avlTree.dfs(node => node, 'IN');
|
||||
expect(dfs[0].key).toBe(1);
|
||||
expect(dfs[dfs.length - 1].key).toBe(16);
|
||||
tree.perfectlyBalance();
|
||||
const bfs = tree.bfs(node => node);
|
||||
expect(tree.isPerfectlyBalanced()).toBe(true);
|
||||
avlTree.perfectlyBalance();
|
||||
const bfs = avlTree.bfs(node => node);
|
||||
expect(avlTree.isPerfectlyBalanced()).toBe(true);
|
||||
expect(bfs[0].key).toBe(8);
|
||||
expect(bfs[bfs.length - 1].key).toBe(16);
|
||||
|
||||
expect(tree.delete(tree.getNode(11))[0].deleted?.key).toBe(11);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(node15 && tree.getHeight(node15)).toBe(2);
|
||||
expect(avlTree.delete(avlTree.getNode(11))[0].deleted?.key).toBe(11);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(node15 && avlTree.getHeight(node15)).toBe(2);
|
||||
|
||||
expect(tree.delete(1)[0].deleted?.key).toBe(1);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(4);
|
||||
expect(avlTree.delete(1)[0].deleted?.key).toBe(1);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(4);
|
||||
|
||||
expect(tree.delete(4)[0].deleted?.key).toBe(4);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(4);
|
||||
expect(avlTree.delete(4)[0].deleted?.key).toBe(4);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(4);
|
||||
|
||||
expect(tree.delete(10)[0].deleted?.key).toBe(10);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(10)[0].deleted?.key).toBe(10);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(15)[0].deleted?.key).toBe(15);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.delete(15)[0].deleted?.key).toBe(15);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(5)[0].deleted?.key).toBe(5);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(5)[0].deleted?.key).toBe(5);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(13)[0].deleted?.key).toBe(13);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(13)[0].deleted?.key).toBe(13);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(3)[0].deleted?.key).toBe(3);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(3)[0].deleted?.key).toBe(3);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(8)[0].deleted?.key).toBe(8);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(8)[0].deleted?.key).toBe(8);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(6)[0].deleted?.key).toBe(6);
|
||||
expect(tree.delete(6).length).toBe(0);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(2);
|
||||
expect(avlTree.delete(6)[0].deleted?.key).toBe(6);
|
||||
expect(avlTree.delete(6).length).toBe(0);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(2);
|
||||
|
||||
expect(tree.delete(7)[0].deleted?.key).toBe(7);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(2);
|
||||
expect(avlTree.delete(7)[0].deleted?.key).toBe(7);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(2);
|
||||
|
||||
expect(tree.delete(9)[0].deleted?.key).toBe(9);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(2);
|
||||
expect(tree.delete(14)[0].deleted?.key).toBe(14);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(1);
|
||||
expect(avlTree.delete(9)[0].deleted?.key).toBe(9);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(2);
|
||||
expect(avlTree.delete(14)[0].deleted?.key).toBe(14);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(1);
|
||||
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
const lastBFSIds = tree.bfs();
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
const lastBFSIds = avlTree.bfs();
|
||||
expect(lastBFSIds[0]).toBe(12);
|
||||
expect(lastBFSIds[1]).toBe(2);
|
||||
expect(lastBFSIds[2]).toBe(16);
|
||||
|
||||
const lastBFSNodes = tree.bfs(node => node);
|
||||
const lastBFSNodes = avlTree.bfs(node => node);
|
||||
expect(lastBFSNodes[0].key).toBe(12);
|
||||
expect(lastBFSNodes[1].key).toBe(2);
|
||||
expect(lastBFSNodes[2].key).toBe(16);
|
||||
});
|
||||
|
||||
it('should replace value', () => {
|
||||
const tree = new AVLTree<number, string>([4, 5, [1, '1'], 2, 3], { isMapMode: false });
|
||||
expect(tree.get(1)).toBe('1');
|
||||
expect(tree.getNode(1)?.value).toBe('1');
|
||||
tree.add(1, 'a');
|
||||
expect(tree.get(1)).toBe('a');
|
||||
tree.add([1, 'b']);
|
||||
expect(tree.getNode(1)?.value).toBe('b');
|
||||
expect(tree.get(1)).toBe('b');
|
||||
const avlTree = new AVLTree<number, string>([4, 5, [1, '1'], 2, 3], { isMapMode: false });
|
||||
expect(avlTree.get(1)).toBe('1');
|
||||
expect(avlTree.getNode(1)?.value).toBe('1');
|
||||
avlTree.add(1, 'a');
|
||||
expect(avlTree.get(1)).toBe('a');
|
||||
avlTree.add([1, 'b']);
|
||||
expect(avlTree.getNode(1)?.value).toBe('b');
|
||||
expect(avlTree.get(1)).toBe('b');
|
||||
const treeMap = new AVLTree<number>([4, 5, [1, '1'], 2, 3]);
|
||||
expect(treeMap.get(1)).toBe('1');
|
||||
expect(treeMap.getNode(1)?.value).toBe(undefined);
|
||||
|
@ -130,106 +130,106 @@ describe('AVL Tree Test', () => {
|
|||
describe('AVL Tree Test recursively', () => {
|
||||
it('should perform various operations on a AVL Tree', () => {
|
||||
const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
|
||||
const tree = new AVLTree<number>([], { iterationType: 'RECURSIVE' });
|
||||
const avlTree = new AVLTree<number>([], { iterationType: 'RECURSIVE' });
|
||||
|
||||
for (const i of arr) tree.add([i, i]);
|
||||
for (const i of arr) avlTree.add([i, i]);
|
||||
|
||||
const node6 = tree.getNode(6);
|
||||
const node6 = avlTree.getNode(6);
|
||||
|
||||
expect(node6 && tree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && tree.getDepth(node6)).toBe(1);
|
||||
expect(node6 && avlTree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && avlTree.getDepth(node6)).toBe(1);
|
||||
|
||||
const getNodeById = tree.getNode(10);
|
||||
const getNodeById = avlTree.getNode(10);
|
||||
expect(getNodeById?.key).toBe(10);
|
||||
|
||||
const getMinNodeByRoot = tree.getLeftMost();
|
||||
const getMinNodeByRoot = avlTree.getLeftMost();
|
||||
expect(getMinNodeByRoot).toBe(1);
|
||||
|
||||
const node15 = tree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15);
|
||||
const node15 = avlTree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15);
|
||||
expect(getMinNodeBySpecificNode?.key).toBe(12);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
expect(subTreeSum).toBe(70);
|
||||
|
||||
let lesserSum = 0;
|
||||
tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
// node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
|
||||
expect(node15?.value).toBe(undefined);
|
||||
|
||||
const dfs = tree.dfs(node => node, 'IN');
|
||||
const dfs = avlTree.dfs(node => node, 'IN');
|
||||
expect(dfs[0].key).toBe(1);
|
||||
expect(dfs[dfs.length - 1].key).toBe(16);
|
||||
|
||||
tree.perfectlyBalance();
|
||||
const bfs = tree.bfs(node => node);
|
||||
expect(tree.isPerfectlyBalanced()).toBe(true);
|
||||
avlTree.perfectlyBalance();
|
||||
const bfs = avlTree.bfs(node => node);
|
||||
expect(avlTree.isPerfectlyBalanced()).toBe(true);
|
||||
expect(bfs[0].key).toBe(8);
|
||||
expect(bfs[bfs.length - 1].key).toBe(16);
|
||||
|
||||
expect(tree.delete(11)[0].deleted?.key).toBe(11);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(node15 && tree.getHeight(node15)).toBe(2);
|
||||
expect(avlTree.delete(11)[0].deleted?.key).toBe(11);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(node15 && avlTree.getHeight(node15)).toBe(2);
|
||||
|
||||
expect(tree.delete(1)[0].deleted?.key).toBe(1);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(4);
|
||||
expect(avlTree.delete(1)[0].deleted?.key).toBe(1);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(4);
|
||||
|
||||
expect(tree.delete(4)[0].deleted?.key).toBe(4);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(4);
|
||||
expect(avlTree.delete(4)[0].deleted?.key).toBe(4);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(4);
|
||||
|
||||
expect(tree.delete(10)[0].deleted?.key).toBe(10);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(10)[0].deleted?.key).toBe(10);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(15)[0].deleted?.key).toBe(15);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.delete(15)[0].deleted?.key).toBe(15);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(5)[0].deleted?.key).toBe(5);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(5)[0].deleted?.key).toBe(5);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(13)[0].deleted?.key).toBe(13);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(13)[0].deleted?.key).toBe(13);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(3)[0].deleted?.key).toBe(3);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(3)[0].deleted?.key).toBe(3);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(8)[0].deleted?.key).toBe(8);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(3);
|
||||
expect(avlTree.delete(8)[0].deleted?.key).toBe(8);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(3);
|
||||
|
||||
expect(tree.delete(6)[0].deleted?.key).toBe(6);
|
||||
expect(tree.delete(6).length).toBe(0);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(2);
|
||||
expect(avlTree.delete(6)[0].deleted?.key).toBe(6);
|
||||
expect(avlTree.delete(6).length).toBe(0);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(2);
|
||||
|
||||
expect(tree.delete(7)[0].deleted?.key).toBe(7);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(2);
|
||||
expect(avlTree.delete(7)[0].deleted?.key).toBe(7);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(2);
|
||||
|
||||
expect(tree.delete(9)[0].deleted?.key).toBe(9);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(2);
|
||||
expect(tree.delete(14)[0].deleted?.key).toBe(14);
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
expect(tree.getHeight()).toBe(1);
|
||||
expect(avlTree.delete(9)[0].deleted?.key).toBe(9);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(2);
|
||||
expect(avlTree.delete(14)[0].deleted?.key).toBe(14);
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
expect(avlTree.getHeight()).toBe(1);
|
||||
|
||||
expect(tree.isAVLBalanced()).toBe(true);
|
||||
const lastBFSIds = tree.bfs();
|
||||
expect(avlTree.isAVLBalanced()).toBe(true);
|
||||
const lastBFSIds = avlTree.bfs();
|
||||
expect(lastBFSIds[0]).toBe(12);
|
||||
expect(lastBFSIds[1]).toBe(2);
|
||||
expect(lastBFSIds[2]).toBe(16);
|
||||
|
||||
const lastBFSNodes = tree.bfs(node => node);
|
||||
const lastBFSNodes = avlTree.bfs(node => node);
|
||||
expect(lastBFSNodes[0].key).toBe(12);
|
||||
expect(lastBFSNodes[1].key).toBe(2);
|
||||
expect(lastBFSNodes[2].key).toBe(16);
|
||||
|
@ -237,66 +237,66 @@ describe('AVL Tree Test recursively', () => {
|
|||
});
|
||||
|
||||
describe('AVLTree APIs test', () => {
|
||||
const avl = new AVLTree<number, { id: number; text: string }>();
|
||||
const avlTree = new AVLTree<number, { id: number; text: string }>();
|
||||
beforeEach(() => {
|
||||
avl.clear();
|
||||
avlTree.clear();
|
||||
});
|
||||
|
||||
it('add', () => {
|
||||
avl.add(1);
|
||||
avlTree.add(1);
|
||||
const node2 = new AVLTreeNode(2);
|
||||
avl.add(node2);
|
||||
avlTree.add(node2);
|
||||
const node3 = new AVLTreeNode(3, {
|
||||
id: 3,
|
||||
text: 'text3'
|
||||
});
|
||||
avl.add(node3);
|
||||
avl.add([3, { id: 3, text: 'text33' }]);
|
||||
avlTree.add(node3);
|
||||
avlTree.add([3, { id: 3, text: 'text33' }]);
|
||||
|
||||
const bfsRes = avl.bfs(node => node.key);
|
||||
const bfsRes = avlTree.bfs(node => node.key);
|
||||
expect(bfsRes[0]).toBe(2);
|
||||
});
|
||||
|
||||
it('should the clone method', () => {
|
||||
function checkTreeStructure(avl: AVLTree<string, number>) {
|
||||
expect(avl.size).toBe(4);
|
||||
expect(avl.root?.key).toBe('2');
|
||||
expect(avl.root?.left?.key).toBe('1');
|
||||
expect(avl.root?.left?.left?.key).toBe(undefined);
|
||||
expect(avl.root?.left?.right?.key).toBe(undefined);
|
||||
expect(avl.root?.right?.key).toBe('4');
|
||||
expect(avl.root?.right?.left?.key).toBe(undefined);
|
||||
expect(avl.root?.right?.right?.key).toBe('5');
|
||||
function checkTreeStructure(avlTree: AVLTree<string, number>) {
|
||||
expect(avlTree.size).toBe(4);
|
||||
expect(avlTree.root?.key).toBe('2');
|
||||
expect(avlTree.root?.left?.key).toBe('1');
|
||||
expect(avlTree.root?.left?.left?.key).toBe(undefined);
|
||||
expect(avlTree.root?.left?.right?.key).toBe(undefined);
|
||||
expect(avlTree.root?.right?.key).toBe('4');
|
||||
expect(avlTree.root?.right?.left?.key).toBe(undefined);
|
||||
expect(avlTree.root?.right?.right?.key).toBe('5');
|
||||
}
|
||||
|
||||
const avl = new AVLTree<string, number>();
|
||||
avl.addMany([
|
||||
const avlTree = new AVLTree<string, number>();
|
||||
avlTree.addMany([
|
||||
['2', 2],
|
||||
['4', 4],
|
||||
['5', 5],
|
||||
['3', 3],
|
||||
['1', 1]
|
||||
]);
|
||||
expect(avl.size).toBe(5);
|
||||
expect(avl.root?.key).toBe('2');
|
||||
expect(avl.root?.left?.key).toBe('1');
|
||||
expect(avl.root?.left?.left?.key).toBe(undefined);
|
||||
expect(avl.root?.left?.right?.key).toBe(undefined);
|
||||
expect(avl.root?.right?.key).toBe('4');
|
||||
expect(avl.root?.right?.left?.key).toBe('3');
|
||||
expect(avl.root?.right?.right?.key).toBe('5');
|
||||
avl.delete('3');
|
||||
checkTreeStructure(avl);
|
||||
const cloned = avl.clone();
|
||||
expect(avlTree.size).toBe(5);
|
||||
expect(avlTree.root?.key).toBe('2');
|
||||
expect(avlTree.root?.left?.key).toBe('1');
|
||||
expect(avlTree.root?.left?.left?.key).toBe(undefined);
|
||||
expect(avlTree.root?.left?.right?.key).toBe(undefined);
|
||||
expect(avlTree.root?.right?.key).toBe('4');
|
||||
expect(avlTree.root?.right?.left?.key).toBe('3');
|
||||
expect(avlTree.root?.right?.right?.key).toBe('5');
|
||||
avlTree.delete('3');
|
||||
checkTreeStructure(avlTree);
|
||||
const cloned = avlTree.clone();
|
||||
checkTreeStructure(cloned);
|
||||
cloned.delete('1');
|
||||
expect(avl.size).toBe(4);
|
||||
expect(avlTree.size).toBe(4);
|
||||
expect(cloned.size).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTree', () => {
|
||||
it('should balance the tree using _balanceLR when nodes are added', () => {
|
||||
it('should balance the avlTree using _balanceLR when nodes are added', () => {
|
||||
const avlTree = new AVLTree();
|
||||
avlTree.add([10, 'A']);
|
||||
avlTree.add([5, 'B']);
|
||||
|
@ -307,30 +307,30 @@ describe('AVLTree', () => {
|
|||
// Adding nodes to trigger _balanceLR
|
||||
avlTree.add([12, 'F']);
|
||||
|
||||
// You can add more specific assertions to check the tree's balance and structure.
|
||||
// You can add more specific assertions to check the avlTree's balance and structure.
|
||||
});
|
||||
|
||||
it('should addMany undefined and null', () => {
|
||||
const avl = new AVLTree<number, string>();
|
||||
const addManyWithUndefined = avl.addMany([1, undefined, 3]);
|
||||
const avlTree = new AVLTree<number, string>();
|
||||
const addManyWithUndefined = avlTree.addMany([1, undefined, 3]);
|
||||
expect(addManyWithUndefined).toEqual([true, false, true]);
|
||||
expect(avl.get(undefined)).toBe(undefined);
|
||||
const addManyWithNull = avl.addMany([1, null, 3, 4]);
|
||||
expect(avlTree.get(undefined)).toBe(undefined);
|
||||
const addManyWithNull = avlTree.addMany([1, null, 3, 4]);
|
||||
expect(addManyWithNull).toEqual([true, false, true, true]);
|
||||
const addManyEntriesWithNull = avl.addMany([
|
||||
const addManyEntriesWithNull = avlTree.addMany([
|
||||
[1, '1'],
|
||||
[null, 'null'],
|
||||
[3, '3'],
|
||||
[4, '4']
|
||||
]);
|
||||
expect(addManyEntriesWithNull).toEqual([true, false, true, true]);
|
||||
expect(avl.get(null)).toBe(undefined);
|
||||
const node0 = avl.add(0, '0');
|
||||
expect(avlTree.get(null)).toBe(undefined);
|
||||
const node0 = avlTree.add(0, '0');
|
||||
expect(node0).toBe(true);
|
||||
expect(avl.get(0)).toBe('0');
|
||||
expect(avlTree.get(0)).toBe('0');
|
||||
});
|
||||
|
||||
it('should balance the tree using _balanceLR when nodes are deleted', () => {
|
||||
it('should balance the avlTree using _balanceLR when nodes are deleted', () => {
|
||||
const avlTree = new AVLTree();
|
||||
avlTree.add([10, 'A']);
|
||||
avlTree.add([5, 'B']);
|
||||
|
@ -342,43 +342,43 @@ describe('AVLTree', () => {
|
|||
// Deleting nodes to trigger _balanceLR
|
||||
avlTree.delete(3);
|
||||
|
||||
// You can add more specific assertions to check the tree's balance and structure.
|
||||
// You can add more specific assertions to check the avlTree's balance and structure.
|
||||
});
|
||||
|
||||
describe('BinaryTree APIs test', () => {
|
||||
const avl = new AVLTree<number, { id: number; text: string }>();
|
||||
describe('AVLTree APIs test', () => {
|
||||
const avlTree = new AVLTree<number, { id: number; text: string }>();
|
||||
beforeEach(() => {
|
||||
avl.clear();
|
||||
avlTree.clear();
|
||||
});
|
||||
|
||||
it('add', () => {
|
||||
avl.add(1);
|
||||
avlTree.add(1);
|
||||
const node2 = new AVLTreeNode(2);
|
||||
avl.add(node2);
|
||||
avlTree.add(node2);
|
||||
const node3 = new AVLTreeNode(3, {
|
||||
id: 3,
|
||||
text: 'text3'
|
||||
});
|
||||
avl.add(node3);
|
||||
avl.add([3, { id: 3, text: 'text33' }]);
|
||||
avlTree.add(node3);
|
||||
avlTree.add([3, { id: 3, text: 'text33' }]);
|
||||
|
||||
const bfsRes = avl.bfs(node => node);
|
||||
const bfsRes = avlTree.bfs(node => node);
|
||||
expect(bfsRes[0]?.key).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTree iterative methods test', () => {
|
||||
let avl: AVLTree<number, string>;
|
||||
let avlTree: AVLTree<number, string>;
|
||||
beforeEach(() => {
|
||||
avl = new AVLTree();
|
||||
avl.add([1, 'a']);
|
||||
avl.add([2, 'b']);
|
||||
avl.add([3, 'c']);
|
||||
avlTree = new AVLTree();
|
||||
avlTree.add([1, 'a']);
|
||||
avlTree.add([2, 'b']);
|
||||
avlTree.add([3, 'c']);
|
||||
});
|
||||
|
||||
it('The node obtained by get Node should match the node type', () => {
|
||||
const node3 = avl.getNode(3);
|
||||
const node3 = avlTree.getNode(3);
|
||||
expect(node3).toBeInstanceOf(BinaryTreeNode);
|
||||
expect(node3).toBeInstanceOf(BSTNode);
|
||||
expect(node3).toBeInstanceOf(AVLTreeNode);
|
||||
|
@ -386,7 +386,7 @@ describe('AVLTree iterative methods test', () => {
|
|||
|
||||
it('forEach should iterate over all elements', () => {
|
||||
const mockCallback = jest.fn();
|
||||
avl.forEach((key, value) => {
|
||||
avlTree.forEach((key, value) => {
|
||||
mockCallback(key, value);
|
||||
});
|
||||
|
||||
|
@ -396,8 +396,8 @@ describe('AVLTree iterative methods test', () => {
|
|||
expect(mockCallback.mock.calls[2]).toEqual([3, 'c']);
|
||||
});
|
||||
|
||||
it('filter should return a new tree with filtered elements', () => {
|
||||
const filteredTree = avl.filter(key => key > 1);
|
||||
it('filter should return a new avlTree with filtered elements', () => {
|
||||
const filteredTree = avlTree.filter(key => key > 1);
|
||||
expect(filteredTree.size).toBe(2);
|
||||
expect([...filteredTree]).toEqual([
|
||||
[2, 'b'],
|
||||
|
@ -405,8 +405,8 @@ describe('AVLTree iterative methods test', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('map should return a new tree with modified elements', () => {
|
||||
const mappedTree = avl.map((key, value) => [(key * 2).toString(), value]);
|
||||
it('map should return a new avlTree with modified elements', () => {
|
||||
const mappedTree = avlTree.map((key, value) => [(key * 2).toString(), value]);
|
||||
expect(mappedTree.size).toBe(3);
|
||||
expect([...mappedTree]).toEqual([
|
||||
['2', 'a'],
|
||||
|
@ -416,13 +416,13 @@ describe('AVLTree iterative methods test', () => {
|
|||
});
|
||||
|
||||
it('reduce should accumulate values', () => {
|
||||
const sum = avl.reduce((acc, value, key) => acc + key, 0);
|
||||
const sum = avlTree.reduce((acc, value, key) => acc + key, 0);
|
||||
expect(sum).toBe(6);
|
||||
});
|
||||
|
||||
it('[Symbol.iterator] should provide an iterator', () => {
|
||||
const entries = [];
|
||||
for (const entry of avl) {
|
||||
for (const entry of avlTree) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
|
@ -435,23 +435,23 @@ describe('AVLTree iterative methods test', () => {
|
|||
});
|
||||
|
||||
it('should clone work well', () => {
|
||||
const cloned = avl.clone();
|
||||
const cloned = avlTree.clone();
|
||||
expect(cloned.root?.left?.key).toBe(1);
|
||||
expect(cloned.root?.right?.value).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should keys', () => {
|
||||
const keys = avl.keys();
|
||||
const keys = avlTree.keys();
|
||||
expect([...keys]).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should values', () => {
|
||||
const values = avl.values();
|
||||
const values = avlTree.values();
|
||||
expect([...values]).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it('should leaves', () => {
|
||||
const leaves = avl.leaves();
|
||||
const leaves = avlTree.leaves();
|
||||
expect(leaves).toEqual([1, 3]);
|
||||
});
|
||||
});
|
||||
|
@ -459,85 +459,85 @@ describe('AVLTree iterative methods test', () => {
|
|||
describe('AVL Tree not map mode', () => {
|
||||
it('should perform various operations on a AVL Tree', () => {
|
||||
const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
|
||||
const tree = new AVLTree<number>([], { isMapMode: false });
|
||||
const avlTree = new AVLTree<number>([], { isMapMode: false });
|
||||
|
||||
for (const i of arr) tree.add([i, i]);
|
||||
for (const i of arr) avlTree.add([i, i]);
|
||||
|
||||
tree.add(null);
|
||||
const node6 = tree.getNode(6);
|
||||
avlTree.add(null);
|
||||
const node6 = avlTree.getNode(6);
|
||||
|
||||
expect(node6 && tree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && tree.getDepth(node6)).toBe(1);
|
||||
expect(node6 && avlTree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && avlTree.getDepth(node6)).toBe(1);
|
||||
|
||||
const getNodeById = tree.getNode(10);
|
||||
const getNodeById = avlTree.getNode(10);
|
||||
expect(getNodeById?.key).toBe(10);
|
||||
|
||||
const getMinNodeByRoot = tree.getLeftMost();
|
||||
const getMinNodeByRoot = avlTree.getLeftMost();
|
||||
expect(getMinNodeByRoot).toBe(1);
|
||||
|
||||
const node15 = tree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15);
|
||||
const node15 = avlTree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15);
|
||||
expect(getMinNodeBySpecificNode?.key).toBe(12);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
expect(subTreeSum).toBe(70);
|
||||
|
||||
let lesserSum = 0;
|
||||
tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
// node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
|
||||
expect(tree.get(node15)).toBe(15);
|
||||
expect(avlTree.get(node15)).toBe(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVL Tree not map mode test recursively', () => {
|
||||
it('should perform various operations on a AVL Tree', () => {
|
||||
const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
|
||||
const tree = new AVLTree<number>([], { iterationType: 'RECURSIVE', isMapMode: false });
|
||||
const avlTree = new AVLTree<number>([], { iterationType: 'RECURSIVE', isMapMode: false });
|
||||
|
||||
for (const i of arr) tree.add([i, i]);
|
||||
for (const i of arr) avlTree.add([i, i]);
|
||||
|
||||
const node6 = tree.getNode(6);
|
||||
const node6 = avlTree.getNode(6);
|
||||
|
||||
expect(node6 && tree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && tree.getDepth(node6)).toBe(1);
|
||||
expect(node6 && avlTree.getHeight(node6)).toBe(3);
|
||||
expect(node6 && avlTree.getDepth(node6)).toBe(1);
|
||||
|
||||
const getNodeById = tree.getNode(10);
|
||||
const getNodeById = avlTree.getNode(10);
|
||||
expect(getNodeById?.key).toBe(10);
|
||||
|
||||
const getMinNodeByRoot = tree.getLeftMost();
|
||||
const getMinNodeByRoot = avlTree.getLeftMost();
|
||||
expect(getMinNodeByRoot).toBe(1);
|
||||
|
||||
const node15 = tree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node => node, node15);
|
||||
const node15 = avlTree.getNode(15);
|
||||
const getMinNodeBySpecificNode = node15 && avlTree.getLeftMost(node => node, node15);
|
||||
expect(getMinNodeBySpecificNode?.key).toBe(12);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) tree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
if (node15) avlTree.dfs(node => (subTreeSum += node.key), 'PRE', node15);
|
||||
expect(subTreeSum).toBe(70);
|
||||
|
||||
let lesserSum = 0;
|
||||
tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
avlTree.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
// node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
|
||||
expect(tree.get(node15)).toBe(15);
|
||||
expect(avlTree.get(node15)).toBe(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AVLTree iterative methods not map mode', () => {
|
||||
let avl: AVLTree<number, string>;
|
||||
let avlTree: AVLTree<number, string>;
|
||||
beforeEach(() => {
|
||||
avl = new AVLTree<number, string>([], { isMapMode: false });
|
||||
avl.add([1, 'a']);
|
||||
avl.add([2, 'b']);
|
||||
avl.add([3, 'c']);
|
||||
avlTree = new AVLTree<number, string>([], { isMapMode: false });
|
||||
avlTree.add([1, 'a']);
|
||||
avlTree.add([2, 'b']);
|
||||
avlTree.add([3, 'c']);
|
||||
});
|
||||
|
||||
it('should clone work well', () => {
|
||||
const cloned = avl.clone();
|
||||
const cloned = avlTree.clone();
|
||||
expect(cloned.root?.left?.key).toBe(1);
|
||||
expect(cloned.get(cloned.root?.right?.key)).toBe('c');
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1121,7 +1121,7 @@ describe('BST iterative methods test', () => {
|
|||
});
|
||||
|
||||
it('filter should return a new tree with filtered elements', () => {
|
||||
const filteredTree = bst.filter((key, value) => key > 1);
|
||||
const filteredTree = bst.filter(key => key > 1);
|
||||
expect(filteredTree.size).toBe(2);
|
||||
expect([...filteredTree]).toEqual([
|
||||
[2, 'b'],
|
||||
|
|
|
@ -150,7 +150,6 @@ describe('Overall BinaryTree Test', () => {
|
|||
tmm.add(2);
|
||||
tmm.add(5);
|
||||
tmm.add(4);
|
||||
expect(tmm.count).toBe(10);
|
||||
expect(tmm.root?.key).toBe(3);
|
||||
expect(tmm.root?.left?.key).toBe(1);
|
||||
expect(tmm.root?.left?.left?.key).toBe(NaN);
|
||||
|
@ -163,7 +162,6 @@ describe('Overall BinaryTree Test', () => {
|
|||
expect(tmm.delete(7)[0].deleted?.key).toBe(7);
|
||||
expect(tmm.has(7)).toBe(false);
|
||||
expect(tmm.size).toBe(7);
|
||||
expect(tmm.count).toBe(9);
|
||||
expect(tmm.root?.key).toBe(3);
|
||||
expect(tmm.root?.left?.key).toBe(1);
|
||||
expect(tmm.root?.right?.key).toBe(9);
|
||||
|
@ -176,7 +174,6 @@ describe('Overall BinaryTree Test', () => {
|
|||
// expect(tmm.bfs()).toEqual([6, 1, 9, 3, 2, 5, 4]);
|
||||
const clonedTMM = tmm.clone();
|
||||
expect(clonedTMM.size).toBe(7);
|
||||
expect(clonedTMM.count).toBe(9);
|
||||
expect(clonedTMM.root?.key).toBe(3);
|
||||
expect(clonedTMM.root?.left?.key).toBe(1);
|
||||
expect(clonedTMM.root?.right?.key).toBe(5);
|
||||
|
|
|
@ -185,14 +185,14 @@ describe('RedBlackTree 1', () => {
|
|||
});
|
||||
|
||||
it('should replace value', () => {
|
||||
const tree = new RedBlackTree<number, string>([4, 5, [1, '1'], 2, 3], { isMapMode: false });
|
||||
expect(tree.get(1)).toBe('1');
|
||||
expect(tree.getNode(1)?.value).toBe('1');
|
||||
tree.add(1, 'a');
|
||||
expect(tree.get(1)).toBe('a');
|
||||
tree.add([1, 'b']);
|
||||
expect(tree.getNode(1)?.value).toBe('b');
|
||||
expect(tree.get(1)).toBe('b');
|
||||
const rbTree = new RedBlackTree<number, string>([4, 5, [1, '1'], 2, 3], { isMapMode: false });
|
||||
expect(rbTree.get(1)).toBe('1');
|
||||
expect(rbTree.getNode(1)?.value).toBe('1');
|
||||
rbTree.add(1, 'a');
|
||||
expect(rbTree.get(1)).toBe('a');
|
||||
rbTree.add([1, 'b']);
|
||||
expect(rbTree.getNode(1)?.value).toBe('b');
|
||||
expect(rbTree.get(1)).toBe('b');
|
||||
const treeMap = new RedBlackTree<number>([4, 5, [1, '1'], 2, 3]);
|
||||
expect(treeMap.get(1)).toBe('1');
|
||||
expect(treeMap.getNode(1)?.value).toBe(undefined);
|
||||
|
@ -668,9 +668,9 @@ describe('RedBlackTree 2', () => {
|
|||
});
|
||||
|
||||
it('map should return a new rbTree with modified elements', () => {
|
||||
const mappedTree = rbTree.map((key, value) => [(key * 2).toString(), value]);
|
||||
expect(mappedTree.size).toBe(3);
|
||||
expect([...mappedTree]).toEqual([
|
||||
const rbTreeMapped = rbTree.map((key, value) => [(key * 2).toString(), value]);
|
||||
expect(rbTreeMapped.size).toBe(3);
|
||||
expect([...rbTreeMapped]).toEqual([
|
||||
['2', 'a'],
|
||||
['4', 'b'],
|
||||
['6', 'c']
|
||||
|
@ -699,119 +699,116 @@ describe('RedBlackTree 2', () => {
|
|||
});
|
||||
|
||||
describe('RedBlackTree - _deleteFixup', () => {
|
||||
let tree: RedBlackTree<number, number>;
|
||||
let rbTree: RedBlackTree<number, number>;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = new RedBlackTree();
|
||||
rbTree = new RedBlackTree();
|
||||
});
|
||||
|
||||
it('should handle deleting a red leaf node', () => {
|
||||
tree.add(10, 10);
|
||||
tree.add(5, 5); // Red leaf
|
||||
tree.add(20, 20);
|
||||
rbTree.add(10, 10);
|
||||
rbTree.add(5, 5); // Red leaf
|
||||
rbTree.add(20, 20);
|
||||
|
||||
expect(tree.delete(5)).toHaveLength(1); // Delete red leaf
|
||||
expect(tree.root?.left).toBe(tree.NIL); // Left child should be NIL
|
||||
expect(rbTree.delete(5)).toHaveLength(1); // Delete red leaf
|
||||
expect(rbTree.root?.left).toBe(rbTree.NIL); // Left child should be NIL
|
||||
});
|
||||
|
||||
it('should handle deleting a black leaf node', () => {
|
||||
tree.add(10, 10);
|
||||
tree.add(5, 5); // Black node
|
||||
tree.add(20, 20);
|
||||
tree.add(1, 1); // Black leaf node
|
||||
rbTree.add(10, 10);
|
||||
rbTree.add(5, 5); // Black node
|
||||
rbTree.add(20, 20);
|
||||
rbTree.add(1, 1); // Black leaf node
|
||||
|
||||
expect(tree.delete(1)).toHaveLength(1); // Delete black leaf
|
||||
expect(tree.root?.left?.left).toBe(tree.NIL);
|
||||
expect(rbTree.delete(1)).toHaveLength(1); // Delete black leaf
|
||||
expect(rbTree.root?.left?.left).toBe(rbTree.NIL);
|
||||
});
|
||||
|
||||
it('should handle deleting black node with red sibling', () => {
|
||||
tree.add(10, 10);
|
||||
tree.add(5, 5); // Black node
|
||||
tree.add(20, 20); // Red sibling
|
||||
tree.add(25, 25); // Force the sibling to be red
|
||||
rbTree.add(10, 10);
|
||||
rbTree.add(5, 5); // Black node
|
||||
rbTree.add(20, 20); // Red sibling
|
||||
rbTree.add(25, 25); // Force the sibling to be red
|
||||
|
||||
expect(tree.delete(5)).toHaveLength(1); // Delete black node
|
||||
expect(tree.root?.right?.color).toBe('BLACK'); // Ensure sibling color is black after fixup
|
||||
expect(rbTree.delete(5)).toHaveLength(1); // Delete black node
|
||||
expect(rbTree.root?.right?.color).toBe('BLACK'); // Ensure sibling color is black after fixup
|
||||
});
|
||||
|
||||
it('should handle deleting black node with black sibling', () => {
|
||||
tree.add(10, 10);
|
||||
tree.add(5, 5); // Black node
|
||||
tree.add(20, 20); // Black sibling
|
||||
rbTree.add(10, 10);
|
||||
rbTree.add(5, 5); // Black node
|
||||
rbTree.add(20, 20); // Black sibling
|
||||
|
||||
expect(tree.delete(5)).toHaveLength(1); // Delete black node
|
||||
expect(tree.root?.left).toBe(tree.NIL);
|
||||
expect(rbTree.delete(5)).toHaveLength(1); // Delete black node
|
||||
expect(rbTree.root?.left).toBe(rbTree.NIL);
|
||||
});
|
||||
|
||||
it('should handle deleting the root node', () => {
|
||||
tree.add(10, 10); // Root node
|
||||
tree.add(5, 5);
|
||||
tree.add(20, 20);
|
||||
rbTree.add(10, 10); // Root node
|
||||
rbTree.add(5, 5);
|
||||
rbTree.add(20, 20);
|
||||
|
||||
expect(tree.delete(10)).toHaveLength(1); // Delete root node
|
||||
expect(tree.root?.key).toBe(20); // New root should be 20
|
||||
expect(rbTree.delete(10)).toHaveLength(1); // Delete root node
|
||||
expect(rbTree.root?.key).toBe(20); // New root should be 20
|
||||
});
|
||||
|
||||
it('should handle complex case with multiple rotations', () => {
|
||||
tree.add(10, 10);
|
||||
tree.add(5, 5);
|
||||
tree.add(15, 15);
|
||||
tree.add(12, 12);
|
||||
tree.add(18, 18);
|
||||
tree.add(16, 16);
|
||||
rbTree.add(10, 10);
|
||||
rbTree.add(5, 5);
|
||||
rbTree.add(15, 15);
|
||||
rbTree.add(12, 12);
|
||||
rbTree.add(18, 18);
|
||||
rbTree.add(16, 16);
|
||||
|
||||
// Delete a node that will cause rotations and color changes
|
||||
expect(tree.delete(5)).toHaveLength(1);
|
||||
expect(rbTree.delete(5)).toHaveLength(1);
|
||||
|
||||
// Verify the color and structure after fixup
|
||||
expect(tree.root?.color).toBe('BLACK');
|
||||
expect(tree.root?.left).toBe(tree.NIL);
|
||||
expect(tree.root?.right?.left?.color).toBe('BLACK');
|
||||
expect(rbTree.root?.color).toBe('BLACK');
|
||||
expect(rbTree.root?.left).toBe(rbTree.NIL);
|
||||
expect(rbTree.root?.right?.left?.color).toBe('BLACK');
|
||||
});
|
||||
|
||||
it('should handle complex delete fixup scenarios', () => {
|
||||
const tree = new RedBlackTree<number, number>();
|
||||
const rbTree = new RedBlackTree<number, number>();
|
||||
|
||||
// Build a tree that will require complex fixup
|
||||
tree.add(20, 20);
|
||||
tree.add(10, 10);
|
||||
tree.add(30, 30);
|
||||
tree.add(5, 5);
|
||||
tree.add(15, 15);
|
||||
tree.add(25, 25);
|
||||
tree.add(35, 35);
|
||||
tree.add(2, 2);
|
||||
tree.add(8, 8);
|
||||
// Build a rbTree that will require complex fixup
|
||||
rbTree.add(20, 20);
|
||||
rbTree.add(10, 10);
|
||||
rbTree.add(30, 30);
|
||||
rbTree.add(5, 5);
|
||||
rbTree.add(15, 15);
|
||||
rbTree.add(25, 25);
|
||||
rbTree.add(35, 35);
|
||||
rbTree.add(2, 2);
|
||||
rbTree.add(8, 8);
|
||||
|
||||
// This deletion should trigger a complex fixup
|
||||
tree.delete(2);
|
||||
// tree.print(tree.root, { isShowNull: true, isShowRedBlackNIL: true, isShowUndefined: false });
|
||||
rbTree.delete(2);
|
||||
// rbTree.print(rbTree.root, { isShowNull: true, isShowRedBlackNIL: true, isShowUndefined: false });
|
||||
|
||||
expect(tree.isLeaf(2)).toBe(false);
|
||||
expect(tree.isLeaf(8)).toBe(true);
|
||||
expect(tree.isLeaf(15)).toBe(true);
|
||||
expect(tree.isLeaf(25)).toBe(true);
|
||||
expect(tree.isLeaf(35)).toBe(true);
|
||||
expect(tree.isLeaf(20)).toBe(false);
|
||||
expect(tree.isLeaf(30)).toBe(false);
|
||||
// Verify tree structure and colors after fixup
|
||||
expect(tree.root?.color).toBe('BLACK');
|
||||
expect(tree.root?.key).toBe(20);
|
||||
expect(tree.root?.left?.color).toBe('RED');
|
||||
expect(tree.root?.left?.key).toBe(10);
|
||||
expect(tree.root?.right?.color).toBe('BLACK');
|
||||
expect(tree.root?.right?.key).toBe(30);
|
||||
expect(tree.root?.left?.left?.color).toBe('BLACK');
|
||||
expect(tree.root?.left?.left?.key).toBe(5);
|
||||
expect(tree.root?.left?.right?.color).toBe('BLACK');
|
||||
expect(tree.root?.left?.right?.key).toBe(15);
|
||||
expect(tree.leaves(node => (node === null ? '' : `${node.key} ${node.color}`), tree.root, 'RECURSIVE')).toEqual([
|
||||
'8 RED',
|
||||
'15 BLACK',
|
||||
'25 RED',
|
||||
'35 RED'
|
||||
]);
|
||||
expect(tree.listLevels(node => (node === tree.NIL ? 'NIL' : `${node.key} ${node.color}`))).toEqual([
|
||||
expect(rbTree.isLeaf(2)).toBe(false);
|
||||
expect(rbTree.isLeaf(8)).toBe(true);
|
||||
expect(rbTree.isLeaf(15)).toBe(true);
|
||||
expect(rbTree.isLeaf(25)).toBe(true);
|
||||
expect(rbTree.isLeaf(35)).toBe(true);
|
||||
expect(rbTree.isLeaf(20)).toBe(false);
|
||||
expect(rbTree.isLeaf(30)).toBe(false);
|
||||
// Verify rbTree structure and colors after fixup
|
||||
expect(rbTree.root?.color).toBe('BLACK');
|
||||
expect(rbTree.root?.key).toBe(20);
|
||||
expect(rbTree.root?.left?.color).toBe('RED');
|
||||
expect(rbTree.root?.left?.key).toBe(10);
|
||||
expect(rbTree.root?.right?.color).toBe('BLACK');
|
||||
expect(rbTree.root?.right?.key).toBe(30);
|
||||
expect(rbTree.root?.left?.left?.color).toBe('BLACK');
|
||||
expect(rbTree.root?.left?.left?.key).toBe(5);
|
||||
expect(rbTree.root?.left?.right?.color).toBe('BLACK');
|
||||
expect(rbTree.root?.left?.right?.key).toBe(15);
|
||||
expect(rbTree.leaves(node => (node === null ? '' : `${node.key} ${node.color}`), rbTree.root, 'RECURSIVE')).toEqual(
|
||||
['8 RED', '15 BLACK', '25 RED', '35 RED']
|
||||
);
|
||||
expect(rbTree.listLevels(node => (node === rbTree.NIL ? 'NIL' : `${node.key} ${node.color}`))).toEqual([
|
||||
['20 BLACK'],
|
||||
['10 RED', '30 BLACK'],
|
||||
['5 BLACK', '15 BLACK', '25 RED', '35 RED'],
|
||||
|
|
975
test/unit/data-structures/binary-tree/tree-counter.test.ts
Normal file
975
test/unit/data-structures/binary-tree/tree-counter.test.ts
Normal file
|
@ -0,0 +1,975 @@
|
|||
import { BinaryTreeNode, BSTNode, RedBlackTreeNode, TreeCounter, TreeCounterNode } from '../../../../src';
|
||||
import { isDebugTest } from '../../../config';
|
||||
import { getRandomInt } from '../../../utils';
|
||||
|
||||
const isDebug = isDebugTest;
|
||||
// const isDebug = true;
|
||||
|
||||
describe('TreeCounter count', () => {
|
||||
let treeCounter: TreeCounter<number>;
|
||||
beforeEach(() => {
|
||||
treeCounter = new TreeCounter<number>();
|
||||
});
|
||||
|
||||
it('Should added node count ', () => {
|
||||
treeCounter.addMany([
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3],
|
||||
[4, 4],
|
||||
[5, 5]
|
||||
]);
|
||||
const newNode = new TreeCounterNode(3, 33, 10);
|
||||
treeCounter.add(newNode);
|
||||
expect(treeCounter.count).toBe(15);
|
||||
expect(treeCounter.getComputedCount()).toBe(15);
|
||||
expect(treeCounter.getNode(3)?.count).toBe(11);
|
||||
});
|
||||
|
||||
it('Should count', () => {
|
||||
treeCounter.addMany([
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3]
|
||||
]);
|
||||
treeCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 1);
|
||||
expect(treeCounter.getComputedCount()).toBe(7);
|
||||
expect(treeCounter.count).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter operations test1', () => {
|
||||
it('should height ', () => {
|
||||
const treeCounter = new TreeCounter();
|
||||
expect(treeCounter.getHeight()).toBe(-1);
|
||||
expect(treeCounter.getMinHeight()).toBe(-1);
|
||||
|
||||
treeCounter.addMany([1, 6, 7, 2, 3, 4, 9, 11, 8, 5, 10, 12, 16, 14, 13, 15]);
|
||||
// treeCounter.print()
|
||||
expect(treeCounter.getHeight()).toBe(5);
|
||||
expect(treeCounter.getMinHeight()).toBe(2);
|
||||
});
|
||||
|
||||
it('should size and count', () => {
|
||||
const treeCounter = new TreeCounter();
|
||||
|
||||
expect(treeCounter instanceof TreeCounter);
|
||||
|
||||
treeCounter.add([11, 11]);
|
||||
treeCounter.add([3, 3]);
|
||||
expect(treeCounter.count).toBe(2);
|
||||
expect(treeCounter.getComputedCount()).toBe(2);
|
||||
expect(treeCounter.size).toBe(2);
|
||||
|
||||
const keyValuePairs: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
|
||||
treeCounter.addMany(keyValuePairs);
|
||||
expect(treeCounter.size).toBe(16);
|
||||
expect(treeCounter.count).toBe(18);
|
||||
expect(treeCounter.getComputedCount()).toBe(18);
|
||||
treeCounter.delete(11);
|
||||
expect(treeCounter.count).toBe(17);
|
||||
expect(treeCounter.getComputedCount()).toBe(17);
|
||||
treeCounter.delete(3, true);
|
||||
expect(treeCounter.count).toBe(15);
|
||||
expect(treeCounter.getComputedCount()).toBe(15);
|
||||
});
|
||||
|
||||
it('should perform various operations on a TreeCounter with numeric values1', () => {
|
||||
const treeCounter = new TreeCounter<number, number>();
|
||||
|
||||
expect(treeCounter instanceof TreeCounter);
|
||||
|
||||
treeCounter.add([11, 11]);
|
||||
treeCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
treeCounter.addMany(idAndValues);
|
||||
expect(treeCounter.root instanceof TreeCounterNode);
|
||||
|
||||
if (treeCounter.root) expect(treeCounter.root.key == 11);
|
||||
|
||||
expect(treeCounter.size).toBe(16);
|
||||
expect(treeCounter.count).toBe(18);
|
||||
expect(treeCounter.getComputedCount()).toBe(18);
|
||||
|
||||
expect(treeCounter.has(6));
|
||||
if (isDebug) treeCounter.print();
|
||||
expect(treeCounter.getHeight(6)).toBe(1);
|
||||
expect(treeCounter.getDepth(6)).toBe(3);
|
||||
const nodeId10 = treeCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = treeCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
|
||||
const nodesByCount1 = treeCounter.getNodes(node => node.count === 1);
|
||||
expect(nodesByCount1.length).toBe(14);
|
||||
|
||||
const nodesByCount2 = treeCounter.getNodes(node => node.count === 2);
|
||||
expect(nodesByCount2.length).toBe(2);
|
||||
const leftMost = treeCounter.getLeftMost();
|
||||
expect(leftMost).toBe(1);
|
||||
|
||||
const node15 = treeCounter.getNode(15);
|
||||
const minNodeBySpecificNode = node15 && treeCounter.getLeftMost(node => node, node15);
|
||||
expect(minNodeBySpecificNode?.key).toBe(14);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15);
|
||||
expect(subTreeSum).toBe(45);
|
||||
let lesserSum = 0;
|
||||
treeCounter.lesserOrGreaterTraverse(node => (lesserSum += node.key), -1, 10);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
expect(node15 instanceof TreeCounterNode);
|
||||
if (node15 instanceof TreeCounterNode) {
|
||||
const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', 15);
|
||||
expect(subTreeAdd);
|
||||
}
|
||||
const node11 = treeCounter.getNode(11);
|
||||
expect(node11 instanceof TreeCounterNode);
|
||||
if (node11 instanceof TreeCounterNode) {
|
||||
const allGreaterNodesAdded = treeCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11);
|
||||
expect(allGreaterNodesAdded);
|
||||
}
|
||||
|
||||
const dfsInorderNodes = treeCounter.dfs(node => node, 'IN');
|
||||
expect(dfsInorderNodes[0].key).toBe(1);
|
||||
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
|
||||
expect(treeCounter.isPerfectlyBalanced()).toBe(false);
|
||||
treeCounter.perfectlyBalance();
|
||||
expect(treeCounter.isPerfectlyBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
const bfsNodesAfterBalanced = treeCounter.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(6);
|
||||
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
|
||||
|
||||
const removed11 = treeCounter.delete(11, true);
|
||||
expect(removed11 instanceof Array);
|
||||
expect(removed11[0]);
|
||||
expect(removed11[0].deleted);
|
||||
|
||||
if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight(15)).toBe(1);
|
||||
|
||||
const removed1 = treeCounter.delete(1, true);
|
||||
expect(removed1 instanceof Array);
|
||||
expect(removed1[0]);
|
||||
expect(removed1[0].deleted);
|
||||
if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight()).toBe(5);
|
||||
|
||||
const removed4 = treeCounter.delete(4, true);
|
||||
expect(removed4 instanceof Array);
|
||||
expect(removed4[0]);
|
||||
expect(removed4[0].deleted);
|
||||
if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(5);
|
||||
|
||||
const removed10 = treeCounter.delete(10, true);
|
||||
expect(removed10 instanceof Array);
|
||||
expect(removed10[0]);
|
||||
expect(removed10[0].deleted);
|
||||
if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight()).toBe(4);
|
||||
|
||||
const removed15 = treeCounter.delete(15, true);
|
||||
expect(removed15 instanceof Array);
|
||||
expect(removed15[0]);
|
||||
expect(removed15[0].deleted);
|
||||
if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed5 = treeCounter.delete(5, true);
|
||||
expect(removed5 instanceof Array);
|
||||
expect(removed5[0]);
|
||||
expect(removed5[0].deleted);
|
||||
if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed13 = treeCounter.delete(13, true);
|
||||
expect(removed13 instanceof Array);
|
||||
expect(removed13[0]);
|
||||
expect(removed13[0].deleted);
|
||||
if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed3 = treeCounter.delete(3, true);
|
||||
expect(removed3 instanceof Array);
|
||||
expect(removed3[0]);
|
||||
expect(removed3[0].deleted);
|
||||
if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed8 = treeCounter.delete(8, true);
|
||||
expect(removed8 instanceof Array);
|
||||
expect(removed8[0]);
|
||||
expect(removed8[0].deleted);
|
||||
if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed6 = treeCounter.delete(6, true);
|
||||
expect(removed6 instanceof Array);
|
||||
expect(removed6[0]);
|
||||
expect(removed6[0].deleted);
|
||||
if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6);
|
||||
expect(treeCounter.delete(6, true).length).toBe(0);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed7 = treeCounter.delete(7, true);
|
||||
expect(removed7 instanceof Array);
|
||||
expect(removed7[0]);
|
||||
expect(removed7[0].deleted);
|
||||
if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed9 = treeCounter.delete(9, true);
|
||||
expect(removed9 instanceof Array);
|
||||
expect(removed9[0]);
|
||||
expect(removed9[0].deleted);
|
||||
if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed14 = treeCounter.delete(14, true);
|
||||
expect(removed14 instanceof Array);
|
||||
expect(removed14[0]);
|
||||
expect(removed14[0].deleted);
|
||||
if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(1);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsIDs = treeCounter.bfs(node => node.key);
|
||||
|
||||
expect(bfsIDs[0]).toBe(12);
|
||||
expect(bfsIDs[1]).toBe(2);
|
||||
expect(bfsIDs[2]).toBe(16);
|
||||
|
||||
const bfsNodes = treeCounter.bfs(node => node);
|
||||
|
||||
expect(bfsNodes[0].key).toBe(12);
|
||||
expect(bfsNodes[1].key).toBe(2);
|
||||
expect(bfsNodes[2].key).toBe(16);
|
||||
|
||||
expect(treeCounter.count).toBe(6);
|
||||
expect(treeCounter.getComputedCount()).toBe(8);
|
||||
});
|
||||
|
||||
it('should perform various operations on a TreeCounter with object values', () => {
|
||||
const objTreeCounter = new TreeCounter<number, { key: number; keyA: number }>();
|
||||
expect(objTreeCounter).toBeInstanceOf(TreeCounter);
|
||||
objTreeCounter.add([11, { key: 11, keyA: 11 }]);
|
||||
objTreeCounter.add([3, { key: 3, keyA: 3 }]);
|
||||
const values: [number, { key: number; keyA: number }][] = [
|
||||
[15, { key: 15, keyA: 15 }],
|
||||
[1, { key: 1, keyA: 1 }],
|
||||
[8, { key: 8, keyA: 8 }],
|
||||
[13, { key: 13, keyA: 13 }],
|
||||
[16, { key: 16, keyA: 16 }],
|
||||
[2, { key: 2, keyA: 2 }],
|
||||
[6, { key: 6, keyA: 6 }],
|
||||
[9, { key: 9, keyA: 9 }],
|
||||
[12, { key: 12, keyA: 12 }],
|
||||
[14, { key: 14, keyA: 14 }],
|
||||
[4, { key: 4, keyA: 4 }],
|
||||
[7, { key: 7, keyA: 7 }],
|
||||
[10, { key: 10, keyA: 10 }],
|
||||
[5, { key: 5, keyA: 5 }]
|
||||
];
|
||||
|
||||
objTreeCounter.addMany(values);
|
||||
|
||||
expect(objTreeCounter.root).toBeInstanceOf(TreeCounterNode);
|
||||
|
||||
if (objTreeCounter.root) expect(objTreeCounter.root.key).toBe(5);
|
||||
|
||||
expect(objTreeCounter.count).toBe(16);
|
||||
expect(objTreeCounter.getComputedCount()).toBe(16);
|
||||
|
||||
expect(objTreeCounter.has(6)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter operations test recursively1', () => {
|
||||
it('should perform various operations on a TreeCounter with numeric values1', () => {
|
||||
const treeCounter = new TreeCounter<number>([], {
|
||||
iterationType: 'RECURSIVE'
|
||||
});
|
||||
|
||||
expect(treeCounter instanceof TreeCounter);
|
||||
treeCounter.add([11, 11]);
|
||||
treeCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
treeCounter.addMany(idAndValues);
|
||||
expect(treeCounter.root).toBeInstanceOf(TreeCounterNode);
|
||||
|
||||
if (treeCounter.root) expect(treeCounter.root.key).toBe(5);
|
||||
|
||||
expect(treeCounter.size).toBe(16);
|
||||
expect(treeCounter.count).toBe(18);
|
||||
expect(treeCounter.getComputedCount()).toBe(18);
|
||||
|
||||
expect(treeCounter.has(6));
|
||||
|
||||
expect(treeCounter.getHeight(6)).toBe(1);
|
||||
expect(treeCounter.getDepth(6)).toBe(3);
|
||||
const nodeId10 = treeCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = treeCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
|
||||
const nodesByCount1 = treeCounter.getNodes(node => node.count === 1);
|
||||
expect(nodesByCount1.length).toBe(14);
|
||||
|
||||
const nodesByCount2 = treeCounter.getNodes(node => node.count === 2);
|
||||
expect(nodesByCount2.length).toBe(2);
|
||||
const leftMost = treeCounter.getLeftMost();
|
||||
expect(leftMost).toBe(1);
|
||||
|
||||
const node15 = treeCounter.getNode(15);
|
||||
const minNodeBySpecificNode = node15 && treeCounter.getLeftMost(node => node, node15);
|
||||
expect(minNodeBySpecificNode?.key).toBe(14);
|
||||
|
||||
let subTreeSum = 0;
|
||||
if (node15) treeCounter.dfs(node => (subTreeSum += node.key), 'PRE', 15);
|
||||
expect(subTreeSum).toBe(45);
|
||||
let lesserSum = 0;
|
||||
expect(treeCounter.has(9)).toBe(true);
|
||||
treeCounter.lesserOrGreaterTraverse(
|
||||
node => {
|
||||
lesserSum += node.key;
|
||||
return node.key;
|
||||
},
|
||||
-1,
|
||||
10
|
||||
);
|
||||
expect(lesserSum).toBe(45);
|
||||
|
||||
expect(node15 instanceof TreeCounterNode);
|
||||
if (node15 instanceof TreeCounterNode) {
|
||||
const subTreeAdd = treeCounter.dfs(node => (node.count += 1), 'PRE', 15);
|
||||
expect(subTreeAdd);
|
||||
}
|
||||
const node11 = treeCounter.getNode(11);
|
||||
expect(node11 instanceof TreeCounterNode);
|
||||
if (node11 instanceof TreeCounterNode) {
|
||||
const allGreaterNodesAdded = treeCounter.lesserOrGreaterTraverse(node => (node.count += 2), 1, 11);
|
||||
expect(allGreaterNodesAdded);
|
||||
}
|
||||
|
||||
const dfsInorderNodes = treeCounter.dfs(node => node, 'IN');
|
||||
expect(dfsInorderNodes[0].key).toBe(1);
|
||||
expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16);
|
||||
expect(treeCounter.isPerfectlyBalanced()).toBe(false);
|
||||
|
||||
treeCounter.perfectlyBalance();
|
||||
|
||||
expect(treeCounter.isPerfectlyBalanced()).toBe(false);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
const bfsNodesAfterBalanced = treeCounter.bfs(node => node);
|
||||
expect(bfsNodesAfterBalanced[0].key).toBe(6);
|
||||
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
|
||||
|
||||
const removed11 = treeCounter.delete(11, true);
|
||||
expect(removed11 instanceof Array);
|
||||
expect(removed11[0]);
|
||||
expect(removed11[0].deleted);
|
||||
|
||||
if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight(15)).toBe(1);
|
||||
|
||||
const removed1 = treeCounter.delete(1, true);
|
||||
expect(removed1 instanceof Array);
|
||||
expect(removed1[0]);
|
||||
expect(removed1[0].deleted);
|
||||
if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight()).toBe(5);
|
||||
|
||||
const removed4 = treeCounter.delete(4, true);
|
||||
expect(removed4 instanceof Array);
|
||||
expect(removed4[0]);
|
||||
expect(removed4[0].deleted);
|
||||
if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(5);
|
||||
|
||||
const removed10 = treeCounter.delete(10, true);
|
||||
expect(removed10 instanceof Array);
|
||||
expect(removed10[0]);
|
||||
expect(removed10[0].deleted);
|
||||
if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight()).toBe(4);
|
||||
|
||||
const removed15 = treeCounter.delete(15, true);
|
||||
expect(removed15 instanceof Array);
|
||||
expect(removed15[0]);
|
||||
expect(removed15[0].deleted);
|
||||
if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed5 = treeCounter.delete(5, true);
|
||||
expect(removed5 instanceof Array);
|
||||
expect(removed5[0]);
|
||||
expect(removed5[0].deleted);
|
||||
if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed13 = treeCounter.delete(13, true);
|
||||
expect(removed13 instanceof Array);
|
||||
expect(removed13[0]);
|
||||
expect(removed13[0].deleted);
|
||||
if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed3 = treeCounter.delete(3, true);
|
||||
expect(removed3 instanceof Array);
|
||||
expect(removed3[0]);
|
||||
expect(removed3[0].deleted);
|
||||
if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed8 = treeCounter.delete(8, true);
|
||||
expect(removed8 instanceof Array);
|
||||
expect(removed8[0]);
|
||||
expect(removed8[0].deleted);
|
||||
if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed6 = treeCounter.delete(6, true);
|
||||
expect(removed6 instanceof Array);
|
||||
expect(removed6[0]);
|
||||
expect(removed6[0].deleted);
|
||||
if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6);
|
||||
expect(treeCounter.delete(6, true).length).toBe(0);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed7 = treeCounter.delete(7, true);
|
||||
expect(removed7 instanceof Array);
|
||||
expect(removed7[0]);
|
||||
expect(removed7[0].deleted);
|
||||
if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(false);
|
||||
expect(treeCounter.getHeight()).toBe(3);
|
||||
|
||||
const removed9 = treeCounter.delete(9, true);
|
||||
expect(removed9 instanceof Array);
|
||||
expect(removed9[0]);
|
||||
expect(removed9[0].deleted);
|
||||
if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(2);
|
||||
|
||||
const removed14 = treeCounter.delete(14, true);
|
||||
expect(removed14 instanceof Array);
|
||||
expect(removed14[0]);
|
||||
expect(removed14[0].deleted);
|
||||
if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14);
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
expect(treeCounter.getHeight()).toBe(1);
|
||||
|
||||
expect(treeCounter.isAVLBalanced()).toBe(true);
|
||||
|
||||
const bfsIDs = treeCounter.bfs(node => node.key);
|
||||
|
||||
expect(bfsIDs[0]).toBe(12);
|
||||
expect(bfsIDs[1]).toBe(2);
|
||||
expect(bfsIDs[2]).toBe(16);
|
||||
|
||||
const bfsNodes = treeCounter.bfs(node => node);
|
||||
|
||||
expect(bfsNodes[0].key).toBe(12);
|
||||
expect(bfsNodes[1].key).toBe(2);
|
||||
expect(bfsNodes[2].key).toBe(16);
|
||||
|
||||
expect(treeCounter.count).toBe(6);
|
||||
expect(treeCounter.getComputedCount()).toBe(8);
|
||||
});
|
||||
|
||||
it('should perform various operations on a TreeCounter with object values', () => {
|
||||
const objTreeCounter = new TreeCounter<number, { key: number; keyA: number }>();
|
||||
expect(objTreeCounter).toBeInstanceOf(TreeCounter);
|
||||
objTreeCounter.add([11, { key: 11, keyA: 11 }]);
|
||||
objTreeCounter.add([3, { key: 3, keyA: 3 }]);
|
||||
const values: [number, { key: number; keyA: number }][] = [
|
||||
[15, { key: 15, keyA: 15 }],
|
||||
[1, { key: 1, keyA: 1 }],
|
||||
[8, { key: 8, keyA: 8 }],
|
||||
[13, { key: 13, keyA: 13 }],
|
||||
[16, { key: 16, keyA: 16 }],
|
||||
[2, { key: 2, keyA: 2 }],
|
||||
[6, { key: 6, keyA: 6 }],
|
||||
[9, { key: 9, keyA: 9 }],
|
||||
[12, { key: 12, keyA: 12 }],
|
||||
[14, { key: 14, keyA: 14 }],
|
||||
[4, { key: 4, keyA: 4 }],
|
||||
[7, { key: 7, keyA: 7 }],
|
||||
[10, { key: 10, keyA: 10 }],
|
||||
[5, { key: 5, keyA: 5 }]
|
||||
];
|
||||
|
||||
objTreeCounter.addMany(values);
|
||||
|
||||
expect(objTreeCounter.root).toBeInstanceOf(TreeCounterNode);
|
||||
|
||||
if (objTreeCounter.root) expect(objTreeCounter.root.key).toBe(5);
|
||||
|
||||
expect(objTreeCounter.count).toBe(16);
|
||||
expect(objTreeCounter.getComputedCount()).toBe(16);
|
||||
|
||||
expect(objTreeCounter.has(6)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter delete test', function () {
|
||||
const treeCounter = new TreeCounter<number, number>();
|
||||
const inputSize = 1000; // Adjust input sizes as needed
|
||||
|
||||
beforeEach(() => {
|
||||
treeCounter.clear();
|
||||
});
|
||||
|
||||
it(`Observe the time consumption of TreeCounter.dfs be good`, function () {
|
||||
const startDFS = performance.now();
|
||||
const dfs = treeCounter.dfs(node => node);
|
||||
if (isDebug) console.log('---bfs', performance.now() - startDFS, dfs.length);
|
||||
});
|
||||
|
||||
it('The structure remains normal after random deletion', function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
treeCounter.add(i);
|
||||
}
|
||||
|
||||
expect(treeCounter.size).toBe(inputSize);
|
||||
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
treeCounter.delete(num);
|
||||
}
|
||||
|
||||
let nilCount = 0;
|
||||
const dfs = (cur: TreeCounterNode<number>) => {
|
||||
if (isNaN(cur.key)) nilCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (treeCounter.root) dfs(treeCounter.root);
|
||||
|
||||
expect(treeCounter.size).toBeLessThanOrEqual(inputSize);
|
||||
expect(treeCounter.getHeight()).toBeGreaterThan(Math.log2(inputSize) - 1);
|
||||
expect(treeCounter.getHeight()).toBeLessThan(Math.log2(inputSize) * 2);
|
||||
|
||||
expect(nilCount).toBe(treeCounter.size + 1);
|
||||
});
|
||||
|
||||
it(`Random additions, complete deletions of structures are normal`, function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
if (i === 0 && isDebug) console.log(`first:`, num);
|
||||
treeCounter.add(num);
|
||||
}
|
||||
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
treeCounter.delete(i, true);
|
||||
}
|
||||
|
||||
let nilCount = 0;
|
||||
const dfs = (cur: TreeCounterNode<number>) => {
|
||||
if (isNaN(cur.key)) nilCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (treeCounter.root) dfs(treeCounter.root);
|
||||
|
||||
expect(treeCounter.size).toBe(0);
|
||||
expect(treeCounter.getHeight()).toBe(-1);
|
||||
expect(nilCount).toBe(treeCounter.size + 1);
|
||||
|
||||
if (isDebug) treeCounter.print();
|
||||
});
|
||||
|
||||
it(`Random additions, count deletions of structures are normal`, function () {
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
const num = getRandomInt(0, inputSize - 1);
|
||||
if (i === 0 && isDebug) console.log(`first:`, num);
|
||||
treeCounter.add(num);
|
||||
}
|
||||
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
treeCounter.delete(i);
|
||||
}
|
||||
|
||||
let nanCount = 0;
|
||||
const dfs = (cur: TreeCounterNode<number>) => {
|
||||
if (isNaN(cur.key)) nanCount++;
|
||||
if (cur.left) dfs(cur.left);
|
||||
if (cur.right) dfs(cur.right);
|
||||
};
|
||||
if (treeCounter.root) dfs(treeCounter.root);
|
||||
|
||||
expect(treeCounter.size).toBeGreaterThanOrEqual(0);
|
||||
expect(treeCounter.getHeight()).toBeGreaterThanOrEqual(0);
|
||||
expect(nanCount).toBeLessThanOrEqual(inputSize);
|
||||
|
||||
if (isDebug) treeCounter.print();
|
||||
});
|
||||
|
||||
it('should the clone method', () => {
|
||||
function checkTreeStructure(treeCounter: TreeCounter<string, number>) {
|
||||
expect(treeCounter.size).toBe(4);
|
||||
expect(treeCounter.root?.key).toBe('2');
|
||||
expect(treeCounter.root?.left?.key).toBe('1');
|
||||
expect(treeCounter.root?.left?.left?.key).toBe(NaN);
|
||||
expect(treeCounter.root?.left?.right?.key).toBe(NaN);
|
||||
expect(treeCounter.root?.right?.key).toBe('4');
|
||||
expect(treeCounter.root?.right?.left?.key).toBe(NaN);
|
||||
expect(treeCounter.root?.right?.right?.key).toBe('5');
|
||||
}
|
||||
|
||||
const treeCounter = new TreeCounter<string, number>();
|
||||
treeCounter.addMany([
|
||||
['2', 2],
|
||||
['4', 4],
|
||||
['5', 5],
|
||||
['3', 3],
|
||||
['1', 1]
|
||||
]);
|
||||
expect(treeCounter.size).toBe(5);
|
||||
expect(treeCounter.root?.key).toBe('2');
|
||||
expect(treeCounter.root?.left?.key).toBe('1');
|
||||
expect(treeCounter.root?.left?.left?.key).toBe(NaN);
|
||||
expect(treeCounter.root?.left?.right?.key).toBe(NaN);
|
||||
expect(treeCounter.root?.right?.key).toBe('4');
|
||||
expect(treeCounter.root?.right?.left?.key).toBe(`3`);
|
||||
expect(treeCounter.root?.right?.right?.key).toBe('5');
|
||||
treeCounter.delete('3');
|
||||
checkTreeStructure(treeCounter);
|
||||
const cloned = treeCounter.clone();
|
||||
checkTreeStructure(cloned);
|
||||
cloned.delete('1');
|
||||
expect(treeCounter.size).toBe(4);
|
||||
expect(cloned.size).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter iterative methods test', () => {
|
||||
let treeCounter: TreeCounter<number, string>;
|
||||
beforeEach(() => {
|
||||
treeCounter = new TreeCounter<number, string>();
|
||||
treeCounter.add(1, 'a', 10);
|
||||
treeCounter.add([2, 'b'], undefined, 10);
|
||||
treeCounter.add([3, 'c'], undefined, 1);
|
||||
});
|
||||
|
||||
it('The node obtained by get Node should match the node type', () => {
|
||||
const node3 = treeCounter.getNode(3);
|
||||
expect(node3).toBeInstanceOf(BinaryTreeNode);
|
||||
expect(node3).toBeInstanceOf(BSTNode);
|
||||
expect(node3).toBeInstanceOf(RedBlackTreeNode);
|
||||
});
|
||||
|
||||
it('forEach should iterate over all elements', () => {
|
||||
const mockCallback = jest.fn();
|
||||
treeCounter.forEach((key, value) => {
|
||||
mockCallback(key, value);
|
||||
});
|
||||
|
||||
expect(mockCallback.mock.calls.length).toBe(3);
|
||||
expect(mockCallback.mock.calls[0]).toEqual([1, 'a']);
|
||||
expect(mockCallback.mock.calls[1]).toEqual([2, 'b']);
|
||||
expect(mockCallback.mock.calls[2]).toEqual([3, 'c']);
|
||||
});
|
||||
|
||||
it('filter should return a new tree with filtered elements', () => {
|
||||
const filteredTree = treeCounter.filter(key => key > 1);
|
||||
expect(filteredTree.size).toBe(2);
|
||||
expect([...filteredTree]).toEqual([
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('map should return a new tree with modified elements', () => {
|
||||
const treeCounterMapped = treeCounter.map((key, value) => [(key * 2).toString(), value]);
|
||||
expect(treeCounterMapped.size).toBe(3);
|
||||
expect([...treeCounterMapped]).toEqual([
|
||||
['2', 'a'],
|
||||
['4', 'b'],
|
||||
['6', 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('reduce should accumulate values', () => {
|
||||
const sum = treeCounter.reduce((acc, value, key) => acc + key, 0);
|
||||
expect(sum).toBe(6);
|
||||
});
|
||||
|
||||
it('[Symbol.iterator] should provide an iterator', () => {
|
||||
const entries = [];
|
||||
for (const entry of treeCounter) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
expect(entries.length).toBe(3);
|
||||
expect(entries).toEqual([
|
||||
[1, 'a'],
|
||||
[2, 'b'],
|
||||
[3, 'c']
|
||||
]);
|
||||
});
|
||||
|
||||
it('should clone work well', () => {
|
||||
expect(treeCounter.count).toBe(21);
|
||||
expect(treeCounter.getComputedCount()).toBe(21);
|
||||
const cloned = treeCounter.clone();
|
||||
expect(cloned.root?.left?.key).toBe(1);
|
||||
if (cloned.isMapMode) expect(cloned.get(cloned.root?.right)).toBe('c');
|
||||
else expect(cloned.root?.right?.value).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should keys', () => {
|
||||
const keys = treeCounter.keys();
|
||||
expect([...keys]).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('should values', () => {
|
||||
const values = treeCounter.values();
|
||||
expect([...values]).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it('should leaves', () => {
|
||||
const leaves = treeCounter.leaves();
|
||||
expect(leaves).toEqual([1, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter count not map mode', () => {
|
||||
let treeCounter: TreeCounter<number>;
|
||||
beforeEach(() => {
|
||||
treeCounter = new TreeCounter<number>([], { isMapMode: false });
|
||||
});
|
||||
|
||||
it('Should added node count ', () => {
|
||||
treeCounter.addMany([
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[3, 3],
|
||||
[4, 4],
|
||||
[5, 5]
|
||||
]);
|
||||
const newNode = new TreeCounterNode(3, undefined, 10);
|
||||
treeCounter.add(newNode, 33, 20);
|
||||
// TODO expect(treeCounter.count).toBe(25);
|
||||
expect(treeCounter.count).toBe(15);
|
||||
expect(treeCounter.getComputedCount()).toBe(15);
|
||||
expect(treeCounter.getNode(3)?.count).toBe(11);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter operations test1 not map mode', () => {
|
||||
it('should perform various operations on a TreeCounter with numeric values1', () => {
|
||||
const treeCounter = new TreeCounter<number, number>([], { isMapMode: false });
|
||||
|
||||
expect(treeCounter instanceof TreeCounter);
|
||||
|
||||
treeCounter.add([11, 11]);
|
||||
treeCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
treeCounter.addMany(idAndValues);
|
||||
expect(treeCounter.root instanceof TreeCounterNode);
|
||||
|
||||
if (treeCounter.root) expect(treeCounter.root.key == 11);
|
||||
|
||||
expect(treeCounter.size).toBe(16);
|
||||
expect(treeCounter.count).toBe(18);
|
||||
expect(treeCounter.getComputedCount()).toBe(18);
|
||||
|
||||
expect(treeCounter.has(6));
|
||||
if (isDebug) treeCounter.print();
|
||||
expect(treeCounter.getHeight(6)).toBe(1);
|
||||
expect(treeCounter.getDepth(6)).toBe(3);
|
||||
const nodeId10 = treeCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = treeCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter operations test recursively1 not map mode', () => {
|
||||
it('should perform various operations on a TreeCounter with numeric values1', () => {
|
||||
const treeCounter = new TreeCounter<number>([], {
|
||||
iterationType: 'RECURSIVE',
|
||||
isMapMode: false
|
||||
});
|
||||
|
||||
expect(treeCounter instanceof TreeCounter);
|
||||
treeCounter.add([11, 11]);
|
||||
treeCounter.add([3, 3]);
|
||||
const idAndValues: [number, number][] = [
|
||||
[11, 11],
|
||||
[3, 3],
|
||||
[15, 15],
|
||||
[1, 1],
|
||||
[8, 8],
|
||||
[13, 13],
|
||||
[16, 16],
|
||||
[2, 2],
|
||||
[6, 6],
|
||||
[9, 9],
|
||||
[12, 12],
|
||||
[14, 14],
|
||||
[4, 4],
|
||||
[7, 7],
|
||||
[10, 10],
|
||||
[5, 5]
|
||||
];
|
||||
treeCounter.addMany(idAndValues);
|
||||
expect(treeCounter.root).toBeInstanceOf(TreeCounterNode);
|
||||
|
||||
if (treeCounter.root) expect(treeCounter.root.key).toBe(5);
|
||||
|
||||
expect(treeCounter.size).toBe(16);
|
||||
expect(treeCounter.count).toBe(18);
|
||||
expect(treeCounter.getComputedCount()).toBe(18);
|
||||
|
||||
expect(treeCounter.has(6));
|
||||
|
||||
expect(treeCounter.getHeight(6)).toBe(1);
|
||||
expect(treeCounter.getDepth(6)).toBe(3);
|
||||
const nodeId10 = treeCounter.getNode(10);
|
||||
expect(nodeId10?.key).toBe(10);
|
||||
|
||||
const nodeVal9 = treeCounter.getNode(node => node.key === 9);
|
||||
expect(nodeVal9?.key).toBe(9);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TreeCounter iterative methods test not map mode', () => {
|
||||
let treeCounter: TreeCounter<number, string>;
|
||||
beforeEach(() => {
|
||||
treeCounter = new TreeCounter<number, string>([], { isMapMode: false });
|
||||
treeCounter.add(1, 'a', 10);
|
||||
treeCounter.add([2, 'b'], undefined, 10);
|
||||
treeCounter.add([3, 'c'], undefined, 1);
|
||||
});
|
||||
|
||||
it('should clone work well', () => {
|
||||
expect(treeCounter.count).toBe(21);
|
||||
expect(treeCounter.getComputedCount()).toBe(21);
|
||||
const cloned = treeCounter.clone();
|
||||
expect(cloned.root?.left?.key).toBe(1);
|
||||
expect(cloned.get(cloned.root?.right)).toBe(undefined);
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load diff
|
@ -343,7 +343,7 @@ describe('HashMap', () => {
|
|||
});
|
||||
|
||||
it('filter() should remove elements that do not match the condition', () => {
|
||||
const filteredHashMap = hashMap.filter((key, value) => key !== 'key1');
|
||||
const filteredHashMap = hashMap.filter(key => key !== 'key1');
|
||||
expect(filteredHashMap.has('key1')).toBe(false);
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"extends": "./tsconfig-base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src/",
|
||||
"outDir": "dist/mjs",
|
||||
"outDir": "dist/esm",
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"moduleResolution": "Node",
|
Loading…
Reference in a new issue