[BST] BST has added an 'addMany' method, supports batch insertion using binary splitting, supports both recursive and iterative approaches.

This commit is contained in:
Revone 2023-09-19 22:47:58 +08:00
parent 0abd5eced7
commit 6f3baa9c79
2 changed files with 117 additions and 44 deletions

View file

@ -297,41 +297,35 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
return inserted;
}
/**
* The `addMany` function adds multiple nodes to a tree data structure and returns an array of the inserted nodes or
* null/undefined values.
* @param {(BinaryTreeNodeId|N)[]} idsOrNodes - An array of BinaryTreeNodeId or N objects. These can be either the ID
* of a binary tree node or the actual node object itself.
* @param {N['val'][]} [data] - Optional array of values to be added to the nodes. If provided, the length of this
* array should be the same as the length of the `idsOrNodes` array.
* @returns The function `addMany` returns an array of values `(N | null | undefined)[]`.
* The `addMany` function takes an array of binary tree node IDs or nodes, and optionally an array of corresponding data
* values, and adds them to the binary tree.
* @param {(BinaryTreeNodeId | null)[] | (N | null)[]} idsOrNodes - An array of BinaryTreeNodeId or BinaryTreeNode
* objects, or null values.
* @param {N['val'][]} [data] - The `data` parameter is an optional array of values (`N['val'][]`) that corresponds to
* the nodes or node IDs being added. It is used to set the value of each node being added. If `data` is not provided,
* the value of the nodes will be `undefined`.
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
addMany(idsOrNodes: (BinaryTreeNodeId | N | null)[], data?: N['val'][]): (N | null | undefined)[] {
addMany(idsOrNodes: (BinaryTreeNodeId | null)[] | (N | null)[], data?: N['val'][]): (N | null | undefined)[] {
// TODO not sure addMany not be run multi times
const inserted: (N | null | undefined)[] = [];
const map: Map<N | BinaryTreeNodeId | null, number> = new Map();
for (const idOrNode of idsOrNodes) map.set(idOrNode, (map.get(idOrNode) ?? 0) + 1);
for (let i = 0; i < idsOrNodes.length; i++) {
const idOrNode = idsOrNodes[i];
if (map.has(idOrNode)) {
if (idOrNode instanceof AbstractBinaryTreeNode) {
inserted.push(this.add(idOrNode.id, idOrNode.val));
continue;
}
if (idOrNode === null) {
inserted.push(this.add(null));
continue;
}
const val = data?.[i];
inserted.push(this.add(idOrNode, val));
map.delete(idOrNode);
if (idOrNode instanceof AbstractBinaryTreeNode) {
inserted.push(this.add(idOrNode.id, idOrNode.val));
continue;
}
if (idOrNode === null) {
inserted.push(this.add(null));
continue;
}
const val = data?.[i];
inserted.push(this.add(idOrNode, val));
}
return inserted;
}
@ -345,7 +339,7 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
* array. Each value in the `data` array will be assigned to the
* @returns The method is returning a boolean value.
*/
fill(idsOrNodes: (BinaryTreeNodeId | N | null)[], data?: N[] | Array<N['val']>): boolean {
fill(idsOrNodes: (BinaryTreeNodeId | null)[] | (N | null)[], data?: N[] | Array<N['val']>): boolean {
this.clear();
return idsOrNodes.length === this.addMany(idsOrNodes, data).length;
}
@ -363,9 +357,10 @@ export abstract class AbstractBinaryTree<N extends AbstractBinaryTreeNode<N['val
*/
remove(nodeOrId: N | BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): BinaryTreeDeletedResult<N>[] {
isUpdateAllLeftSum = isUpdateAllLeftSum === undefined ? true: isUpdateAllLeftSum;
isUpdateAllLeftSum = isUpdateAllLeftSum === undefined ? true : isUpdateAllLeftSum;
// TODO may implement update all left sum
if (isUpdateAllLeftSum) {}
if (isUpdateAllLeftSum) {
}
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
if (!this.root) return bstDeletedResult;

View file

@ -44,18 +44,25 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
/**
* The `add` function adds a new node to a binary tree, ensuring that duplicates are not accepted.
* @param {BinaryTreeNodeId} id - The `id` parameter is the identifier of the binary tree node that we want to add. It
* is of type `BinaryTreeNodeId`.
* @param [val] - The `val` parameter is an optional value that can be assigned to the node being added. It represents
* the value associated with the node.
* @returns The function `add` returns the inserted node (`inserted`) if it was successfully added to the binary tree.
* If the node was not added (e.g., due to a duplicate ID), it returns `null` or `undefined`.
* The `add` function adds a new node to a binary search tree, either by creating a new node or by updating an existing
* node with the same ID.
* @param {BinaryTreeNodeId | N | null} idOrNode - The `idOrNode` parameter can be either a `BinaryTreeNodeId` or a `N`
* (which represents a binary tree node) or `null`.
* @param [val] - The `val` parameter is an optional value that can be assigned to the `val` property of the new node
* being added to the binary search tree.
* @returns The function `add` returns the inserted node (`inserted`) which can be of type `N`, `null`, or `undefined`.
*/
override add(id: BinaryTreeNodeId, val?: N['val']): N | null | undefined {
override add(idOrNode: BinaryTreeNodeId | N | null, val?: N['val']): N | null | undefined {
// TODO support node as a param
let inserted: N | null = null;
const newNode = this.createNode(id, val);
let newNode: N | null = null;
if (idOrNode instanceof BSTNode) {
newNode = idOrNode;
} else if (typeof idOrNode === 'number') {
newNode = this.createNode(idOrNode, val);
} else if (idOrNode === null) {
newNode = null;
}
if (this.root === null) {
this._setRoot(newNode);
this._setSize(this.size + 1);
@ -65,14 +72,14 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
let traversing = true;
while (traversing) {
if (cur !== null && newNode !== null) {
if (this._compare(cur.id, id) === CP.eq) {
if (this._compare(cur.id, newNode.id) === CP.eq) {
if (newNode) {
cur.val = newNode.val;
}
//Duplicates are not accepted.
traversing = false;
inserted = cur;
} else if (this._compare(cur.id, id) === CP.gt) {
} else if (this._compare(cur.id, newNode.id) === CP.gt) {
// Traverse left of the node
if (cur.left === undefined) {
if (newNode) {
@ -87,7 +94,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
//Traverse the left of the current node
if (cur.left) cur = cur.left;
}
} else if (this._compare(cur.id, id) === CP.lt) {
} else if (this._compare(cur.id, newNode.id) === CP.lt) {
// Traverse right of the node
if (cur.right === undefined) {
if (newNode) {
@ -111,7 +118,79 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
return inserted;
}
// TODO need to implement addMany by using binary search for insertion.
/**
* The `addMany` function overrides the base class method to add multiple nodes to a binary search tree in a balanced
* manner.
* @param {[BinaryTreeNodeId | N , N['val']][]} idsOrNodes - The `idsOrNodes` parameter in the `addMany` function is an array of
* `BinaryTreeNodeId` or `N` (node) objects, or `null` values. It represents the nodes or node IDs that need to be added
* to the binary search tree.
* @param {N['val'][]} data - The values of tree nodes
* @param {boolean} isBalanceAdd - If true the nodes will be balance inserted in binary search method.
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
override addMany(idsOrNodes: (BinaryTreeNodeId | null)[] | (N | null)[], data?: N['val'][], isBalanceAdd = false): (N | null | undefined)[] {
function hasNoNull (arr: (BinaryTreeNodeId | null)[] | (N | null)[]): arr is BinaryTreeNodeId[] | N[] {
return arr.indexOf(null) === -1;
}
if (!isBalanceAdd || !hasNoNull(idsOrNodes)) {
return super.addMany(idsOrNodes, data);
}
const inserted: (N | null | undefined)[] = [];
const combinedArr: [BinaryTreeNodeId | N , N['val']][] = idsOrNodes.map((value, index) => [value, data?.[index]]);
let sorted = [];
function isNodeOrNullTuple(arr: [BinaryTreeNodeId | N , N['val']][]): arr is [N , N['val']][] {
for (const [idOrNode] of arr) if (idOrNode instanceof BSTNode) return true;
return false;
}
function isBinaryTreeIdOrNullTuple(arr: [BinaryTreeNodeId | N , N['val']][]): arr is [BinaryTreeNodeId , N['val']][] {
for (const [idOrNode] of arr) if (typeof idOrNode === 'number') return true;
return false;
}
let sortedIdsOrNodes: (number | N | null)[] = [], sortedData: (N["val"] | undefined)[] | undefined = [];
if (isNodeOrNullTuple(combinedArr)) {
sorted = combinedArr.sort((a, b) => a[0].id - b[0].id);
} else if (isBinaryTreeIdOrNullTuple(combinedArr)) {
sorted = combinedArr.sort((a, b) => a[0] - b[0]);
} else {
throw new Error('Invalid input idsOrNodes')
}
sortedIdsOrNodes = sorted.map(([idOrNode,]) => idOrNode);
sortedData = sorted.map(([,val]) => val);
const recursive = (arr: (BinaryTreeNodeId | null | N)[], data?: N['val'][]) => {
if (arr.length === 0) return;
const mid = Math.floor((arr.length - 1) / 2);
const newNode = this.add(arr[mid], data?.[mid]);
inserted.push(newNode);
recursive(arr.slice(0, mid), data?.slice(0, mid));
recursive(arr.slice(mid + 1), data?.slice(mid + 1));
}
const iterative = () => {
const n = sorted.length;
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 newNode = this.add(sortedIdsOrNodes[m], sortedData?.[m]);
inserted.push(newNode);
stack.push([m + 1, r]);
stack.push([l, m - 1]);
}
}
}
}
if (this.loopType === LoopType.RECURSIVE) {
recursive(sortedIdsOrNodes, sortedData)
} else {
iterative();
}
return inserted;
}
/**
* The function returns the first node in a binary tree that matches the given property name and value.
@ -150,8 +229,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
* is set to `true`, the function will return an array with only one node (if
* @returns an array of nodes (type N).
*/
override getNodes(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean): N[] {
propertyName = propertyName ?? 'id';
override getNodes(nodeProperty: BinaryTreeNodeId | N, propertyName : BinaryTreeNodePropertyName = 'id', onlyOne ?: boolean): N[] {
if (!this.root) return [];
const result: N[] = [];