feat: The addMany method in binary tree data structures supports both addMany(keys, values) and addMany(entries).

This commit is contained in:
Revone 2023-12-07 10:19:17 +08:00
parent 7ddad8eb65
commit 1c8324147f
10 changed files with 107 additions and 65 deletions

View file

@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
- [`auto-changelog`](https://github.com/CookPete/auto-changelog)
## [v1.48.4](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
## [v1.48.5](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
### Changes

View file

@ -99,7 +99,7 @@ export class AVLTree<K = any, V = any, N extends AVLTreeNode<K, V, N> = AVLTreeN
/**
* Time Complexity: O(log n) - logarithmic time, where "n" is the number of nodes in the tree. The add method of the superclass (BST) has logarithmic time complexity.
* Space Complexity: O(1) - constant space, as it doesn't use additional data structures that scale with input size.
*
*
* The function overrides the add method of a binary tree node and balances the tree after inserting
* a new node.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be either a key, a node, or an

View file

@ -235,7 +235,7 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
/**
* Time Complexity O(log n) - O(n)
* Space Complexity O(1)
*
*
* The `add` function adds a new node to a binary tree, either by creating a new node or replacing an
* existing node with the same key.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be one of the following:
@ -288,22 +288,38 @@ export class BinaryTree<K = any, V = any, N extends BinaryTreeNode<K, V, N> = Bi
* Time Complexity: O(k log n) - O(k * n)
* Space Complexity: O(1)
*
* The function `addMany` takes in an iterable of `BTNodeExemplar` objects, adds each object to the
* current instance, and returns an array of the inserted nodes.
* @param nodes - The `nodes` parameter is an iterable (such as an array or a set) of
* `BTNodeExemplar<K, V,N>` objects.
* @returns The function `addMany` returns an array of values, where each value is either of type
* `N`, `null`, or `undefined`.
* The `addMany` function takes in a collection of nodes and an optional collection of values, and
* adds each node with its corresponding value to the data structure.
* @param nodes - An iterable collection of BTNodeExemplar objects.
* @param [values] - An optional iterable of values that will be assigned to each node being added.
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
addMany(nodes: Iterable<BTNodeExemplar<K, V, N>>): (N | null | undefined)[] {
addMany(nodes: Iterable<BTNodeExemplar<K, V, N>>, values?: Iterable<V | undefined>): (N | null | undefined)[] {
// TODO not sure addMany not be run multi times
const inserted: (N | null | undefined)[] = [];
for (const kne of nodes) {
inserted.push(this.add(kne));
let valuesIterator: Iterator<V | undefined> | undefined;
if (values) {
valuesIterator = values[Symbol.iterator]();
}
for (const kne of nodes) {
let value: V | undefined | null = undefined;
if (valuesIterator) {
const valueResult = valuesIterator.next();
if (!valueResult.done) {
value = valueResult.value;
}
}
inserted.push(this.add(kne, value));
}
return inserted;
}
/**
* Time Complexity: O(k * n) "n" is the number of nodes in the tree, and "k" is the number of keys to be inserted.
* Space Complexity: O(1)

View file

@ -192,7 +192,7 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
/**
* Time Complexity: O(log n) - Average case for a balanced tree. In the worst case (unbalanced tree), it can be O(n).
* Space Complexity: O(1) - Constant space is used.
*
*
* The `add` function adds a new node to a binary tree, updating the value if the key already exists
* or inserting a new node if the key is unique.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can accept three types of values:
@ -256,31 +256,45 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
* Time Complexity: O(k log n) - Adding each element individually in a balanced tree.
* Space Complexity: O(k) - Additional space is required for the sorted array.
*
* The `addMany` function in TypeScript adds multiple nodes to a binary tree, either in a balanced or
* unbalanced manner, and returns an array of the inserted nodes.
* @param keysOrNodesOrEntries - An iterable containing keys, nodes, or entries to be added to the
* binary tree.
* @param [isBalanceAdd=true] - A boolean flag indicating whether the tree should be balanced after
* adding the nodes. The default value is true.
* The `addMany` function in TypeScript adds multiple keys or nodes to a binary tree, optionally
* balancing the tree after each addition.
* @param keysOrNodesOrEntries - An iterable containing the keys, nodes, or entries to be added to
* the binary tree.
* @param [values] - An optional iterable of values to be associated with the keys or nodes being
* added. If provided, the values will be assigned to the corresponding keys or nodes in the same
* order. If not provided, undefined will be assigned as the value for each key or node.
* @param [isBalanceAdd=true] - A boolean flag indicating whether the add operation should be
* balanced or not. If set to true, the add operation will be balanced using a binary search tree
* algorithm. If set to false, the add operation will not be balanced and the elements will be added
* in the order they appear in the input.
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies the
* type of iteration to use when adding multiple keys or nodes to the binary tree. It has a default
* value of `this.iterationType`, which means it will use the iteration type specified by the binary
* tree instance.
* @returns The `addMany` function returns an array of `N` or `undefined` values.
* type of iteration to use when adding multiple keys or nodes. It has a default value of
* `this.iterationType`, which suggests that it is a property of the current object.
* @returns The function `addMany` returns an array of nodes (`N`) or `undefined` values.
*/
override addMany(
keysOrNodesOrEntries: Iterable<BTNodeExemplar<K, V, N>>,
values?: Iterable<V | undefined>,
isBalanceAdd = true,
iterationType = this.iterationType
): (N | undefined)[] {
const inserted: (N | undefined)[] = []
const inserted: (N | undefined)[] = [];
let valuesIterator: Iterator<V | undefined> | undefined;
if (values) {
valuesIterator = values[Symbol.iterator]();
}
if (!isBalanceAdd) {
for (const kve of keysOrNodesOrEntries) {
const nn = this.add(kve)
const value = valuesIterator?.next().value;
const nn = this.add(kve, value);
inserted.push(nn);
}
return inserted;
}
const realBTNExemplars: BTNodePureExemplar<K, V, N>[] = [];
const isRealBTNExemplar = (kve: BTNodeExemplar<K, V, N>): kve is BTNodePureExemplar<K, V, N> => {
@ -292,22 +306,20 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
isRealBTNExemplar(kve) && realBTNExemplars.push(kve);
}
// TODO this addMany function is inefficient, it should be optimized
let sorted: BTNodePureExemplar<K, V, N>[] = [];
sorted = realBTNExemplars.sort((a, b) => {
let aR: number, bR: number;
if (this.isEntry(a)) aR = this.extractor(a[0])
else if (this.isRealNode(a)) aR = this.extractor(a.key)
if (this.isEntry(a)) aR = this.extractor(a[0]);
else if (this.isRealNode(a)) aR = this.extractor(a.key);
else aR = this.extractor(a);
if (this.isEntry(b)) bR = this.extractor(b[0])
else if (this.isRealNode(b)) bR = this.extractor(b.key)
if (this.isEntry(b)) bR = this.extractor(b[0]);
else if (this.isRealNode(b)) bR = this.extractor(b.key);
else bR = this.extractor(b);
return aR - bR;
})
});
const _dfs = (arr: BTNodePureExemplar<K, V, N>[]) => {
if (arr.length === 0) return;
@ -318,6 +330,7 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
_dfs(arr.slice(0, mid));
_dfs(arr.slice(mid + 1));
};
const _iterate = () => {
const n = sorted.length;
const stack: [[number, number]] = [[0, n - 1]];
@ -335,6 +348,7 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
}
}
};
if (iterationType === IterationType.RECURSIVE) {
_dfs(sorted);
} else {
@ -344,6 +358,7 @@ export class BST<K = any, V = any, N extends BSTNode<K, V, N> = BSTNode<K, V, BS
return inserted;
}
// /**
// * Time Complexity: O(n log n) - Adding each element individually in a balanced tree.
// * Space Complexity: O(n) - Additional space is required for the sorted array.

