UNPKG

heap-typed

Version:

Heap. Javascript & Typescript Data Structure.

417 lines (416 loc) 19.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TreeMultiMap = exports.TreeMultiMapNode = void 0; const rb_tree_1 = require("./rb-tree"); class TreeMultiMapNode extends rb_tree_1.RedBlackTreeNode { /** * 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, value, count = 1, color = 'BLACK') { super(key, value, color); this._count = 1; this.count = count; } /** * The function returns the value of the private variable _count. * @returns The count property of the object, which is of type number. */ get count() { return this._count; } /** * The above function sets the value of the count property. * @param {number} value - The value parameter is of type number, which means it can accept any * numeric value. */ set count(value) { this._count = value; } } exports.TreeMultiMapNode = TreeMultiMapNode; class TreeMultiMap extends rb_tree_1.RedBlackTree { /** * 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. */ constructor(keysNodesEntriesOrRaws = [], options) { super([], options); this._count = 0; if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws); } // 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() { 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() { 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 NODE. */ createNode(key, value, color = 'BLACK', count) { return new TreeMultiMapNode(key, value, count, color); } /** * 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`. */ createTree(options) { return new TreeMultiMap([], Object.assign({ iterationType: this.iterationType, isMapMode: this._isMapMode, comparator: this._comparator, toEntryFn: this._toEntryFn }, options)); } /** * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a * node based on the input. * @param {BTNRep<K, V, NODE> | R} keyNodeEntryOrRaw - The parameter * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, NODE>`. * @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 NODE object or undefined. */ keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count = 1) { 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 !== null && value !== void 0 ? value : entryValue; if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; } if (this._toEntryFn) { const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw); const finalValue = value !== null && value !== void 0 ? 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]; } /** * The function checks if the input is an instance of the TreeMultiMapNode class. * @param {BTNRep<K, V, NODE> | R} keyNodeEntryOrRaw - The parameter * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep<K, V, NODE>`. * @returns a boolean value indicating whether the input parameter `keyNodeEntryOrRaw` is * an instance of the `TreeMultiMapNode` class. */ isNode(keyNodeEntryOrRaw) { 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, NODE> | 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. */ add(keyNodeEntryOrRaw, value, count = 1) { const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count); const orgCount = (newNode === null || newNode === void 0 ? void 0 : 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, NODE> | 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<NODE>` objects. */ delete(keyNodeEntryOrRaw, ignoreCount = false) { if (keyNodeEntryOrRaw === null) return []; const results = []; let nodeToDelete; 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; if (!this.isRealNode(nodeToDelete.left)) { replacementNode = nodeToDelete.right; if (ignoreCount || nodeToDelete.count <= 1) { 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; replacementNode = successor.right; if (successor.parent === nodeToDelete) { if (this.isRealNode(replacementNode)) { replacementNode.parent = successor; } } else { if (ignoreCount || nodeToDelete.count <= 1) { 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. */ 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. */ perfectlyBalance(iterationType = this.iterationType) { const sorted = this.dfs(node => node, 'IN'), n = sorted.length; if (sorted.length < 1) return false; this.clear(); if (iterationType === 'RECURSIVE') { const buildBalanceBST = (l, r) => { 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 = [[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. */ clone() { const cloned = this.createTree(); this.bfs(node => cloned.add(node.key, undefined, node.count)); if (this._isMapMode) cloned._store = this._store; return cloned; } /** * 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, NODE>} 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, NODE>` class. * @param {R | BSTNOptKeyOrNode<K, NODE>} 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. */ _swapProperties(srcNode, destNode) { 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 {NODE} oldNode - The `oldNode` parameter is the node that you want to replace in the data * structure. * @param {NODE} newNode - The `newNode` parameter is an instance of the `NODE` class. * @returns The method is returning the result of calling the `_replaceNode` method from the * superclass, which is of type `NODE`. */ _replaceNode(oldNode, newNode) { newNode.count = oldNode.count + newNode.count; return super._replaceNode(oldNode, newNode); } } exports.TreeMultiMap = TreeMultiMap;