From 3a86a5c37daf2656d2a03509df69ac394250b81b Mon Sep 17 00:00:00 2001 From: Revone Date: Sat, 21 Oct 2023 23:56:14 +0800 Subject: [PATCH] [binary-tree, graph] Replace all code that uses Arrays as makeshift Queues with actual Queues to improve efficiency. [binary-tree] Remove most methods for calculating sums and modifying properties, adopting a generic callback-based design. [bst] Enable default batch addition in BST using sorted binary insertion to maintain tree balance as much as possible. --- .../binary-tree/binary-tree.ts | 70 +----- src/data-structures/binary-tree/bst.ts | 128 ++-------- .../binary-tree/tree-multiset.ts | 234 +----------------- src/data-structures/graph/abstract-graph.ts | 7 +- .../binary-tree/avl-tree.test.ts | 8 +- .../data-structures/binary-tree/bst.test.ts | 51 +++- .../binary-tree/overall.test.ts | 2 +- .../binary-tree/tree-multiset.test.ts | 48 +++- test/unit/data-structures/queue/deque.test.ts | 17 ++ test/unit/data-structures/queue/queue.test.ts | 42 ++++ 10 files changed, 185 insertions(+), 422 deletions(-) diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 54b600e..8f4037f 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -23,6 +23,7 @@ import { } from '../../types'; import {IBinaryTree} from '../../interfaces'; import {trampoline} from '../../utils'; +import {Queue} from '../queue'; export class BinaryTreeNode = BinaryTreeNodeNested> { /** @@ -210,8 +211,8 @@ export class BinaryTree = BinaryTreeNode> */ add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined { const _bfs = (root: N, newNode: N | null): N | undefined | null => { - const queue: Array = [root]; - while (queue.length > 0) { + const queue = new Queue([root]); + while (queue.size > 0) { const cur = queue.shift(); if (cur) { if (newNode && cur.key === newNode.key) return; @@ -509,8 +510,8 @@ export class BinaryTree = BinaryTreeNode> _traverse(this.root); } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { + const queue = new Queue([this.root]); + while (queue.size > 0) { const cur = queue.shift(); if (cur) { if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return result; @@ -762,65 +763,11 @@ export class BinaryTree = BinaryTreeNode> } } - /** - * The function `subTreeSum` calculates the sum of a specified property in a binary tree or subtree. - * @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a binary - * tree or the ID of a binary tree node. It can also be `null` if there is no subtree. - * @param {BinaryTreeNodePropertyName} [propertyName] - propertyName is an optional parameter that specifies the - * property of the binary tree node to use for calculating the sum. It can be either 'key' or 'val'. If propertyName is - * not provided, it defaults to 'key'. - * @returns a number, which is the sum of the values of the specified property in the subtree rooted at `subTreeRoot`. - */ - subTreeSum(subTreeRoot: N | BinaryTreeNodeKey | null, propertyName: BinaryTreeNodePropertyName = 'key'): number { - if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'key'); - - if (!subTreeRoot) return 0; - - let sum = 0; - - const _sumByProperty = (cur: N) => { - let needSum: number; - switch (propertyName) { - case 'key': - needSum = cur.key; - break; - case 'val': - needSum = typeof cur.val === 'number' ? cur.val : 0; - break; - default: - needSum = cur.key; - break; - } - return needSum; - }; - - if (this._loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N): void => { - sum += _sumByProperty(cur); - cur.left && _traverse(cur.left); - cur.right && _traverse(cur.right); - }; - - _traverse(subTreeRoot); - } else { - const stack: N[] = [subTreeRoot]; - - while (stack.length > 0) { - const cur = stack.pop()!; - sum += _sumByProperty(cur); - cur.right && stack.push(cur.right); - cur.left && stack.push(cur.left); - } - } - - return sum; - } - /** * The function `subTreeForeach` adds a delta value to a specified property of each node in a subtree. * @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a binary * tree or the ID of a node in the binary tree. It can also be `null` if there is no subtree to add to. - * @param callBack - The `callBack` parameter is a function that takes a node as a parameter and returns a number. + * @param callback - The `callback` parameter is a function that takes a node as a parameter and returns a value. * specifies the property of the binary tree node that should be modified. If not provided, it defaults to 'key'. * @returns a boolean value. */ @@ -887,10 +834,9 @@ export class BinaryTree = BinaryTreeNode> */ bfs(nodeOrPropertyName: NodeOrPropertyName = 'key'): BinaryTreeNodeProperties { this._clearResults(); - const queue: Array = [this.root]; + const queue = new Queue([this.root]); - while (queue.length !== 0) { - // TODO Array.shift is not efficient, consider using Deque + while (queue.size !== 0) { const cur = queue.shift(); if (cur) { this._accumulatedByPropertyName(cur, nodeOrPropertyName); diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 22b1af6..c4b49ed 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -15,6 +15,7 @@ import type { import {CP, LoopType} from '../../types'; import {BinaryTree, BinaryTreeNode} from './binary-tree'; import {IBinaryTree} from '../../interfaces'; +import {Queue} from '../queue'; export class BSTNode = BSTNodeNested> extends BinaryTreeNode { constructor(key: BinaryTreeNodeKey, val?: V) { @@ -59,7 +60,7 @@ export class BST = BSTNode> extends BinaryTree * @returns The function `add` returns the inserted node (`inserted`) which can be of type `N`, `null`, or `undefined`. */ override add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined { - // TODO support node as a param + // TODO support node as a parameter let inserted: N | null = null; let newNode: N | null = null; if (keyOrNode instanceof BSTNode) { @@ -137,8 +138,9 @@ export class BST = BSTNode> extends BinaryTree override addMany( keysOrNodes: (BinaryTreeNodeKey | null)[] | (N | null)[], data?: N['val'][], - isBalanceAdd = false + isBalanceAdd = true ): (N | null | undefined)[] { + // TODO this addMany function is inefficient, it should be optimized function hasNoNull(arr: (BinaryTreeNodeKey | null)[] | (N | null)[]): arr is BinaryTreeNodeKey[] | N[] { return arr.indexOf(null) === -1; } @@ -265,8 +267,8 @@ export class BST = BSTNode> extends BinaryTree _traverse(this.root); } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { + const queue = new Queue([this.root]); + while (queue.size > 0) { const cur = queue.shift(); if (cur) { if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return result; @@ -285,133 +287,47 @@ export class BST = BSTNode> extends BinaryTree } // --- start additional functions - /** - * The `lesserSum` function calculates the sum of property values in a binary tree for nodes that have a property value - * less than a given node. - * @param {N | BinaryTreeNodeKey | null} beginNode - The `beginNode` parameter can be one of the following: - * @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that - * specifies the property name to use for calculating the sum. If not provided, it defaults to `'key'`. - * @returns The function `lesserSum` returns a number, which represents the sum of the values of the nodes in the - * binary tree that have a lesser value than the specified `beginNode` based on the `propertyName`. - */ - lesserSum(beginNode: N | BinaryTreeNodeKey | null, propertyName: BinaryTreeNodePropertyName = 'key'): number { - if (typeof beginNode === 'number') beginNode = this.get(beginNode, 'key'); - if (!beginNode) return 0; - if (!this.root) return 0; - const key = beginNode.key; - const getSumByPropertyName = (cur: N) => { - let needSum: number; - switch (propertyName) { - case 'key': - needSum = cur.key; - break; - default: - needSum = cur.key; - break; - } - return needSum; - }; - - let sum = 0; - - if (this.loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N): void => { - const compared = this._compare(cur.key, key); - if (compared === CP.eq) { - if (cur.right) sum += this.subTreeSum(cur.right, propertyName); - return; - } else if (compared === CP.lt) { - if (cur.left) sum += this.subTreeSum(cur.left, propertyName); - sum += getSumByPropertyName(cur); - if (cur.right) _traverse(cur.right); - else return; - } else { - if (cur.left) _traverse(cur.left); - else return; - } - }; - - _traverse(this.root); - } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { - const cur = queue.shift(); - if (cur) { - const compared = this._compare(cur.key, key); - if (compared === CP.eq) { - if (cur.right) sum += this.subTreeSum(cur.right, propertyName); - return sum; - } else if (compared === CP.lt) { - // todo maybe a bug - if (cur.left) sum += this.subTreeSum(cur.left, propertyName); - sum += getSumByPropertyName(cur); - if (cur.right) queue.push(cur.right); - else return sum; - } else { - if (cur.left) queue.push(cur.left); - else return sum; - } - } - } - } - - return sum; - } /** - * The `allGreaterNodesAdd` function adds a delta value to the specified property of all nodes in a binary tree that + * The `lesserOrGreaterForeach` function adds a delta value to the specified property of all nodes in a binary tree that * have a greater value than a given node. - * @param {N | BinaryTreeNodeKey | null} node - The `node` parameter can be either of type `N` (a generic type), - * `BinaryTreeNodeKey`, or `null`. It represents the node in the binary tree to which the delta value will be added. - * @param {number} delta - The `delta` parameter is a number that represents the amount by which the property value of - * each greater node should be increased. - * @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that - * specifies the property name of the nodes in the binary tree that you want to update. If not provided, it defaults to - * 'key'. - * @returns a boolean value. + * @param {N | BinaryTreeNodeKey | null} node - The `node` parameter can be either of type `N` (a generic type), `BinaryTreeNodeKey`, or `null`. It + * represents the node in the binary tree to which the delta value will be added. + * @param lesserOrGreater - The `lesserOrGreater` parameter is an optional parameter that specifies whether the delta + * @param callback - The `callback` parameter is a function that takes a node as a parameter and returns a boolean */ - allGreaterNodesAdd( + lesserOrGreaterForeach( node: N | BinaryTreeNodeKey | null, - delta: number, - propertyName: BinaryTreeNodePropertyName = 'key' + lesserOrGreater: CP = CP.lt, + callback: (node: N) => void ): boolean { if (typeof node === 'number') node = this.get(node, 'key'); if (!node) return false; const key = node.key; if (!this.root) return false; - const _sumByPropertyName = (cur: N) => { - switch (propertyName) { - case 'key': - cur.key += delta; - break; - default: - cur.key += delta; - break; - } - }; if (this.loopType === LoopType.RECURSIVE) { const _traverse = (cur: N) => { const compared = this._compare(cur.key, key); - if (compared === CP.gt) _sumByPropertyName(cur); + if (compared === lesserOrGreater) callback(cur); if (!cur.left && !cur.right) return; - if (cur.left && this._compare(cur.left.key, key) === CP.gt) _traverse(cur.left); - if (cur.right && this._compare(cur.right.key, key) === CP.gt) _traverse(cur.right); + if (cur.left && this._compare(cur.left.key, key) === lesserOrGreater) _traverse(cur.left); + if (cur.right && this._compare(cur.right.key, key) === lesserOrGreater) _traverse(cur.right); }; _traverse(this.root); return true; } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { + const queue = new Queue([this.root]); + while (queue.size > 0) { const cur = queue.shift(); if (cur) { const compared = this._compare(cur.key, key); - if (compared === CP.gt) _sumByPropertyName(cur); + if (compared === lesserOrGreater) callback(cur); - if (cur.left && this._compare(cur.left.key, key) === CP.gt) queue.push(cur.left); - if (cur.right && this._compare(cur.right.key, key) === CP.gt) queue.push(cur.right); + if (cur.left && this._compare(cur.left.key, key) === lesserOrGreater) queue.push(cur.left); + if (cur.right && this._compare(cur.right.key, key) === lesserOrGreater) queue.push(cur.right); } } return true; diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index 06294b6..1e62b91 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -9,6 +9,7 @@ import type {BinaryTreeNodeKey, TreeMultisetNodeNested, TreeMultisetOptions} fro import {BinaryTreeDeletedResult, CP, DFSOrderPattern, FamilyPosition, LoopType} from '../../types'; import {IBinaryTree} from '../../interfaces'; import {AVLTree, AVLTreeNode} from './avl-tree'; +import {Queue} from '../queue'; export class TreeMultisetNode< V = any, @@ -349,121 +350,6 @@ export class TreeMultiset = TreeMultiset return bstDeletedResult; } - /** - * The function `getSubTreeCount` calculates the number of nodes and the sum of their counts in a subtree, using either - * recursive or iterative traversal. - * @param {N | null | undefined} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree in a - * binary tree. - * @returns The function `getSubTreeCount` returns an array `[number, number]`. - */ - getSubTreeCount(subTreeRoot: N | null | undefined) { - const res: [number, number] = [0, 0]; - if (!subTreeRoot) return res; - - if (this.loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N) => { - res[0]++; - res[1] += cur.count; - cur.left && _traverse(cur.left); - cur.right && _traverse(cur.right); - }; - - _traverse(subTreeRoot); - return res; - } else { - const stack: N[] = [subTreeRoot]; - - while (stack.length > 0) { - const cur = stack.pop()!; - res[0]++; - res[1] += cur.count; - cur.right && stack.push(cur.right); - cur.left && stack.push(cur.left); - } - - return res; - } - } - - /** - * The function `subTreeSumCount` calculates the sum of the `count` property of each node in a subtree, either - * recursively or iteratively. - * @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree - * in a binary tree. It can be either a `BinaryTreeNodeKey` (a unique identifier for a node in the binary tree) or - * `null` if the subtree is empty. - * @returns the sum of the count values of all nodes in the subtree rooted at `subTreeRoot`. - */ - subTreeSumCount(subTreeRoot: N | BinaryTreeNodeKey | null): number { - if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'key'); - - if (!subTreeRoot) return 0; - - let sum = 0; - - if (this.loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N): void => { - sum += cur.count; - cur.left && _traverse(cur.left); - cur.right && _traverse(cur.right); - }; - - _traverse(subTreeRoot); - } else { - const stack: N[] = [subTreeRoot]; - - while (stack.length > 0) { - const cur = stack.pop()!; - sum += cur.count; - cur.right && stack.push(cur.right); - cur.left && stack.push(cur.left); - } - } - - return sum; - } - - /** - * The function `subTreeAddCount` recursively or iteratively traverses a binary tree and adds a given delta value to - * the `count` property of each node. - * @param {N | BinaryTreeNodeKey | null} subTreeRoot - The `subTreeRoot` parameter represents the root node of a subtree - * in a binary tree. It can be either a `BinaryTreeNodeKey` (a unique identifier for a node in the binary tree), a - * `BinaryTreeNode` object, or `null` if the subtree is empty. - * @param {number} delta - The delta parameter is a number that represents the amount by which the count of each node - * in the subtree should be increased or decreased. - * @returns a boolean value. - */ - subTreeAddCount(subTreeRoot: N | BinaryTreeNodeKey | null, delta: number): boolean { - if (typeof subTreeRoot === 'number') subTreeRoot = this.get(subTreeRoot, 'key'); - - if (!subTreeRoot) return false; - - const _addByProperty = (cur: N) => { - cur.count += delta; - this._setCount(this.count + delta); - }; - - if (this.loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N) => { - _addByProperty(cur); - cur.left && _traverse(cur.left); - cur.right && _traverse(cur.right); - }; - - _traverse(subTreeRoot); - } else { - const stack: N[] = [subTreeRoot]; - - while (stack.length > 0) { - const cur = stack.pop()!; - - _addByProperty(cur); - cur.right && stack.push(cur.right); - cur.left && stack.push(cur.left); - } - } - return true; - } - /** * The function `getNodesByCount` returns an array of nodes that have a specific count property, either recursively or * using a queue. @@ -492,8 +378,8 @@ export class TreeMultiset = TreeMultiset _traverse(this.root); } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { + const queue = new Queue([this.root]); + while (queue.size > 0) { const cur = queue.shift(); if (cur) { if (cur.count === nodeProperty) { @@ -545,120 +431,6 @@ export class TreeMultiset = TreeMultiset return nodes.map(node => node.count); } - /** - * The function dfsCountIterative performs an iterative depth-first search and returns an array of node counts based on - * the specified traversal pattern. - * @param {'in' | 'pre' | 'post'} [pattern] - The pattern parameter is a string that specifies the traversal order for - * the Depth-First Search (dfs) algorithm. It can have three possible values: 'in', 'pre', or 'post'. - * @param loopType - The loopType parameter is a string that specifies the type of loop to use when traversing the - * @returns The dfsCountIterative function returns an array of numbers, which represents the count property of each node - * in the dfs traversal. - */ - dfsCount(pattern: DFSOrderPattern = 'in', loopType: LoopType = LoopType.ITERATIVE): number[] { - const nodes = super.dfs(pattern, 'node', loopType); - return nodes.map(node => node.count); - } - - /** - * The `lesserSumCount` function calculates the sum of the counts of all nodes in a binary tree that have a lesser - * value than a given node. - * @param {N | BinaryTreeNodeKey | null} beginNode - The `beginNode` parameter can be one of the following: - * @returns the sum of the counts of nodes in the binary tree that have a lesser value than the given beginNode. - */ - lesserSumCount(beginNode: N | BinaryTreeNodeKey | null): number { - if (typeof beginNode === 'number') beginNode = this.get(beginNode, 'key'); - if (!beginNode) return 0; - if (!this.root) return 0; - const key = beginNode.key; - - let sum = 0; - - if (this.loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N): void => { - const compared = this._compare(cur.key, key); - if (compared === CP.eq) { - if (cur.right) sum += this.subTreeSumCount(cur.right); - return; - } else if (compared === CP.lt) { - if (cur.left) sum += this.subTreeSumCount(cur.left); - sum += cur.count; - if (cur.right) _traverse(cur.right); - else return; - } else { - if (cur.left) _traverse(cur.left); - else return; - } - }; - - _traverse(this.root); - } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { - const cur = queue.shift(); - if (cur) { - const compared = this._compare(cur.key, key); - if (compared === CP.eq) { - if (cur.right) sum += this.subTreeSumCount(cur.right); - return sum; - } else if (compared === CP.lt) { - // todo maybe a bug - if (cur.left) sum += this.subTreeSumCount(cur.left); - sum += cur.count; - if (cur.right) queue.push(cur.right); - else return sum; - } else { - if (cur.left) queue.push(cur.left); - else return sum; - } - } - } - } - - return sum; - } - - /** - * The function `allGreaterNodesAddCount` updates the count property of all nodes in a binary tree that have an ID - * greater than a given ID by a specified delta value. - * @param {N | BinaryTreeNodeKey | null} node - The `node` parameter can be one of the following: - * @param {number} delta - The `delta` parameter is a number that represents the amount by which the `count` property - * of each node should be increased. - * @returns a boolean value. - */ - allGreaterNodesAddCount(node: N | BinaryTreeNodeKey | null, delta: number): boolean { - if (typeof node === 'number') node = this.get(node, 'key'); - if (!node) return false; - const key = node.key; - if (!this.root) return false; - - if (this.loopType === LoopType.RECURSIVE) { - const _traverse = (cur: N) => { - const compared = this._compare(cur.key, key); - if (compared === CP.gt) cur.count += delta; - - if (!cur.left && !cur.right) return; - if (cur.left && this._compare(cur.left.key, key) === CP.gt) _traverse(cur.left); - if (cur.right && this._compare(cur.right.key, key) === CP.gt) _traverse(cur.right); - }; - - _traverse(this.root); - return true; - } else { - const queue: N[] = [this.root]; - while (queue.length > 0) { - const cur = queue.shift(); - if (cur) { - const compared = this._compare(cur.key, key); - if (compared === CP.gt) cur.count += delta; - - if (cur.left && this._compare(cur.left.key, key) === CP.gt) queue.push(cur.left); - if (cur.right && this._compare(cur.right.key, key) === CP.gt) queue.push(cur.right); - } - } - return true; - } - } - /** * The clear() function clears the data and sets the count to 0. */ diff --git a/src/data-structures/graph/abstract-graph.ts b/src/data-structures/graph/abstract-graph.ts index 92b5380..e820f6b 100644 --- a/src/data-structures/graph/abstract-graph.ts +++ b/src/data-structures/graph/abstract-graph.ts @@ -9,6 +9,7 @@ import {arrayRemove, uuidV4} from '../../utils'; import {PriorityQueue} from '../priority-queue'; import type {DijkstraResult, VertexKey} from '../../types'; import {IGraph} from '../../interfaces'; +import {Queue} from '../queue'; export abstract class AbstractVertex { /** @@ -342,11 +343,11 @@ export abstract class AbstractGraph< } const visited: Map = new Map(); - const queue: V[] = [vertex1]; + const queue = new Queue([vertex1]); visited.set(vertex1, true); let cost = 0; - while (queue.length > 0) { - for (let i = 0; i < queue.length; i++) { + while (queue.size > 0) { + for (let i = 0; i < queue.size; i++) { const cur = queue.shift(); if (cur === vertex2) { return cost; diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index 358132a..fd2fb41 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -1,4 +1,4 @@ -import {AVLTree} from '../../../../src'; +import {AVLTree, CP} from '../../../../src'; describe('AVL Tree Test', () => { it('should perform various operations on a AVL Tree', () => { @@ -22,10 +22,12 @@ describe('AVL Tree Test', () => { const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node15); expect(getMinNodeBySpecificNode?.key).toBe(12); - const subTreeSum = node15 && tree.subTreeSum(node15); + let subTreeSum = 0; + node15 && tree.subTreeForeach(node15, node => (subTreeSum += node.key)); expect(subTreeSum).toBe(70); - const lesserSum = tree.lesserSum(10); + let lesserSum = 0; + tree.lesserOrGreaterForeach(10, CP.lt, node => (lesserSum += node.key)); 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. diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index bd5d06f..84c0d79 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -1,4 +1,6 @@ -import {BST, BSTNode} from '../../../../src'; +import {BST, BSTNode, CP} from '../../../../src'; + +const isDebug = false; describe('BST operations test', () => { it('should perform various operations on a Binary Search Tree with numeric values', () => { @@ -7,7 +9,7 @@ describe('BST operations test', () => { 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, idsAndValues); + bst.addMany(idsAndValues, idsAndValues, false); expect(bst.root).toBeInstanceOf(BSTNode); if (bst.root) expect(bst.root.key).toBe(11); @@ -33,10 +35,12 @@ describe('BST operations test', () => { const minNodeBySpecificNode = node15 && bst.getLeftMost(node15); expect(minNodeBySpecificNode?.key).toBe(12); - const subTreeSum = node15 && bst.subTreeSum(15); + let subTreeSum = 0; + node15 && bst.subTreeForeach(15, node => (subTreeSum += node.key)); expect(subTreeSum).toBe(70); - const lesserSum = bst.lesserSum(10); + let lesserSum = 0; + bst.lesserOrGreaterForeach(10, CP.lt, node => (lesserSum += node.key)); expect(lesserSum).toBe(45); expect(node15).toBeInstanceOf(BSTNode); @@ -204,7 +208,8 @@ describe('BST operations test', () => { objBST.addMany( values.map(item => item.key), - values + values, + false ); expect(objBST.root).toBeInstanceOf(BSTNode); @@ -231,10 +236,12 @@ describe('BST operations test', () => { const minNodeBySpecificNode = node15 && objBST.getLeftMost(node15); expect(minNodeBySpecificNode?.key).toBe(12); - const subTreeSum = node15 && objBST.subTreeSum(node15); + let subTreeSum = 0; + node15 && objBST.subTreeForeach(node15, node => (subTreeSum += node.key)); expect(subTreeSum).toBe(70); - const lesserSum = objBST.lesserSum(10); + let lesserSum = 0; + objBST.lesserOrGreaterForeach(10, CP.lt, node => (lesserSum += node.key)); expect(lesserSum).toBe(45); expect(node15).toBeInstanceOf(BSTNode); @@ -378,3 +385,33 @@ describe('BST operations test', () => { expect(bfsNodes[2].key).toBe(16); }); }); + +describe('BST Performance test', function () { + const bst = new BST>(); + const inputSize = 10000; // Adjust input sizes as needed + + beforeEach(() => { + bst.clear(); + }); + + it(`Observe the time consumption of BST.dfs be good`, function () { + const startDFS = performance.now(); + const dfs = bst.dfs(); + isDebug && console.log('---bfs', performance.now() - startDFS, dfs.length); + }); + + it('Should the time consumption of lesserOrGreaterForeach fitting O(n log n)', function () { + const nodes: number[] = []; + for (let i = 0; i < inputSize; i++) { + nodes.push(i); + } + const start = performance.now(); + bst.addMany(nodes); + isDebug && console.log('---add', performance.now() - start); + const startL = performance.now(); + bst.lesserOrGreaterForeach(inputSize / 2, CP.lt, node => { + return node.key - 1; + }); + isDebug && console.log('---lesserOrGreaterForeach', performance.now() - startL); + }); +}); diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index 9fd298b..2cdffee 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -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]); + 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 diff --git a/test/unit/data-structures/binary-tree/tree-multiset.test.ts b/test/unit/data-structures/binary-tree/tree-multiset.test.ts index c2eb7a6..4be9ce1 100644 --- a/test/unit/data-structures/binary-tree/tree-multiset.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multiset.test.ts @@ -1,5 +1,6 @@ -import {TreeMultiset, TreeMultisetNode} from '../../../../src'; +import {CP, TreeMultiset, TreeMultisetNode} from '../../../../src'; +const isDebug = false; describe('TreeMultiset operations test', () => { it('should perform various operations on a Binary Search Tree with numeric values', () => { const treeMultiset = new TreeMultiset(); @@ -39,20 +40,26 @@ describe('TreeMultiset operations test', () => { const minNodeBySpecificNode = node15 && treeMultiset.getLeftMost(node15); expect(minNodeBySpecificNode?.key).toBe(12); - const subTreeSum = node15 && treeMultiset.subTreeSum(15); + let subTreeSum = 0; + node15 && treeMultiset.subTreeForeach(15, (node: TreeMultisetNode) => (subTreeSum += node.key)); expect(subTreeSum).toBe(70); - const lesserSum = treeMultiset.lesserSum(10); + let lesserSum = 0; + treeMultiset.lesserOrGreaterForeach(10, CP.lt, (node: TreeMultisetNode) => (lesserSum += node.key)); expect(lesserSum).toBe(45); expect(node15 instanceof TreeMultisetNode); if (node15 instanceof TreeMultisetNode) { - const subTreeAdd = treeMultiset.subTreeAddCount(15, 1); + const subTreeAdd = treeMultiset.subTreeForeach(15, (node: TreeMultisetNode) => (node.count += 1)); expect(subTreeAdd); } const node11 = treeMultiset.get(11); expect(node11 instanceof TreeMultisetNode); if (node11 instanceof TreeMultisetNode) { - const allGreaterNodesAdded = treeMultiset.allGreaterNodesAddCount(11, 2); + const allGreaterNodesAdded = treeMultiset.lesserOrGreaterForeach( + 11, + CP.gt, + (node: TreeMultisetNode) => (node.count += 2) + ); expect(allGreaterNodesAdded); } @@ -419,13 +426,19 @@ describe('TreeMultiset operations test', () => { describe('TreeMultiset Performance test', function () { // const treeMS = new TreeMultiset>(); - // const inputSizes = [100]; // Adjust input sizes as needed + // const inputSize = [100]; // Adjust input sizes as needed // // // Define a function to calculate the expected O(n log n) time // function expectedTime(n: number): number { // return n * Math.log(n); // } + const treeMS = new TreeMultiset>(); + const inputSize = 100000; // Adjust input sizes as needed + + beforeEach(() => { + treeMS.clear(); + }); it(`Observe the time consumption of TreeMultiset.add fitting O(n log n)`, function () { // // Create a benchmark suite // const suite = new Benchmark.Suite(); @@ -437,9 +450,9 @@ describe('TreeMultiset Performance test', function () { // } // return arr; // } - // const inputArray = generateRandomArray(inputSizes[0]); + // const inputArray = generateRandomArray(inputSize[0]); // - // suite.add(`TreeMultiset addMany (n=${inputSizes[0]})`, () => { + // suite.add(`TreeMultiset addMany (n=${inputSize[0]})`, () => { // treeMS.addMany([...inputArray]); // }); // @@ -453,9 +466,26 @@ describe('TreeMultiset Performance test', function () { // console.log(`Input size (n): ${n}, Observed time: ${observedTime.toFixed(2)}ms, Expected time: ${expected.toFixed(2)}ms`); // }) // .on('complete', () => { - // console.log(`Benchmark (n=${inputSizes[0]}) completed.`); + // console.log(`Benchmark (n=${inputSize[0]}) completed.`); // done(); // Call done to indicate the test is complete // }) // .run({async: true}); }); + + it(`Observe the time consumption of TreeMultiset.dfs be good`, function () { + const startDFS = performance.now(); + const dfs = treeMS.dfs(); + isDebug && console.log('---bfs', performance.now() - startDFS, dfs.length); + }); + + it('Should the time consumption of lesserOrGreaterForeach fitting O(n log n)', function () { + const start = performance.now(); + for (let i = 0; i < inputSize; i++) { + treeMS.add(i); + } + isDebug && console.log('---add', performance.now() - start); + const startL = performance.now(); + treeMS.lesserOrGreaterForeach(inputSize / 2, CP.lt, (node: TreeMultisetNode) => (node.count += 1)); + isDebug && console.log('---lesserOrGreaterForeach', performance.now() - startL); + }); }); diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index 6fabba0..9e4b275 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -1,4 +1,5 @@ import {Deque, ArrayDeque, ObjectDeque} from '../../../../src'; +import {bigO} from '../../../utils'; describe('Deque Tests', () => { // Test cases for the Deque class (DoublyLinkedList-based) @@ -128,3 +129,19 @@ describe('Deque Tests', () => { // Add more test cases as needed }); }); + +describe('Deque Performance Test', () => { + const dataSize = 10000; + it('should numeric queue be efficient', function () { + const startTime = performance.now(); + const queue = new Deque(); + for (let i = 0; i < dataSize; i++) { + queue.unshift(i); + } + for (let i = 0; i < dataSize; i++) { + queue.pop(); + } + console.log(`Queue Deque Test: ${performance.now() - startTime} ms`); + expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100); + }); +}); diff --git a/test/unit/data-structures/queue/queue.test.ts b/test/unit/data-structures/queue/queue.test.ts index 2a4470a..7f35cab 100644 --- a/test/unit/data-structures/queue/queue.test.ts +++ b/test/unit/data-structures/queue/queue.test.ts @@ -197,3 +197,45 @@ describe('LinkedListQueue', () => { // Add more test cases for other methods of LinkedListQueue. }); + +describe('Queue Performance Test', () => { + const dataSize = 10000; + it('should numeric queue be efficient', function () { + const startTime = performance.now(); + const queue = new Queue(); + for (let i = 0; i < dataSize; i++) { + queue.enqueue(i); + } + for (let i = 0; i < dataSize; i++) { + queue.dequeue(); + } + console.log(`Queue Performance Test: ${performance.now() - startTime} ms`); + expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100); + }); + + it('should numeric Array be more efficient than Queue when the data size is 10000', function () { + const startTime2 = performance.now(); + const queue2: number[] = []; + for (let i = 0; i < dataSize; i++) { + queue2.push(i); + } + for (let i = 0; i < dataSize; i++) { + queue2.shift(); + } + console.log(`Array Performance Test: ${performance.now() - startTime2} ms`); + expect(performance.now() - startTime2).toBeLessThan(bigO.CUBED * 100); + }); + + it('should numeric LinkedListQueue be efficient', function () { + const startTime = performance.now(); + const queue = new LinkedListQueue(); + for (let i = 0; i < dataSize; i++) { + queue.enqueue(i); + } + for (let i = 0; i < dataSize; i++) { + queue.dequeue(); + } + console.log(`LinkedListQueue Performance Test: ${performance.now() - startTime} ms`); + expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100); + }); +});