View file

@ -153,7 +153,7 @@ export class RedBlackTree<K = any, V = any, N extends RedBlackTreeNode<K, V, N>
/**
* Time Complexity: O(log n) on average (where n is the number of nodes in the tree)
* Space Complexity: O(1)
*
*
* The `add` function adds a new node to a binary search tree and performs necessary rotations and
* color changes to maintain the red-black tree properties.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be either a key, a node, or an

View file

@ -126,7 +126,7 @@ export class TreeMultimap<K = any, V = any, N extends TreeMultimapNode<K, V, N>
/**
* Time Complexity: O(log n) - logarithmic time, where "n" is the number of nodes in the tree. The add method of the superclass (AVLTree) has logarithmic time complexity.
* Space Complexity: O(1) - constant space, as it doesn't use additional data structures that scale with input size.
*
*
* The function overrides the add method of a binary tree node and adds a new node to the tree.
* @param keyOrNodeOrEntry - The `keyOrNodeOrEntry` parameter can be either a key, a node, or an
* entry. It represents the key, node, or entry that you want to add to the binary tree.

View file

@ -15,7 +15,7 @@ export interface IBinaryTree<K = number, V = any, N extends BinaryTreeNode<K, V,
add(keyOrNodeOrEntry: BTNodeExemplar<K, V, N>, value?: V, count?: number): N | null | undefined;
addMany(nodes: Iterable<BTNodeExemplar<K, V, N>>): (N | null | undefined)[];
addMany(nodes: Iterable<BTNodeExemplar<K, V, N>>, values?: Iterable<V | undefined>): (N | null | undefined)[];
delete<C extends BTNCallback<N>>(identifier: ReturnType<C> | null, callback: C): BiTreeDeleteResult<N>[];
}

View file

@ -3,9 +3,20 @@ import * as Benchmark from 'benchmark';
import { magnitude } from '../../../utils';
const suite = new Benchmark.Suite();
const { TEN_THOUSAND } = magnitude;
const { MILLION, TEN_THOUSAND } = magnitude;
suite
.add(`${MILLION.toLocaleString()} push & shift`, () => {
const list = new SinglyLinkedList<number>();
for (let i = 0; i < MILLION; i++) {
list.push(i);
}
for (let i = 0; i < MILLION; i++) {
list.shift();
}
})
.add(`${TEN_THOUSAND.toLocaleString()} push & pop`, () => {
const list = new SinglyLinkedList<number>();

View file

@ -10,7 +10,7 @@ describe('BST operations test', () => {
bst.add([11, 11]);
bst.add([3, 3]);
const idsAndValues: [number, number][] = [[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]];
bst.addMany(idsAndValues, false);
bst.addMany(idsAndValues, undefined, false);
expect(bst.root).toBeInstanceOf(BSTNode);
if (bst.root) expect(bst.root.key).toBe(11);
@ -189,28 +189,27 @@ describe('BST operations test', () => {
});
it('should perform various operations on a Binary Search Tree with object values', () => {
const objBST = new BST<number, { key: number; keyA: number }>();
const objBST = new BST<number, { name: string; age: number }>();
expect(objBST).toBeInstanceOf(BST);
objBST.add([11, { key: 11, keyA: 11 }]);
objBST.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 }]
];
objBST.add([11, { name: '11', age: 11 }]);
objBST.add([3, { name: '3', age: 3 }]);
objBST.addMany(values, false);
objBST.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5], [
{ "name": "Alice", "age": 15 },
{ "name": "Bob", "age": 1 },
{ "name": "Charlie", "age": 8 },
{ "name": "David", "age": 13 },
{ "name": "Emma", "age": 16 },
{ "name": "Frank", "age": 2 },
{ "name": "Grace", "age": 6 },
{ "name": "Hannah", "age": 9 },
{ "name": "Isaac", "age": 12 },
{ "name": "Jack", "age": 14 },
{ "name": "Katie", "age": 4 },
{ "name": "Liam", "age": 7 },
{ "name": "Mia", "age": 10 },
{ "name": "Noah", "age": 5 }
], false);
expect(objBST.root).toBeInstanceOf(BSTNode);
@ -232,7 +231,7 @@ describe('BST operations test', () => {
expect(leftMost?.key).toBe(1);
const node15 = objBST.getNode(15);
expect(node15?.value).toEqual({ key: 15, keyA: 15 });
expect(node15?.value).toEqual({ name: 'Alice', age: 15 });
const minNodeBySpecificNode = node15 && objBST.getLeftMost(node15);
expect(minNodeBySpecificNode?.key).toBe(12);
@ -256,7 +255,7 @@ describe('BST operations test', () => {
objBST.perfectlyBalance();
expect(objBST.isPerfectlyBalanced()).toBe(true);
const bfsNodesAfterBalanced: BSTNode<number, { key: number; keyA: number }>[] = [];
const bfsNodesAfterBalanced: BSTNode<number, { name: string; age: number }>[] = [];
objBST.bfs(node => bfsNodesAfterBalanced.push(node));
expect(bfsNodesAfterBalanced[0].key).toBe(8);
expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16);
@ -381,7 +380,7 @@ describe('BST operations test', () => {
expect(bfsIDs[1]).toBe(12);
expect(bfsIDs[2]).toBe(16);
const bfsNodes: BSTNode<number, { key: number; keyA: number }>[] = [];
const bfsNodes: BSTNode<number, { name: string; age: number }>[] = [];
objBST.bfs(node => bfsNodes.push(node));
expect(bfsNodes[0].key).toBe(2);
expect(bfsNodes[1].key).toBe(12);
@ -396,7 +395,7 @@ describe('BST operations test recursively', () => {
bst.add([11, 11]);
bst.add([3, 3]);
const idsAndValues = [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
bst.addMany(idsAndValues, false);
bst.addMany(idsAndValues, undefined, false);
expect(bst.root).toBeInstanceOf(BSTNode);
if (bst.root) expect(bst.root.key).toBe(11);
@ -580,7 +579,7 @@ describe('BST operations test recursively', () => {
expect(objBST).toBeInstanceOf(BST);
objBST.add([11, { key: 11, keyA: 11 }]);
objBST.add([3, { key: 3, keyA: 3 }]);
const values: [number, { key: number; keyA: number }][] = [
const entries: [number, { key: number; keyA: number }][] = [
[15, { key: 15, keyA: 15 }],
[1, { key: 1, keyA: 1 }],
[8, { key: 8, keyA: 8 }],
@ -598,7 +597,8 @@ describe('BST operations test recursively', () => {
];
objBST.addMany(
values,
entries,
undefined,
false
);
@ -829,7 +829,7 @@ describe('BST Performance test', function () {
it('should the lastKey of a BST to be the largest key', function () {
const bst = new BST();
bst.addMany([9, 8, 7, 3, 1, 2, 5, 4, 6], false);
bst.addMany([9, 8, 7, 3, 1, 2, 5, 4, 6], undefined, false);
// TODO
// expect(bst.lastKey()).toBe(9);
});

View file

@ -5,7 +5,7 @@ describe('Overall BinaryTree Test', () => {
const bst = new BST();
bst.add(11);
bst.add(3);
bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5], false);
bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5], undefined, false);
bst.size === 16; // true
expect(bst.size).toBe(16); // true
bst.has(6); // true