[rbtree] rebase from main is ready

This commit is contained in:
Revone 2023-10-31 11:35:50 +08:00
commit cc3be9349f
158 changed files with 8953 additions and 4500 deletions

View file

@ -273,11 +273,6 @@ module.exports = {
your webpack config is a function and takes them (see webpack documentation
for details)
*/
// webpackConfig: {
// fileName: './webpack.config.js',
// env: {},
// arguments: {},
// },
/* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use
for compilation (and whatever other naughty things babel plugins do to

View file

@ -1,15 +1,18 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
root=true # represents the top-level EditorConfig configuration file
[*] # means applicable to all files
charset = utf-8 #Set the file character set to utf-8
indent_style = space #Indent style (tab | space)
indent_size = 2 # indent size
end_of_line = lf # Control line break type (lf | cr | crlf)
trim_trailing_whitespace = true # Remove any whitespace characters at the beginning of the line
insert_final_newline = true #Always insert a new line at the end of the file
[*.md] # Indicates that only md files apply the following rules
max_line_length = off
trim_trailing_whitespace = false
[*.yml]
[*.{yml,yaml}]
indent_size = 2

View file

@ -1,12 +1,17 @@
module.exports = {
"parser": "@typescript-eslint/parser",
"plugins": [
"import",
"@typescript-eslint"
],
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier"
],
ignorePatterns: ["lib/", "dist/", "umd/", "coverage/", "docs/"],
"ignorePatterns": ["lib/", "dist/", "umd/", "coverage/", "docs/"],
"rules": {
"import/no-anonymous-default-export": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
@ -39,9 +44,6 @@ module.exports = {
}
]
},
"plugins": [
"import"
],
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [

1
.gitignore vendored
View file

@ -8,6 +8,7 @@ npm-debug.*
*.orig.*
.DS_Store
.idea
.vscode
/notes
/backup

View file

@ -1,4 +1,5 @@
.idea
.vscode
.auto-changelog
.auto-changelog-template.hbs
.dependency-cruiser.js
@ -9,7 +10,6 @@
/notes
/backup
/webpack.config.js
/scripts
/tsconfig.prod.json

View file

@ -1,5 +1,5 @@
src/types/data-structures/binary-tree.ts
src/types/data-structures/bst.ts
src/types/data-structures/avl-tree.ts
src/types/data-structures/rb-tree.ts
src/types/data-structures/tree-multiset.ts
src/types/data-structures/binary-tree/binary-tree.ts
src/types/data-structures/binary-tree/bst.ts
src/types/data-structures/binary-tree/avl-tree.ts
src/types/data-structures/binary-tree/rb-tree.ts
src/types/data-structures/binary-tree/tree-multiset.ts

View file

@ -3,7 +3,7 @@ module.exports = {
"bracketSpacing": false,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"bracketSameLine": false,
"bracketSameLine": true,
"jsxSingleQuote": true,
"printWidth": 120,
"proseWrap": "preserve",

View file

@ -8,10 +8,18 @@ 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.36.5](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
## [v1.40.0](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
### Changes
- [graph test] edge cases enriched [`#30`](https://github.com/zrwusa/data-structure-typed/pull/30)
- [graph] Modify the data structure design of the graph to change the g… [`#29`](https://github.com/zrwusa/data-structure-typed/pull/29)
- Optimization [`#23`](https://github.com/zrwusa/data-structure-typed/pull/23)
- Optimization [`#20`](https://github.com/zrwusa/data-structure-typed/pull/20)
- [binary-tree, graph] Replace all code that uses Arrays as makeshift Q… [`#18`](https://github.com/zrwusa/data-structure-typed/pull/18)
- 1. No need for dfsIterative; integrate it directly into the dfs metho… [`#17`](https://github.com/zrwusa/data-structure-typed/pull/17)
- [heap] fibonacci heap implemented. [test] big O estimate. [project] n… [`#15`](https://github.com/zrwusa/data-structure-typed/pull/15)
- [rbtree] implemented, but with bugs [`#13`](https://github.com/zrwusa/data-structure-typed/pull/13)
- [trie] renamed ambiguous methods and add comments to all methods. [`#12`](https://github.com/zrwusa/data-structure-typed/pull/12)
- [binarytree] modified the getDepth method to adhere to the proper def… [`#11`](https://github.com/zrwusa/data-structure-typed/pull/11)
- Trie [`#10`](https://github.com/zrwusa/data-structure-typed/pull/10)

View file

@ -56,7 +56,7 @@ import {
```html
<script src='https://cdn.jsdelivr.net/npm/data-structure-typed/umd/bundle.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/data-structure-typed/dist/umd/data-structure-typed.min.js'></script>
```
```js
@ -106,7 +106,7 @@ bst.getDepth(6) === 3; // true
bst.getLeftMost()?.id === 1; // true
bst.remove(6);
bst.delete(6);
bst.get(6); // null
bst.isAVLBalanced(); // true
bst.bfs()[0] === 11; // true
@ -121,7 +121,7 @@ objBST.addMany([{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8},
{id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7},
{id: 10, keyA: 10}, {id: 5, keyA: 5}]);
objBST.remove(11);
objBST.delete(11);
```
#### JS
@ -142,7 +142,7 @@ bst.getDepth(6) === 3; // true
const leftMost = bst.getLeftMost();
leftMost?.id === 1; // true
expect(leftMost?.id).toBe(1);
bst.remove(6);
bst.delete(6);
bst.get(6); // null
bst.isAVLBalanced(); // true or false
const bfsIDs = bst.bfs();
@ -159,12 +159,12 @@ objBST.addMany([{id: 15, keyA: 15}, {id: 1, keyA: 1}, {id: 8, keyA: 8},
{id: 14, keyA: 14}, {id: 4, keyA: 4}, {id: 7, keyA: 7},
{id: 10, keyA: 10}, {id: 5, keyA: 5}]);
objBST.remove(11);
objBST.delete(11);
const avlTree = new AVLTree();
avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5])
avlTree.isAVLBalanced(); // true
avlTree.remove(10);
avlTree.delete(10);
avlTree.isAVLBalanced(); // true
```
@ -178,7 +178,7 @@ import {AVLTree} from 'data-structure-typed';
const avlTree = new AVLTree();
avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5])
avlTree.isAVLBalanced(); // true
avlTree.remove(10);
avlTree.delete(10);
avlTree.isAVLBalanced(); // true
```
@ -190,7 +190,7 @@ const {AVLTree} = require('data-structure-typed');
const avlTree = new AVLTree();
avlTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5])
avlTree.isAVLBalanced(); // true
avlTree.remove(10);
avlTree.delete(10);
avlTree.isAVLBalanced(); // true
```
@ -214,7 +214,7 @@ graph.addEdge('A', 'B');
graph.hasEdge('A', 'B'); // true
graph.hasEdge('B', 'A'); // false
graph.removeEdgeSrcToDest('A', 'B');
graph.deleteEdgeSrcToDest('A', 'B');
graph.hasEdge('A', 'B'); // false
graph.addVertex('C');
@ -237,7 +237,7 @@ graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.removeVertex('C');
graph.deleteVertex('C');
graph.addEdge('A', 'B');
graph.addEdge('B', 'D');
@ -641,6 +641,14 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.id) // ['A', 'B', 'D
## Code design
### Adhere to ES6 standard naming conventions for APIs.
Standardize API conventions by using 'add' and 'delete' for element manipulation methods in all data structures.
Opt for concise and clear method names, avoiding excessive length while ensuring explicit intent.
### Object-oriented programming(OOP)
By strictly adhering to object-oriented design (BinaryTree -> BST -> AVLTree -> TreeMultiset), you can seamlessly
inherit the existing data structures to implement the customized ones you need. Object-oriented design stands as the
optimal approach to data structure design.

1558
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,22 +1,25 @@
{
"name": "data-structure-typed",
"version": "1.36.6",
"version": "1.40.0",
"description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.",
"main": "dist/index.js",
"module": "lib/index.js",
"types": "lib/index.d.ts",
"source": "src/index.ts",
"umd:main": "umd/bundle.min.js",
"main": "dist/cjs/index.js",
"module": "dist/mjs/index.js",
"types": "dist/mjs/index.d.ts",
"umd:main": "dist/umd/data-structure-typed.min.js",
"exports": {
"import": "./lib/index.js",
"require": "./dist/index.js"
".": {
"import": "./dist/mjs/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/mjs/index.d.ts"
}
},
"scripts": {
"build": "npm run build:es6 && npm run build:commonjs && npm run build:umd && npm run build:docs",
"build:es6": "rm -rf lib && tsc",
"build:commonjs": "rm -rf dist && tsc --project tsconfig.prod.json",
"build:umd": "webpack",
"build": "npm run build:mjs && npm run build:cjs && npm run build:umd && npm run build:docs",
"build:mjs": "rm -rf dist/mjs && tsc -p tsconfig.json",
"build:cjs": "rm -rf dist/cjs && tsc -p tsconfig-cjs.json",
"build:umd": "tsup",
"build:docs": "typedoc --out docs ./src",
"check": "tsc --noEmit",
"lint:src": "eslint --fix 'src/**/*.{js,ts}'",
"lint:test": "eslint --fix 'test/**/*.{js,ts}'",
"lint": "npm run lint:src && npm run lint:test",
@ -26,13 +29,17 @@
"fix:src": "npm run lint:src && npm run format:src",
"fix:test": "npm run lint:test && npm run format:test",
"fix": "npm run fix:src && npm run fix:test",
"update:individuals": "npm i avl-tree-typed binary-tree-typed bst-typed heap-typed --save-dev",
"install:individuals": "npm i avl-tree-typed binary-tree-typed bst-typed deque-typed directed-graph-typed doubly-linked-list-typed graph-typed heap-typed linked-list-typed max-heap-typed max-priority-queue-typed min-heap-typed min-priority-queue-typed priority-queue-typed singly-linked-list-typed stack-typed tree-multiset-typed trie-typed undirected-graph-typed queue-typed --save-dev",
"update:subs": "npm i avl-tree-typed binary-tree-typed bst-typed heap-typed --save-dev",
"install:all-subs": "npm i avl-tree-typed binary-tree-typed bst-typed deque-typed directed-graph-typed doubly-linked-list-typed graph-typed heap-typed linked-list-typed max-heap-typed max-priority-queue-typed min-heap-typed min-priority-queue-typed priority-queue-typed singly-linked-list-typed stack-typed tree-multiset-typed trie-typed undirected-graph-typed queue-typed --save-dev",
"test": "jest",
"check:deps": "dependency-cruiser src",
"changelog": "auto-changelog",
"coverage:badge": "istanbul-badges-readme",
"ci": "env && npm run lint && npm run build && npm run update:individuals && npm run test && git fetch --tags && npm run changelog"
"ci": "env && git fetch --tags && npm run lint && npm run build && npm run update:subs && npm run test && npm run changelog",
"copy:to-subs": "sh scripts/copy_to_all_subs.sh",
"publish:subs": "npm run copy:to-subs && sh scripts/publish_all_subs.sh",
"publish:docs": "sh scripts/publish_docs.sh",
"publish:all": "npm run ci && npm publish && npm run publish:docs && npm run publish:subs"
},
"repository": {
"type": "git",
@ -54,26 +61,25 @@
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"auto-changelog": "^2.4.0",
"avl-tree-typed": "^1.36.4",
"avl-tree-typed": "^1.39.6",
"benchmark": "^2.1.4",
"binary-tree-typed": "^1.36.4",
"bst-typed": "^1.36.4",
"binary-tree-typed": "^1.39.6",
"bst-typed": "^1.39.6",
"dependency-cruiser": "^14.1.0",
"eslint": "^8.50.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.28.1",
"heap-typed": "^1.36.4",
"heap-typed": "^1.39.6",
"istanbul-badges-readme": "^1.8.5",
"jest": "^29.7.0",
"prettier": "^3.0.3",
"ts-jest": "^29.1.1",
"ts-loader": "^9.4.4",
"tsup": "^7.2.0",
"typedoc": "^0.25.1",
"typescript": "^5.2.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4"
"typescript": "^5.2.2"
},
"keywords": [
"data",

5
scripts/config.json Normal file
View file

@ -0,0 +1,5 @@
{
"sourceDir": "/Users/revone/projects/data-structure-typed",
"individualsDir": "/Users/revone/projects/data-structure-typed-individuals",
"docsDir": "/Users/revone/projects/data-structure-typed-docs"
}

View file

@ -0,0 +1,45 @@
#!/bin/bash
# Enable extended globbing
shopt -s extglob
# target directory
individuals_dir_default=$(jq -r .individualsDir ./scripts/config.json)
# Source directory
source_dir_default=$(jq -r .sourceDir ./scripts/config.json)
# List of directories
directories=(
"avl-tree-typed"
"binary-tree-typed"
"bst-typed"
"deque-typed"
"directed-graph-typed"
"doubly-linked-list-typed"
"graph-typed"
"heap-typed"
"linked-list-typed"
"max-heap-typed"
"max-priority-queue-typed"
"min-heap-typed"
"min-priority-queue-typed"
"priority-queue-typed"
"singly-linked-list-typed"
"queue-typed"
"stack-typed"
"tree-multiset-typed"
"trie-typed"
"undirected-graph-typed"
)
# Loop through each directory
for dir in "${directories[@]}"; do
# Delete all files except index.ts
find "$individuals_dir_default"/"$dir"/src -type f ! -name 'index.ts' -delete
# Copy the files to the target directory, excluding index.ts
cp -R "$source_dir_default"/src/!(index.ts) "$individuals_dir_default"/"$dir"/src
done
echo "All packages copied."

View file

@ -1,5 +1,10 @@
#!/bin/bash
# Read the version variable from config.json
source_dir_default=$(jq -r .sourceDir ./scripts/config.json)
individuals_dir_default=$(jq -r .individualsDir ./scripts/config.json)
# Function to prompt for a directory path with a default value
prompt_for_directory() {
local message="$1"
@ -9,10 +14,12 @@ prompt_for_directory() {
}
# Prompt for the source directory path
source_dir=$(prompt_for_directory "Enter the source directory" "/Users/revone/projects/data-structure-typed")
#source_dir=$(prompt_for_directory "Enter the source directory" "$source_dir_default")
source_dir="$source_dir_default"
# Prompt for the destination directory path
individuals_dir=$(prompt_for_directory "Enter the destination directory" "/Users/revone/projects/data-structure-typed-individuals")
#individuals_dir=$(prompt_for_directory "Enter the destination directory" "$individuals_dir_default")
individuals_dir="$individuals_dir_default"
# Read the version variable from package.json

View file

@ -1,5 +1,11 @@
#!/bin/bash
# Read the version variable from config.json
source_dir_default=$(jq -r .sourceDir ./scripts/config.json)
docs_dir_default=$(jq -r .docsDir ./scripts/config.json)
# Function to prompt for a directory path with a default value
prompt_for_directory() {
local message="$1"
@ -9,10 +15,12 @@ prompt_for_directory() {
}
# Prompt for the source directory path
source_dir=$(prompt_for_directory "Enter the source directory" "/Users/revone/projects/data-structure-typed")
#source_dir=$(prompt_for_directory "Enter the source directory" "$source_dir_default")
source_dir="$source_dir_default"
# Prompt for the destination directory path
docs_dir=$(prompt_for_directory "Enter the destination directory" "/Users/revone/projects/data-structure-typed-docs")
# Prompt for the docs directory path
#docs_dir=$(prompt_for_directory "Enter the docs directory" "$docs_dir_default")
docs_dir="$docs_dir_default"
# Check if jq is installed and install it if needed
if ! command -v jq &> /dev/null; then

View file

@ -6,22 +6,22 @@
* @license MIT License
*/
import {BST, BSTNode} from './bst';
import type {AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeletedResult, BinaryTreeNodeKey} from '../../types';
import type {AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeletedResult, BTNKey} from '../../types';
import {BTNCallback} from '../../types';
import {IBinaryTree} from '../../interfaces';
export class AVLTreeNode<V = any, FAMILY extends AVLTreeNode<V, FAMILY> = AVLTreeNodeNested<V>> extends BSTNode<
V,
FAMILY
> {
export class AVLTreeNode<V = any, N extends AVLTreeNode<V, N> = AVLTreeNodeNested<V>> extends BSTNode<V, N> {
height: number;
constructor(key: BinaryTreeNodeKey, val?: V) {
super(key, val);
constructor(key: BTNKey, value?: V) {
super(key, value);
this.height = 0;
}
}
export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends BST<N> implements IBinaryTree<N> {
export class AVLTree<V = any, N extends AVLTreeNode<V, N> = AVLTreeNode<V, AVLTreeNodeNested<V>>>
extends BST<V, N>
implements IBinaryTree<V, N> {
/**
* This is a constructor function for an AVL tree data structure in TypeScript.
* @param {AVLTreeOptions} [options] - The `options` parameter is an optional object that can be passed to the
@ -33,66 +33,51 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The `swapLocation` function swaps the location of two nodes in a binary tree.
* @param {N} srcNode - The source node that you want to swap with the destination node.
* @param {N} destNode - The `destNode` parameter represents the destination node where the values from `srcNode` will
* be swapped to.
* @returns The `destNode` is being returned.
*/
override swapLocation(srcNode: N, destNode: N): N {
const {key, val, height} = destNode;
const tempNode = this.createNode(key, val);
if (tempNode) {
tempNode.height = height;
destNode.key = srcNode.key;
destNode.val = srcNode.val;
destNode.height = srcNode.height;
srcNode.key = tempNode.key;
srcNode.val = tempNode.val;
srcNode.height = tempNode.height;
}
return destNode;
}
/**
* The function creates a new AVL tree node with the given key and value.
* @param {BinaryTreeNodeKey} key - The `key` parameter is the identifier for the binary tree node. It is used to uniquely
* identify each node in the tree.
* @param [val] - The `val` parameter is an optional value that can be assigned to the node. It represents the value
* that will be stored in the node.
* The function creates a new AVL tree node with the specified key and value.
* @param {BTNKey} key - The key parameter is the key value that will be associated with
* the new node. It is used to determine the position of the node in the binary search tree.
* @param [value] - The parameter `value` is an optional value that can be assigned to the node. It is of
* type `V`, which means it can be any value that is assignable to the `value` property of the
* node type `N`.
* @returns a new AVLTreeNode object with the specified key and value.
*/
override createNode(key: BinaryTreeNodeKey, val?: N['val']): N {
return new AVLTreeNode<N['val'], N>(key, val) as N;
override createNode(key: BTNKey, value?: V): N {
return new AVLTreeNode<V, N>(key, value) as N;
}
/**
* The function overrides the add method of a binary tree node and balances the tree after inserting a new node.
* @param {BinaryTreeNodeKey} key - The `key` parameter is the identifier of the binary tree node that we want to add.
* @param [val] - The `val` parameter is an optional value that can be assigned to the node being added. It is of type
* `N['val']`, which means it should be of the same type as the `val` property of the nodes in the binary tree.
* @returns The method is returning the inserted node, or null or undefined if the insertion was not successful.
* The function overrides the add method of a binary tree node and balances the tree after inserting
* a new node.
* @param {BTNKey | N | null} keyOrNode - The `keyOrNode` parameter can accept either a
* `BTNKey` or a `N` (which represents a node in the binary tree) or `null`.
* @param [value] - The `value` parameter is the value that you want to assign to the new node that you
* are adding to the binary search tree.
* @returns The method is returning the inserted node (`N`), `null`, or `undefined`.
*/
override add(key: BinaryTreeNodeKey, val?: N['val']): N | null | undefined {
// TODO support node as a param
const inserted = super.add(key, val);
override add(keyOrNode: BTNKey | N | null, value?: V): N | null | undefined {
const inserted = super.add(keyOrNode, value);
if (inserted) this._balancePath(inserted);
return inserted;
}
/**
* The function overrides the remove method of a binary tree and performs additional operations to balance the tree after
* deletion.
* @param {BinaryTreeNodeKey} key - The `key` parameter represents the identifier of the binary tree node that needs to be
* removed.
* The function overrides the delete method of a binary tree and balances the tree after deleting a
* node if necessary.
* @param {ReturnType<C>} identifier - The `identifier` parameter is either a
* `BTNKey` or a generic type `N`. It represents the property of the node that we are
* searching for. It can be a specific key value or any other property of the node.
* @param callback - The `callback` parameter is a function that takes a node as input and returns a
* value. This value is compared with the `identifier` parameter to determine if the node should be
* included in the result. The `callback` parameter has a default value of
* `((node: N) => node.key)`
* @returns The method is returning an array of `BinaryTreeDeletedResult<N>` objects.
*/
override remove(key: BinaryTreeNodeKey): BinaryTreeDeletedResult<N>[] {
const deletedResults = super.remove(key);
override delete<C extends BTNCallback<N>>(
identifier: ReturnType<C>,
callback: C = ((node: N) => node.key) as C
): BinaryTreeDeletedResult<N>[] {
if ((identifier as any) instanceof AVLTreeNode) callback = (node => node) as C;
const deletedResults = super.delete(identifier, callback);
for (const {needBalanced} of deletedResults) {
if (needBalanced) {
this._balancePath(needBalanced);
@ -102,10 +87,37 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The balance factor of a given AVL tree node is calculated by subtracting the height of its left subtree from the
* height of its right subtree.
* @param node - The parameter "node" is of type N, which represents a node in an AVL tree.
* @returns The balance factor of the given AVL tree node.
* The function swaps the key, value, and height properties between two nodes in a binary tree.
* @param {N} srcNode - The `srcNode` parameter represents the source node that needs to be swapped
* with the `destNode`.
* @param {N} destNode - The `destNode` parameter represents the destination node where the values
* from the source node (`srcNode`) will be swapped to.
* @returns The method is returning the `destNode` after swapping its properties with the `srcNode`.
*/
protected override _swap(srcNode: N, destNode: N): N {
const {key, value, height} = destNode;
const tempNode = this.createNode(key, value);
if (tempNode) {
tempNode.height = height;
destNode.key = srcNode.key;
destNode.value = srcNode.value;
destNode.height = srcNode.height;
srcNode.key = tempNode.key;
srcNode.value = tempNode.value;
srcNode.height = tempNode.height;
}
return destNode;
}
/**
* The function calculates the balance factor of a node in a binary tree.
* @param {N} node - The parameter "node" represents a node in a binary tree data structure.
* @returns the balance factor of a given node. The balance factor is calculated by subtracting the
* height of the left subtree from the height of the right subtree.
*/
protected _balanceFactor(node: N): number {
if (!node.right)
@ -118,8 +130,9 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The function updates the height of a node in an AVL tree based on the heights of its left and right subtrees.
* @param node - The parameter `node` is an AVLTreeNode object, which represents a node in an AVL tree.
* The function updates the height of a node in a binary tree based on the heights of its left and
* right children.
* @param {N} node - The parameter "node" represents a node in a binary tree data structure.
*/
protected _updateHeight(node: N): void {
if (!node.left && !node.right) node.height = 0;
@ -131,9 +144,10 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The `_balancePath` function balances the AVL tree by performing appropriate rotations based on the balance factor of
* each node in the path from the given node to the root.
* @param node - The `node` parameter is an AVLTreeNode object, which represents a node in an AVL tree.
* 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 {N} node - The `node` parameter in the `_balancePath` function represents the node in the
* AVL tree that needs to be balanced.
*/
protected _balancePath(node: N): void {
const path = this.getPathToRoot(node, false); // first O(log n) + O(log n)
@ -146,7 +160,7 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
// Balance Restoration: If a balance issue is discovered after inserting a node, it requires balance restoration operations. Balance restoration includes four basic cases where rotation operations need to be performed to fix the balance:
switch (
this._balanceFactor(A) // second O(1)
) {
) {
case -2:
if (A && A.left) {
if (this._balanceFactor(A.left) <= 0) {
@ -175,8 +189,8 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The `_balanceLL` function performs a left-left rotation on an AVL tree to balance it.
* @param A - The parameter A is an AVLTreeNode object.
* The function `_balanceLL` performs a left-left rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
*/
protected _balanceLL(A: N): void {
const parentOfA = A.parent;
@ -205,8 +219,8 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The `_balanceLR` function performs a left-right rotation to balance an AVL tree.
* @param A - A is an AVLTreeNode object.
* The `_balanceLR` function performs a left-right rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
*/
protected _balanceLR(A: N): void {
const parentOfA = A.parent;
@ -253,8 +267,8 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The `_balanceRR` function performs a right-right rotation on an AVL tree to balance it.
* @param A - The parameter A is an AVLTreeNode object.
* The function `_balanceRR` performs a right-right rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
*/
protected _balanceRR(A: N): void {
const parentOfA = A.parent;
@ -288,8 +302,8 @@ export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode> extends B
}
/**
* The `_balanceRL` function performs a right-left rotation to balance an AVL tree.
* @param A - A is an AVLTreeNode object.
* The function `_balanceRL` performs a right-left rotation to balance a binary tree.
* @param {N} A - A is a node in a binary tree.
*/
protected _balanceRL(A: N): void {
const parentOfA = A.parent;

View file

@ -5,40 +5,130 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import {getMSB} from '../../utils';
export class BinaryIndexedTree {
protected readonly _freq: number;
protected readonly _max: number;
/**
* The constructor initializes an array with a specified length and fills it with zeros.
* @param {number} n - The parameter `n` represents the size of the array that will be used to store the sum tree. The
* sum tree is a binary tree data structure used to efficiently calculate the sum of a range of elements in an array.
* The size of the sum tree array is `n + 1` because
* The constructor initializes the properties of an object, including default frequency, maximum
* value, a freqMap data structure, the most significant bit, and the count of negative frequencies.
* @param - - `frequency`: The default frequency value. It is optional and has a default
* value of 0.
*/
constructor(n: number) {
this._sumTree = new Array<number>(n + 1).fill(0);
constructor({frequency = 0, max}: { frequency?: number; max: number }) {
this._freq = frequency;
this._max = max;
this._freqMap = {0: 0};
this._msb = getMSB(max);
this._negativeCount = frequency < 0 ? max : 0;
}
private _sumTree: number[];
protected _freqMap: Record<number, number>;
get sumTree(): number[] {
return this._sumTree;
get freqMap(): Record<number, number> {
return this._freqMap;
}
static lowBit(x: number) {
return x & -x;
protected _msb: number;
get msb(): number {
return this._msb;
}
protected _negativeCount: number;
get negativeCount(): number {
return this._negativeCount;
}
get freq(): number {
return this._freq;
}
get max(): number {
return this._max;
}
/**
* The update function updates the values in a binary indexed tree by adding a delta value to the specified index and
* its ancestors.
* @param {number} i - The parameter `i` represents the index of the element in the `_sumTree` array that needs to be
* updated.
* @param {number} delta - The "delta" parameter represents the change in value that needs to be added to the element
* at index "i" in the "_sumTree" array.
* The function "readSingle" reads a single number from a specified index.
* @param {number} index - The `index` parameter is a number that represents the index of an element in a
* collection or array.
* @returns a number.
*/
update(i: number, delta: number) {
while (i < this._sumTree.length) {
this._sumTree[i] += delta;
i += BinaryIndexedTree.lowBit(i);
readSingle(index: number): number {
this._checkIndex(index);
return this._readSingle(index);
}
/**
* The "update" function updates the value at a given index by adding a delta and triggers a callback
* to notify of the change.
* @param {number} position - The `index` parameter represents the index of the element that needs to be
* updated in the data structure.
* @param {number} change - The "delta" parameter represents the change in value that needs to be
* applied to the frequency at the specified index.
*/
update(position: number, change: number): void {
this._checkIndex(position);
const freqCur = this._readSingle(position);
this._update(position, change);
this._updateNegativeCount(freqCur, freqCur + change);
}
/**
* The function "writeSingle" checks the index and writes a single value with a given frequency.
* @param {number} index - The `index` parameter is a number that represents the index of an element. It
* is used to identify the specific element that needs to be written.
* @param {number} freq - The `freq` parameter represents the frequency value that needs to be
* written.
*/
writeSingle(index: number, freq: number): void {
this._checkIndex(index);
this._writeSingle(index, freq);
}
/**
* The read function takes a count parameter, checks if it is an integer, and returns the result of
* calling the _read function with the count parameter clamped between 0 and the maximum value.
* @param {number} count - The `count` parameter is a number that represents the number of items to
* read.
* @returns a number.
*/
read(count: number): number {
if (!Number.isInteger(count)) {
throw new Error('Invalid count');
}
return this._read(Math.max(Math.min(count, this.max), 0));
}
/**
* The function returns the lower bound of a non-descending sequence that sums up to a given number.
* @param {number} sum - The `sum` parameter is a number that represents the target sum that we want
* to find in the sequence.
* @returns The lowerBound function is returning a number.
*/
lowerBound(sum: number): number {
if (this.negativeCount > 0) {
throw new Error('Sequence is not non-descending');
}
return this._binarySearch(sum, (x, y) => x < y);
}
/**
* The upperBound function returns the index of the first element in a sequence that is greater than
* or equal to a given sum.
* @param {number} sum - The "sum" parameter is a number that represents the target sum that we want
* to find in the sequence.
* @returns The upperBound function is returning a number.
*/
upperBound(sum: number): number {
if (this.negativeCount > 0) {
throw new Error('Must not be descending');
}
return this._binarySearch(sum, (x, y) => x <= y);
}
/**
@ -48,29 +138,169 @@ export class BinaryIndexedTree {
* @returns The function `getPrefixSum` returns the prefix sum of the elements in the binary indexed tree up to index
* `i`.
*/
getPrefixSum(i: number) {
getPrefixSum(i: number): number {
this._checkIndex(i);
i++; // Convert to 1-based index
let sum = 0;
while (i > 0) {
sum += this._sumTree[i];
i -= BinaryIndexedTree.lowBit(i);
sum += this._getFrequency(i);
i -= i & -i;
}
return sum;
}
/**
* The function `getRangeSum` calculates the sum of a range of numbers in an array.
* @param {number} start - The start parameter is the starting index of the range for which we want to calculate the
* sum.
* @param {number} end - The "end" parameter represents the ending index of the range for which we want to calculate
* the sum.
* @returns the sum of the elements in the range specified by the start and end indices.
* The function returns the value of a specific index in a freqMap data structure, or a default value if
* the index is not found.
* @param {number} index - The `index` parameter is a number that represents the index of a node in a
* freqMap data structure.
* @returns a number.
*/
getRangeSum(start: number, end: number): number {
if (!(0 <= start && start <= end && end <= this._sumTree.length)) throw 'Index out of bounds';
return this.getPrefixSum(end) - this.getPrefixSum(start);
protected _getFrequency(index: number): number {
if (index in this.freqMap) {
return this.freqMap[index];
}
return this.freq * (index & -index);
}
protected _setSumTree(value: number[]) {
this._sumTree = value;
/**
* The function _updateFrequency adds a delta value to the element at the specified index in the freqMap array.
* @param {number} index - The index parameter is a number that represents the index of the freqMap
* element that needs to be updated.
* @param {number} delta - The `delta` parameter represents the change in value that needs to be
* added to the freqMap at the specified `index`.
*/
protected _updateFrequency(index: number, delta: number): void {
this.freqMap[index] = this._getFrequency(index) + delta;
}
/**
* The function checks if the given index is valid and within the range.
* @param {number} index - The parameter "index" is of type number and represents the index value
* that needs to be checked.
*/
protected _checkIndex(index: number): void {
if (!Number.isInteger(index)) {
throw new Error('Invalid index: Index must be an integer.');
}
if (index < 0 || index >= this.max) {
throw new Error('Index out of range: Index must be within the range [0, this.max).');
}
}
/**
* The function calculates the sum of elements in an array up to a given index using a binary indexed
* freqMap.
* @param {number} index - The `index` parameter is a number that represents the index of an element in a
* data structure.
* @returns a number.
*/
protected _readSingle(index: number): number {
index = index + 1;
let sum = this._getFrequency(index);
const z = index - (index & -index);
index--;
while (index !== z) {
sum -= this._getFrequency(index);
index -= index & -index;
}
return sum;
}
/**
* The function `_updateNegativeCount` updates a counter based on changes in frequency values.
* @param {number} freqCur - The current frequency value.
* @param {number} freqNew - The freqNew parameter represents the new frequency value.
*/
protected _updateNegativeCount(freqCur: number, freqNew: number): void {
if (freqCur < 0 && freqNew >= 0) {
this._negativeCount--;
} else if (freqCur >= 0 && freqNew < 0) {
this._negativeCount++;
}
}
/**
* The `_update` function updates the values in a binary indexed freqMap starting from a given index and
* propagating the changes to its parent nodes.
* @param {number} index - The `index` parameter is a number that represents the index of the element in
* the data structure that needs to be updated.
* @param {number} delta - The `delta` parameter represents the change in value that needs to be
* applied to the elements in the data structure.
*/
protected _update(index: number, delta: number): void {
index = index + 1;
while (index <= this.max) {
this._updateFrequency(index, delta);
index += index & -index;
}
}
/**
* The `_writeSingle` function updates the frequency at a specific index and triggers a callback if
* the frequency has changed.
* @param {number} index - The `index` parameter is a number that represents the index of the element
* being modified or accessed.
* @param {number} freq - The `freq` parameter represents the new frequency value that needs to be
* written to the specified index `index`.
*/
protected _writeSingle(index: number, freq: number): void {
const freqCur = this._readSingle(index);
this._update(index, freq - freqCur);
this._updateNegativeCount(freqCur, freq);
}
/**
* The `_read` function calculates the sum of values in a binary freqMap up to a given count.
* @param {number} count - The `count` parameter is a number that represents the number of elements
* to read from the freqMap.
* @returns the sum of the values obtained from calling the `_getFrequency` method for each index in the
* range from `count` to 1.
*/
protected _read(count: number): number {
let index = count;
let sum = 0;
while (index) {
sum += this._getFrequency(index);
index -= index & -index;
}
return sum;
}
/**
* The function `_binarySearch` performs a binary search to find the largest number that satisfies a given
* condition.
* @param {number} sum - The sum parameter is a number that represents the target sum value.
* @param before - The `before` parameter is a function that takes two numbers `x` and `y` as
* arguments and returns a boolean value. It is used to determine if `x` is less than or equal to
* `y`. The purpose of this function is to compare two numbers and determine their order.
* @returns the value of the variable "left".
*/
protected _binarySearch(sum: number, before: (x: number, y: number) => boolean): number {
let left = 0;
let right = this.msb << 1;
let sumT = sum;
while (right > left + 1) {
const middle = (left + right) >> 1;
const sumM = this._getFrequency(middle);
if (middle <= this.max && before(sumM, sumT)) {
sumT -= sumM;
left = middle;
} else {
right = middle;
}
}
return left;
}
}

File diff suppressed because it is too large Load diff

View file

@ -5,27 +5,26 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type {
BinaryTreeNodeKey,
BinaryTreeNodePropertyName,
BSTComparator,
BSTNodeNested,
BSTOptions
} from '../../types';
import {CP, LoopType} from '../../types';
import type {BSTComparator, BSTNodeNested, BSTOptions, BTNCallback, BTNKey} from '../../types';
import {CP, IterationType} from '../../types';
import {BinaryTree, BinaryTreeNode} from './binary-tree';
import {IBinaryTree} from '../../interfaces';
import {Queue} from '../queue';
export class BSTNode<V = any, FAMILY extends BSTNode<V, FAMILY> = BSTNodeNested<V>> extends BinaryTreeNode<V, FAMILY> {
constructor(key: BinaryTreeNodeKey, val?: V) {
super(key, val);
export class BSTNode<V = any, N extends BSTNode<V, N> = BSTNodeNested<V>> extends BinaryTreeNode<V, N> {
constructor(key: BTNKey, value?: V) {
super(key, value);
}
}
export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N> implements IBinaryTree<N> {
export class BST<V = any, N extends BSTNode<V, N> = BSTNode<V, BSTNodeNested<V>>>
extends BinaryTree<V, N>
implements IBinaryTree<V, N> {
/**
* The constructor function initializes a binary search tree object with an optional comparator function.
* @param {BSTOptions} [options] - An optional object that contains configuration options for the binary search tree.
* The constructor function initializes a binary search tree object with an optional comparator
* function.
* @param {BSTOptions} [options] - An optional object that contains configuration options for the
* binary search tree.
*/
constructor(options?: BSTOptions) {
super(options);
@ -39,39 +38,40 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
/**
* The function creates a new binary search tree node with the given key and value.
* @param {BinaryTreeNodeKey} key - The `key` parameter is the identifier for the binary tree node. It is used to uniquely
* identify each node in the binary tree.
* @param [val] - The `val` parameter is an optional value that can be assigned to the node. It represents the value
* that will be stored in the node.
* @param {BTNKey} key - The key parameter is the key value that will be associated with
* the new node. It is used to determine the position of the node in the binary search tree.
* @param [value] - The parameter `value` is an optional value that can be assigned to the node. It
* represents the value associated with the node in a binary search tree.
* @returns a new instance of the BSTNode class with the specified key and value.
*/
override createNode(key: BinaryTreeNodeKey, val?: N['val']): N {
return new BSTNode<N['val'], N>(key, val) as N;
override createNode(key: BTNKey, value?: V): N {
return new BSTNode<V, N>(key, value) as N;
}
/**
* 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 {BinaryTreeNodeKey | N | null} keyOrNode - The `keyOrNode` parameter can be either a `BinaryTreeNodeKey` 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`.
* The `add` function in a binary search tree class inserts a new node with a given key and value
* into the tree.
* @param {BTNKey | N | null} keyOrNode - The `keyOrNode` parameter can be either a
* `BTNKey` (which can be a number or a string), a `BSTNode` object, or `null`.
* @param [value] - The `value` parameter is the value to be assigned to the new node being added to the
* binary search tree.
* @returns the inserted node (N) if it was successfully added to the binary search tree. If the node
* was not added or if the parameters were invalid, it returns null or undefined.
*/
override add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined {
// TODO support node as a param
override add(keyOrNode: BTNKey | N | null, value?: V): N | null | undefined {
// TODO support node as a parameter
let inserted: N | null = null;
let newNode: N | null = null;
if (keyOrNode instanceof BSTNode) {
newNode = keyOrNode;
} else if (typeof keyOrNode === 'number') {
newNode = this.createNode(keyOrNode, val);
newNode = this.createNode(keyOrNode, value);
} else if (keyOrNode === null) {
newNode = null;
}
if (this.root === null) {
this._setRoot(newNode);
this._setSize(this.size + 1);
this._size = this.size + 1;
inserted = this.root;
} else {
let cur = this.root;
@ -80,7 +80,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
if (cur !== null && newNode !== null) {
if (this._compare(cur.key, newNode.key) === CP.eq) {
if (newNode) {
cur.val = newNode.val;
cur.value = newNode.value;
}
//Duplicates are not accepted.
traversing = false;
@ -93,7 +93,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
//Add to the left of the current node
cur.left = newNode;
this._setSize(this.size + 1);
this._size = this.size + 1;
traversing = false;
inserted = cur.left;
} else {
@ -108,7 +108,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
//Add to the right of the current node
cur.right = newNode;
this._setSize(this.size + 1);
this._size = this.size + 1;
traversing = false;
inserted = cur.right;
} else {
@ -125,41 +125,49 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
/**
* The `addMany` function overrides the base class method to add multiple nodes to a binary search tree in a balanced
* manner.
* @param {[BinaryTreeNodeKey | N , N['val']][]} keysOrNodes - The `keysOrNodes` parameter in the `addMany` function is an array of
* `BinaryTreeNodeKey` 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
* The `addMany` function is used to efficiently add multiple nodes to a binary search tree while
* maintaining balance.
* @param {[BTNKey | N, N['value']][]} keysOrNodes - The `arr` parameter in the `addMany` function
* represents an array of keys or nodes that need to be added to the binary search tree. It can be an
* array of `BTNKey` or `N` (which represents the node type in the binary search tree) or
* `null
* @param {V[]} 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.
* @param iterationType - The `iterationType` parameter determines the type of iteration to be used.
* It can have two possible values:
* @returns The `addMany` function returns an array of `N`, `null`, or `undefined` values.
*/
override addMany(
keysOrNodes: (BinaryTreeNodeKey | null)[] | (N | null)[],
data?: N['val'][],
isBalanceAdd = false
keysOrNodes: (BTNKey | null)[] | (N | null)[],
data?: V[],
isBalanceAdd = true,
iterationType = this.iterationType
): (N | null | undefined)[] {
function hasNoNull(arr: (BinaryTreeNodeKey | null)[] | (N | null)[]): arr is BinaryTreeNodeKey[] | N[] {
// TODO this addMany function is inefficient, it should be optimized
function hasNoNull(arr: (BTNKey | null)[] | (N | null)[]): arr is BTNKey[] | N[] {
return arr.indexOf(null) === -1;
}
if (!isBalanceAdd || !hasNoNull(keysOrNodes)) {
return super.addMany(keysOrNodes, data);
}
const inserted: (N | null | undefined)[] = [];
const combinedArr: [BinaryTreeNodeKey | N, N['val']][] = keysOrNodes.map((value, index) => [value, data?.[index]]);
const combinedArr: [BTNKey | N, N['value']][] = keysOrNodes.map((value, index) => [value, data?.[index]]);
let sorted = [];
function isNodeOrNullTuple(arr: [BinaryTreeNodeKey | N, N['val']][]): arr is [N, N['val']][] {
function isNodeOrNullTuple(arr: [BTNKey | N, N['value']][]): arr is [N, N['value']][] {
for (const [keyOrNode] of arr) if (keyOrNode instanceof BSTNode) return true;
return false;
}
function isBinaryTreeKeyOrNullTuple(
arr: [BinaryTreeNodeKey | N, N['val']][]
): arr is [BinaryTreeNodeKey, N['val']][] {
function isBinaryTreeKeyOrNullTuple(arr: [BTNKey | N, N['value']][]): arr is [BTNKey, N['value']][] {
for (const [keyOrNode] of arr) if (typeof keyOrNode === 'number') return true;
return false;
}
let sortedKeysOrNodes: (number | N | null)[] = [],
sortedData: (N['val'] | undefined)[] | undefined = [];
sortedData: (V | undefined)[] | undefined = [];
if (isNodeOrNullTuple(combinedArr)) {
sorted = combinedArr.sort((a, b) => a[0].key - b[0].key);
@ -169,8 +177,8 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
throw new Error('Invalid input keysOrNodes');
}
sortedKeysOrNodes = sorted.map(([keyOrNode]) => keyOrNode);
sortedData = sorted.map(([, val]) => val);
const recursive = (arr: (BinaryTreeNodeKey | null | N)[], data?: N['val'][]) => {
sortedData = sorted.map(([, value]) => value);
const recursive = (arr: (BTNKey | null | N)[], data?: (V | undefined)[]) => {
if (arr.length === 0) return;
const mid = Math.floor((arr.length - 1) / 2);
@ -196,7 +204,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
}
};
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
recursive(sortedKeysOrNodes, sortedData);
} else {
iterative();
@ -206,73 +214,123 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
/**
* The function returns the first node in a binary tree that matches the given property name and value.
* @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeKey` or a
* generic type `N`. It represents the property of the binary tree node that you want to search for.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property name to use for searching the binary tree nodes. If not provided, it defaults to `'key'`.
* @returns The method is returning either a BinaryTreeNodeKey or N (generic type) or null.
* The function returns the first node in the binary tree that matches the given node property and
* callback.
* @param {ReturnType<C> | N} identifier - The `nodeProperty` parameter is used to specify the
* property of the binary tree node that you want to search for. It can be either a specific key
* value (`BTNKey`) or a custom callback function (`BTNCallback<N>`) that determines
* whether a node matches the desired property.
* @param callback - The `callback` parameter is a function that is used to determine whether a node
* matches the desired property. It takes a node as input and returns a boolean value indicating
* whether the node matches the property or not. If no callback function is provided, the default
* callback function `_defaultCallbackByKey` is used
* @param beginRoot - The `beginRoot` parameter is the starting point for the search. It specifies
* the root node from which the search should begin.
* @param iterationType - The `iterationType` parameter is used to specify the type of iteration to
* be performed when searching for nodes in the binary tree. It can have one of the following values:
* @returns either the first node that matches the given nodeProperty and callback, or null if no
* matching node is found.
*/
<<<<<<< HEAD
override get(nodeProperty: BinaryTreeNodeKey | N, propertyName: BinaryTreeNodePropertyName = 'key'): N | null {
return this.getNodes(nodeProperty, propertyName, true)[0] ?? undefined;
=======
override get<C extends BTNCallback<N>>(
identifier: ReturnType<C> | null,
callback: C = ((node: N) => node.key) as C,
beginRoot = this.root,
iterationType = this.iterationType
): N | null {
return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null;
>>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d
}
/**
* The function returns the key of the rightmost node if the comparison between two values is less than, the key of the
* leftmost node if the comparison is greater than, and the key of the rightmost node otherwise.
* @returns The method `lastKey()` returns the key of the rightmost node in the binary tree if the comparison between
* the values at index 0 and 1 is less than, otherwise it returns the key of the leftmost node. If the comparison is
* equal, it returns the key of the rightmost node. If there are no nodes in the tree, it returns 0.
* The function `lastKey` returns the key of the rightmost node if the comparison result is less
* than, the key of the leftmost node if the comparison result is greater than, and the key of the
* rightmost node otherwise.
* @param {N | null} beginRoot - The `beginRoot` parameter is the starting point for finding the last
* key in a binary tree. It represents the root node of the subtree from which the search for the
* last key should begin. If no specific `beginRoot` is provided, the search will start from the root
* of the entire binary
* @param iterationType - The `iterationType` parameter is used to specify the type of iteration to
* be performed when finding the last key. It determines whether the iteration should be performed in
* pre-order, in-order, or post-order.
* @returns the key of the rightmost node in the binary tree if the comparison result is less than,
* the key of the leftmost node if the comparison result is greater than, and the key of the
* rightmost node otherwise. If no node is found, it returns 0.
*/
lastKey(): BinaryTreeNodeKey {
if (this._compare(0, 1) === CP.lt) return this.getRightMost()?.key ?? 0;
else if (this._compare(0, 1) === CP.gt) return this.getLeftMost()?.key ?? 0;
else return this.getRightMost()?.key ?? 0;
lastKey(beginRoot: N | null = this.root, iterationType = this.iterationType): BTNKey {
if (this._compare(0, 1) === CP.lt) return this.getRightMost(beginRoot, iterationType)?.key ?? 0;
else if (this._compare(0, 1) === CP.gt) return this.getLeftMost(beginRoot, iterationType)?.key ?? 0;
else return this.getRightMost(beginRoot, iterationType)?.key ?? 0;
}
/**
* The function `getNodes` returns an array of nodes in a binary tree that match a given property value.
* @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeKey` or an
* `N` type. It represents the property of the binary tree node that you want to compare with.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property name to use for comparison. If not provided, it defaults to `'key'`.
* @param {boolean} [onlyOne] - The `onlyOne` parameter is an optional boolean parameter that determines whether to
* return only one node that matches the given `nodeProperty` or all nodes that match the `nodeProperty`. If `onlyOne`
* is set to `true`, the function will return an array with only one node (if
* @returns an array of nodes (type N).
* The function `getNodes` retrieves nodes from a binary tree based on a given node property or key,
* using either recursive or iterative traversal.
* @param {ReturnType<C> | N} identifier - The `nodeProperty` parameter represents the property
* of the binary tree node that you want to search for. It can be either a `BTNKey` or a
* generic type `N`.
* @param callback - The `callback` parameter is a function that takes a node as input and returns a
* value. This value is compared with the `nodeProperty` parameter to determine if the node should be
* included in the result. The default value for `callback` is `((node: N) => node.key)`, which is
* a
* @param [onlyOne=false] - A boolean value indicating whether to stop the traversal after finding
* the first node that matches the nodeProperty. If set to true, the function will return an array
* containing only that node. If set to false (default), the function will continue the traversal and
* return an array containing all nodes that match the node
* @param {N | null} beginRoot - The `beginRoot` parameter is the starting node for the traversal. It
* specifies the root node of the binary tree from which the traversal should begin. If `beginRoot`
* is `null`, an empty array will be returned.
* @param iterationType - The `iterationType` parameter determines the type of iteration used to
* traverse the binary tree. It can have one of the following values:
* @returns an array of nodes (N[]).
*/
override getNodes(
nodeProperty: BinaryTreeNodeKey | N,
propertyName: BinaryTreeNodePropertyName = 'key',
onlyOne = false
override getNodes<C extends BTNCallback<N>>(
identifier: ReturnType<C> | null,
callback: C = ((node: N) => node.key) as C,
onlyOne = false,
beginRoot: N | null = this.root,
iterationType = this.iterationType
): N[] {
if (!this.root) return [];
const result: N[] = [];
if (!beginRoot) return [];
const ans: N[] = [];
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return;
const callbackResult = callback(cur);
if (callbackResult === identifier) {
ans.push(cur);
if (onlyOne) return;
}
if (!cur.left && !cur.right) return;
if (propertyName === 'key') {
if (this._compare(cur.key, nodeProperty as number) === CP.gt) cur.left && _traverse(cur.left);
if (this._compare(cur.key, nodeProperty as number) === CP.lt) cur.right && _traverse(cur.right);
// TODO potential bug
if (callback === ((node: N) => node.key)) {
if (this._compare(cur.key, identifier as number) === CP.gt) cur.left && _traverse(cur.left);
if (this._compare(cur.key, identifier as number) === CP.lt) cur.right && _traverse(cur.right);
} else {
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
}
};
_traverse(this.root);
_traverse(beginRoot);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const queue = new Queue<N>([beginRoot]);
while (queue.size > 0) {
const cur = queue.shift();
if (cur) {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return result;
if (propertyName === 'key') {
if (this._compare(cur.key, nodeProperty as number) === CP.gt) cur.left && queue.push(cur.left);
if (this._compare(cur.key, nodeProperty as number) === CP.lt) cur.right && queue.push(cur.right);
const callbackResult = callback(cur);
if (callbackResult === identifier) {
ans.push(cur);
if (onlyOne) return ans;
}
// TODO potential bug
if (callback === ((node: N) => node.key)) {
if (this._compare(cur.key, identifier as number) === CP.gt) cur.left && queue.push(cur.left);
if (this._compare(cur.key, identifier as number) === CP.lt) cur.right && queue.push(cur.right);
} else {
cur.left && queue.push(cur.left);
cur.right && queue.push(cur.right);
@ -281,140 +339,65 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
}
return result;
return ans;
}
// --- 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
* 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.
* The `lesserOrGreaterTraverse` function traverses a binary tree and applies a callback function to
* nodes that have a key value lesser or greater than a target key value.
* @param callback - The `callback` parameter is a function that will be called for each node that
* meets the condition specified by the `lesserOrGreater` parameter. It takes a node as an argument
* and returns a value.
* @param {CP} lesserOrGreater - The `lesserOrGreater` parameter is used to determine whether to
* traverse nodes that are lesser than, greater than, or equal to the `targetNode`. It can take one
* of the following values:
* @param {BTNKey | N | null} targetNode - The `targetNode` parameter in the
* `lesserOrGreaterTraverse` function is used to specify the node from which the traversal should
* start. It can be either a reference to a specific node (`N`), the key of a node
* (`BTNKey`), or `null` to
* @param iterationType - The `iterationType` parameter determines whether the traversal should be
* done recursively or iteratively. It can have two possible values:
* @returns The function `lesserOrGreaterTraverse` returns an array of `ReturnType<BTNCallback<N>>`.
*/
allGreaterNodesAdd(
node: N | BinaryTreeNodeKey | null,
delta: number,
propertyName: BinaryTreeNodePropertyName = 'key'
): boolean {
if (typeof node === 'number') node = this.get(node, 'key');
if (!node) return false;
const key = node.key;
if (!this.root) return false;
lesserOrGreaterTraverse<C extends BTNCallback<N>>(
callback: C = ((node: N) => node.key) as C,
lesserOrGreater: CP = CP.lt,
targetNode: BTNKey | N | null = this.root,
iterationType = this.iterationType
): ReturnType<C>[] {
if (typeof targetNode === 'number') targetNode = this.get(targetNode);
const ans: ReturnType<BTNCallback<N>>[] = [];
if (!targetNode) return ans;
const targetKey = targetNode.key;
if (!this.root) return ans;
const _sumByPropertyName = (cur: N) => {
switch (propertyName) {
case 'key':
cur.key += delta;
break;
default:
cur.key += delta;
break;
}
};
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _traverse = (cur: N) => {
const compared = this._compare(cur.key, key);
if (compared === CP.gt) _sumByPropertyName(cur);
const compared = this._compare(cur.key, targetKey);
if (compared === lesserOrGreater) ans.push(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, targetKey) === lesserOrGreater) _traverse(cur.left);
if (cur.right && this._compare(cur.right.key, targetKey) === lesserOrGreater) _traverse(cur.right);
};
_traverse(this.root);
return true;
return ans;
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const queue = new Queue<N>([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);
const compared = this._compare(cur.key, targetKey);
if (compared === lesserOrGreater) ans.push(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, targetKey) === lesserOrGreater) queue.push(cur.left);
if (cur.right && this._compare(cur.right.key, targetKey) === lesserOrGreater) queue.push(cur.right);
}
}
return true;
return ans;
}
}
@ -429,22 +412,25 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
*/
/**
* The `perfectlyBalance` function takes a binary tree, performs a depth-first search to sort the nodes, and then
* constructs a balanced binary search tree using either a recursive or iterative approach.
* @returns The function `perfectlyBalance()` returns a boolean value.
* The `perfectlyBalance` function balances a binary search tree by adding nodes in a way that
* ensures the tree is perfectly balanced.
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies the
* type of iteration to use when building a balanced binary search tree. It can have two possible
* values:
* @returns The function `perfectlyBalance` returns a boolean value.
*/
perfectlyBalance(): boolean {
const sorted = this.dfs('in', 'node'),
perfectlyBalance(iterationType = this.iterationType): boolean {
const sorted = this.dfs(node => node, 'in'),
n = sorted.length;
this.clear();
if (sorted.length < 1) return false;
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const buildBalanceBST = (l: number, r: number) => {
if (l > r) return;
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add(midNode.key, midNode.val);
this.add(midNode.key, midNode.value);
buildBalanceBST(l, m - 1);
buildBalanceBST(m + 1, r);
};
@ -460,7 +446,7 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
if (l <= r) {
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add(midNode.key, midNode.val);
this.add(midNode.key, midNode.value);
stack.push([m + 1, r]);
stack.push([l, m - 1]);
}
@ -471,15 +457,17 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
}
/**
* The function `isAVLBalanced` checks if a binary tree is balanced according to the AVL tree property.
* The function checks if a binary tree is AVL balanced using either recursive or iterative approach.
* @param iterationType - The `iterationType` parameter is used to determine the method of iteration
* to check if the AVL tree is balanced. It can have two possible values:
* @returns a boolean value.
*/
isAVLBalanced(): boolean {
isAVLBalanced(iterationType = this.iterationType): boolean {
if (!this.root) return true;
let balanced = true;
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const _height = (cur: N | null | undefined): number => {
if (!cur) return 0;
const leftHeight = _height(cur.left),
@ -521,14 +509,14 @@ export class BST<N extends BSTNode<N['val'], N> = BSTNode> extends BinaryTree<N>
protected _comparator: BSTComparator = (a, b) => a - b;
/**
* The function compares two binary tree node IDs using a comparator function and returns whether the first ID is
* greater than, less than, or equal to the second ID.
* @param {BinaryTreeNodeKey} a - "a" is a BinaryTreeNodeKey, which represents the identifier of a binary tree node.
* @param {BinaryTreeNodeKey} b - The parameter "b" in the above code refers to a BinaryTreeNodeKey.
* @returns a value of type CP (ComparisonResult). The possible return values are CP.gt (greater than), CP.lt (less
* than), or CP.eq (equal).
* The function compares two values using a comparator function and returns whether the first value
* is greater than, less than, or equal to the second value.
* @param {BTNKey} a - The parameter "a" is of type BTNKey.
* @param {BTNKey} b - The parameter "b" in the above code represents a BTNKey.
* @returns a value of type CP (ComparisonResult). The possible return values are CP.gt (greater
* than), CP.lt (less than), or CP.eq (equal).
*/
protected _compare(a: BinaryTreeNodeKey, b: BinaryTreeNodeKey): CP {
protected _compare(a: BTNKey, b: BTNKey): CP {
const compared = this._comparator(a, b);
if (compared > 0) return CP.gt;
else if (compared < 0) return CP.lt;

View file

@ -1,36 +1,33 @@
<<<<<<< HEAD
import {BinaryTreeDeletedResult, BinaryTreeNodeKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types';
=======
import {BTNKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types';
>>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d
import {IBinaryTree} from '../../interfaces';
import {BST, BSTNode} from './bst';
export class RBTreeNode<V = any, FAMILY extends RBTreeNode<V, FAMILY> = RBTreeNodeNested<V>> extends BSTNode<
V,
FAMILY
> {
private _color: RBColor;
constructor(key: BinaryTreeNodeKey, val?: V) {
super(key, val);
this._color = RBColor.RED;
export class RBTreeNode<V = any, N extends RBTreeNode<V, N> = RBTreeNodeNested<V>> extends BSTNode<V, N> {
constructor(key: BTNKey, value?: V) {
super(key, value);
this.color = RBColor.RED;
}
get color(): RBColor {
return this._color;
}
color: RBColor;
set color(value: RBColor) {
this._color = value;
}
}
export class RBTree<N extends RBTreeNode<N['val'], N> = RBTreeNode> extends BST<N> implements IBinaryTree<N> {
export class RBTree<V, N extends RBTreeNode<V, N> = RBTreeNode<V, RBTreeNodeNested<V>>>
extends BST<V, N>
implements IBinaryTree<V, N> {
constructor(options?: RBTreeOptions) {
super(options);
}
override createNode(key: BinaryTreeNodeKey, val?: N['val']): N {
return new RBTreeNode(key, val) as N;
override createNode(key: BTNKey, value?: V): N {
return new RBTreeNode(key, value) as N;
}
<<<<<<< HEAD
override add(key: BinaryTreeNodeKey, val?: N['val']): N {
const newNode = this.createNode(key, val);
if (!this.root) {
@ -340,4 +337,338 @@ export class RBTree<N extends RBTreeNode<N['val'], N> = RBTreeNode> extends BST<
return 0;
}
}
=======
// override add(keyOrNode: BTNKey | N | null, value?: V): N | null | undefined {
// const inserted = super.add(keyOrNode, value);
// if (inserted) this._fixInsertViolation(inserted);
// return inserted;
// }
//
// // Method for fixing insert violations in a red-black tree
// protected _fixInsertViolation(node: N) {
// while (node !== this.root! && node.color === RBColor.RED && node.parent!.color === RBColor.RED) {
// const parent = node.parent!;
// const grandparent = parent.parent!;
// let uncle: N | null | undefined = null;
//
// if (parent === grandparent.left) {
// uncle = grandparent.right;
//
// // Case 1: The uncle node is red
// if (uncle && uncle.color === RBColor.RED) {
// grandparent.color = RBColor.RED;
// parent.color = RBColor.BLACK;
// uncle.color = RBColor.BLACK;
// node = grandparent;
// } else {
// // Case 2: The uncle node is black, and the current node is a right child
// if (node === parent.right) {
// this._rotateLeft(parent);
// node = parent;
// // Update parent reference
// node.parent = grandparent;
// parent.parent = node;
// }
//
// // Case 3: The uncle node is black, and the current node is a left child
// parent.color = RBColor.BLACK;
// grandparent.color = RBColor.RED;
// this._rotateRight(grandparent);
// }
// } else {
// // Symmetric case: The parent is the right child of the grandparent
// uncle = grandparent.left;
//
// // Case 1: The uncle node is red
// if (uncle && uncle.color === RBColor.RED) {
// grandparent.color = RBColor.RED;
// parent.color = RBColor.BLACK;
// uncle.color = RBColor.BLACK;
// node = grandparent;
// } else {
// // Case 2: The uncle node is black, and the current node is a left child
// if (node === parent.left) {
// this._rotateRight(parent);
// node = parent;
// // Update parent reference
// node.parent = grandparent;
// parent.parent = node;
// }
//
// // Case 3: The uncle node is black, and the current node is a right child
// parent.color = RBColor.BLACK;
// grandparent.color = RBColor.RED;
// this._rotateLeft(grandparent);
// }
// }
// }
//
// // The root node is always black
// this.root!.color = RBColor.BLACK;
// }
//
// // Left rotation operation
// protected _rotateLeft(node: N) {
// const rightChild = node.right;
// node.right = rightChild!.left;
//
// if (rightChild!.left) {
// rightChild!.left.parent = node;
// }
//
// rightChild!.parent = node.parent;
//
// if (node === this.root) {
// // @ts-ignore
// this._setRoot(rightChild);
// } else if (node === node.parent!.left) {
// node.parent!.left = rightChild;
// } else {
// node.parent!.right = rightChild;
// }
//
// rightChild!.left = node;
// node.parent = rightChild;
// }
//
// // Right rotation operation
// protected _rotateRight(node: N) {
// const leftChild = node.left;
// node.left = leftChild!.right;
//
// if (leftChild!.right) {
// leftChild!.right.parent = node;
// }
//
// leftChild!.parent = node.parent;
//
// if (node === this.root) {
// // @ts-ignore
// this._setRoot(leftChild);
// } else if (node === node.parent!.right) {
// node.parent!.right = leftChild;
// } else {
// node.parent!.left = leftChild;
// }
//
// leftChild!.right = node;
// node.parent = leftChild;
// }
//
// protected _isNodeRed(node: N | null | undefined): boolean {
// return node ? node.color === RBColor.RED : false;
// }
//
// // Find the sibling node
// protected _findSibling(node: N): N | null | undefined {
// if (!node.parent) {
// return undefined;
// }
//
// return node === node.parent.left ? node.parent.right : node.parent.left;
// }
//
// // Remove a node
// protected _removeNode(node: N, replacement: N | null | undefined): void {
// if (node === this.root && !replacement) {
// // If there's only the root node and no replacement, simply delete the root node
// this._setRoot(null);
// } else if (node === this.root || this._isNodeRed(node)) {
// // If the node is the root or a red node, delete it directly
// if (node.parent!.left === node) {
// node.parent!.left = replacement;
// } else {
// node.parent!.right = replacement;
// }
//
// if (replacement) {
// replacement.parent = node.parent!;
// replacement.color = RBColor.BLACK; // Set the replacement node's color to black
// }
// } else {
// // If the node is a black node, perform removal and repair
// const sibling = this._findSibling(node);
//
// if (node.parent!.left === node) {
// node.parent!.left = replacement;
// } else {
// node.parent!.right = replacement;
// }
//
// if (replacement) {
// replacement.parent = node.parent;
// }
//
// if (!this._isNodeRed(sibling)) {
// // If the sibling node is black, perform repair
// this._fixDeleteViolation(replacement || node);
// }
// }
//
// if (node.parent) {
// node.parent = null;
// }
// node.left = null;
// node.right = null;
// }
//
// override delete(keyOrNode: BTNKey | N): BinaryTreeDeletedResult<N>[] {
// const node = this.get(keyOrNode);
// const result: BinaryTreeDeletedResult<N>[] = [{deleted: undefined, needBalanced: null}];
// if (!node) return result; // Node does not exist
//
// const replacement = this._getReplacementNode(node);
//
// const isRed = this._isNodeRed(node);
// const isRedReplacement = this._isNodeRed(replacement);
//
// // Remove the node
// this._removeNode(node, replacement);
//
// if (isRed || isRedReplacement) {
// // If the removed node is red or the replacement node is red, no repair is needed
// return result;
// }
//
// // Repair any violation introduced by the removal
// this._fixDeleteViolation(replacement);
//
// return result;
// }
//
// // Repair operation after node deletion
// protected _fixDeleteViolation(node: N | null | undefined) {
// let sibling;
//
// while (node && node !== this.root && !this._isNodeRed(node)) {
// if (node === node.parent!.left) {
// sibling = node.parent!.right;
//
// if (sibling && this._isNodeRed(sibling)) {
// // Case 1: The sibling node is red
// sibling.color = RBColor.BLACK;
// node.parent!.color = RBColor.RED;
// this._rotateLeft(node.parent!);
// sibling = node.parent!.right;
// }
//
// if (!sibling) return;
//
// if (
// (!sibling.left || sibling.left.color === RBColor.BLACK) &&
// (!sibling.right || sibling.right.color === RBColor.BLACK)
// ) {
// // Case 2: The sibling node and its children are all black
// sibling.color = RBColor.RED;
// node = node.parent!;
// } else {
// if (!(sibling.right && this._isNodeRed(sibling.right))) {
// // Case 3: The sibling node is black, and the left child is red, the right child is black
// sibling.left!.color = RBColor.BLACK;
// sibling.color = RBColor.RED;
// this._rotateRight(sibling);
// sibling = node.parent!.right;
// }
//
// // Case 4: The sibling node is black, and the right child is red
// if (sibling) {
// sibling.color = node.parent!.color;
// }
// if (node.parent) {
// node.parent.color = RBColor.BLACK;
// }
// if (sibling!.right) {
// sibling!.right.color = RBColor.BLACK;
// }
// this._rotateLeft(node.parent!);
// node = this.root;
// }
// } else {
// // Symmetric case: The parent is the right child of the grandparent
// sibling = node.parent!.left;
//
// if (sibling && this._isNodeRed(sibling)) {
// // Case 1: The sibling node is red
// sibling.color = RBColor.BLACK;
// node.parent!.color = RBColor.RED;
// this._rotateRight(node.parent!);
// sibling = node.parent!.left;
// }
//
// if (!sibling) return;
//
// if (
// (!sibling.left || sibling.left.color === RBColor.BLACK) &&
// (!sibling.right || sibling.right.color === RBColor.BLACK)
// ) {
// // Case 2: The sibling node and its children are all black
// sibling.color = RBColor.RED;
// node = node.parent!;
// } else {
// if (!(sibling.left && this._isNodeRed(sibling.left))) {
// // Case 3: The sibling node is black, and the right child is red, the left child is black
// sibling.right!.color = RBColor.BLACK;
// sibling.color = RBColor.RED;
// this._rotateLeft(sibling);
// sibling = node.parent!.left;
// }
//
// // Case 4: The sibling node is black, and the left child is red
// if (sibling) {
// sibling.color = node.parent!.color;
// }
// if (node.parent) {
// node.parent.color = RBColor.BLACK;
// }
// if (sibling!.left) {
// sibling!.left.color = RBColor.BLACK;
// }
// this._rotateRight(node.parent!);
// node = this.root;
// }
// }
// }
//
// if (node) {
// node.color = RBColor.BLACK;
// }
// }
//
// protected _findMin(node: N): N {
// while (node.left) {
// node = node.left;
// }
// return node;
// }
//
// // Get the replacement node
// protected _getReplacementNode(node: N): N | null | undefined {
// if (node.left && node.right) {
// return this._findSuccessor(node);
// }
//
// if (!node.left && !node.right) {
// return null; // Return a fake node with color black
// }
//
// return node.left || node.right;
// }
//
// // Find the successor node
// protected _findSuccessor(node: N): N | null | undefined {
// if (node.right) {
// // If the node has a right child, find the minimum node in the right subtree as the successor
// return this._findMin(node.right);
// }
//
// // Otherwise, traverse upward until finding the first parent whose left child is the current node
// let parent = node.parent;
// while (parent && node === parent.right) {
// node = parent;
// parent = parent.parent;
// }
//
// return parent;
// }
>>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d
}

View file

@ -9,70 +9,18 @@
import type {SegmentTreeNodeVal} from '../../types';
export class SegmentTreeNode {
constructor(start: number, end: number, sum: number, val?: SegmentTreeNodeVal | null) {
this._start = start;
this._end = end;
this._sum = sum;
this._val = val || null;
}
start = 0;
end = 0;
value: SegmentTreeNodeVal | null = null;
sum = 0;
left: SegmentTreeNode | null = null;
right: SegmentTreeNode | null = null;
private _start = 0;
get start(): number {
return this._start;
}
set start(v: number) {
this._start = v;
}
private _end = 0;
get end(): number {
return this._end;
}
set end(v: number) {
this._end = v;
}
private _val: SegmentTreeNodeVal | null = null;
get val(): SegmentTreeNodeVal | null {
return this._val;
}
set val(v: SegmentTreeNodeVal | null) {
this._val = v;
}
private _sum = 0;
get sum(): number {
return this._sum;
}
set sum(v: number) {
this._sum = v;
}
private _left: SegmentTreeNode | null = null;
get left(): SegmentTreeNode | null {
return this._left;
}
set left(v: SegmentTreeNode | null) {
this._left = v;
}
private _right: SegmentTreeNode | null = null;
get right(): SegmentTreeNode | null {
return this._right;
}
set right(v: SegmentTreeNode | null) {
this._right = v;
constructor(start: number, end: number, sum: number, value?: SegmentTreeNodeVal | null) {
this.start = start;
this.end = end;
this.sum = sum;
this.value = value || null;
}
}
@ -101,24 +49,25 @@ export class SegmentTree {
}
}
private _values: number[] = [];
protected _values: number[] = [];
get values(): number[] {
return this._values;
}
private _start = 0;
protected _start = 0;
get start(): number {
return this._start;
}
private _end: number;
protected _end: number;
get end(): number {
return this._end;
}
private _root: SegmentTreeNode | null;
protected _root: SegmentTreeNode | null;
get root(): SegmentTreeNode | null {
return this._root;
@ -154,30 +103,30 @@ export class SegmentTree {
* updated.
* @param {number} sum - The `sum` parameter represents the new value that should be assigned to the `sum` property of
* the `SegmentTreeNode` at the specified `index`.
* @param {SegmentTreeNodeVal} [val] - The `val` parameter is an optional value that can be assigned to the `val`
* @param {SegmentTreeNodeVal} [value] - The `value` parameter is an optional value that can be assigned to the `value`
* property of the `SegmentTreeNode` object. It is not currently used in the code, but you can uncomment the line `//
* cur.val = val;` and pass a value for `val` in the
* cur.value = value;` and pass a value for `value` in the
* @returns The function does not return anything.
*/
updateNode(index: number, sum: number, val?: SegmentTreeNodeVal) {
updateNode(index: number, sum: number, value?: SegmentTreeNodeVal) {
const root = this.root || null;
if (!root) {
return;
}
const dfs = (cur: SegmentTreeNode, index: number, sum: number, val?: SegmentTreeNodeVal) => {
const dfs = (cur: SegmentTreeNode, index: number, sum: number, value?: SegmentTreeNodeVal) => {
if (cur.start === cur.end && cur.start === index) {
cur.sum = sum;
if (val !== undefined) cur.val = val;
if (value !== undefined) cur.value = value;
return;
}
const mid = cur.start + Math.floor((cur.end - cur.start) / 2);
if (index <= mid) {
if (cur.left) {
dfs(cur.left, index, sum, val);
dfs(cur.left, index, sum, value);
}
} else {
if (cur.right) {
dfs(cur.right, index, sum, val);
dfs(cur.right, index, sum, value);
}
}
if (cur.left && cur.right) {
@ -185,7 +134,7 @@ export class SegmentTree {
}
};
dfs(root, index, sum, val);
dfs(root, index, sum, value);
}
/**
@ -238,20 +187,4 @@ export class SegmentTree {
};
return dfs(root, indexA, indexB);
}
protected _setValues(value: number[]) {
this._values = value;
}
protected _setStart(value: number) {
this._start = value;
}
protected _setEnd(value: number) {
this._end = value;
}
protected _setRoot(v: SegmentTreeNode | null) {
this._root = v;
}
}

View file

@ -5,40 +5,39 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type {BinaryTreeNodeKey, TreeMultisetNodeNested, TreeMultisetOptions} from '../../types';
import {BinaryTreeDeletedResult, CP, DFSOrderPattern, FamilyPosition, LoopType} from '../../types';
import type {BTNKey, TreeMultisetNodeNested, TreeMultisetOptions} from '../../types';
import {BinaryTreeDeletedResult, BTNCallback, CP, FamilyPosition, IterationType} from '../../types';
import {IBinaryTree} from '../../interfaces';
import {AVLTree, AVLTreeNode} from './avl-tree';
export class TreeMultisetNode<
V = any,
FAMILY extends TreeMultisetNode<V, FAMILY> = TreeMultisetNodeNested<V>
> extends AVLTreeNode<V, FAMILY> {
N extends TreeMultisetNode<V, N> = TreeMultisetNodeNested<V>
> extends AVLTreeNode<V, N> {
count: number;
/**
* The constructor function initializes a BinaryTreeNode object with a key, value, and count.
* @param {BinaryTreeNodeKey} key - The `key` parameter is of type `BinaryTreeNodeKey` and represents the unique identifier
* @param {BTNKey} key - The `key` parameter is of type `BTNKey` and represents the unique identifier
* of the binary tree node.
* @param {V} [val] - The `val` parameter is an optional parameter of type `V`. It represents the value of the binary
* @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: BinaryTreeNodeKey, val?: V, count = 1) {
super(key, val);
constructor(key: BTNKey, value?: V, count = 1) {
super(key, value);
this.count = count;
}
count: number;
}
/**
* The only distinction between a TreeMultiset and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters.
*/
export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultisetNode>
extends AVLTree<N>
implements IBinaryTree<N>
{
export class TreeMultiset<V = any, N extends TreeMultisetNode<V, N> = TreeMultisetNode<V, TreeMultisetNodeNested<V>>>
extends AVLTree<V, N>
implements IBinaryTree<V, N> {
/**
* The constructor function for a TreeMultiset class in TypeScript, which extends another class and sets an option to
* merge duplicated values.
@ -57,67 +56,43 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
/**
* The function creates a new BSTNode with the given key, value, and count.
* @param {BinaryTreeNodeKey} key - The key parameter is the unique identifier for the binary tree node. It is used to
* @param {BTNKey} key - The key parameter is the unique identifier for the binary tree node. It is used to
* distinguish one node from another in the tree.
* @param {N} val - The `val` parameter represents the value that will be stored in the binary search tree node.
* @param {N} value - The `value` parameter represents the value that will be stored in the binary search tree node.
* @param {number} [count] - The "count" parameter is an optional parameter of type number. It represents the number of
* occurrences of the value in the binary search tree node. If not provided, the count will default to 1.
* @returns A new instance of the BSTNode class with the specified key, value, and count (if provided).
*/
override createNode(key: BinaryTreeNodeKey, val?: N['val'], count?: number): N {
return new TreeMultisetNode(key, val, count) as N;
override createNode(key: BTNKey, value?: V, count?: number): N {
return new TreeMultisetNode(key, value, count) as N;
}
/**
* The function swaps the location of two nodes in a tree data structure.
* @param {N} srcNode - The source node that we want to swap with the destination node.
* @param {N} destNode - The `destNode` parameter represents the destination node where the values from `srcNode` will
* be swapped with.
* @returns the `destNode` after swapping its values with the `srcNode`.
* The `add` function adds a new node to a binary search tree, updating the count if the key already
* exists, and balancing the tree if necessary.
* @param {BTNKey | N | null} keyOrNode - The `keyOrNode` parameter can be either a
* `BTNKey` (which represents the key of the node to be added), a `N` (which represents a
* node to be added), or `null` (which represents a null node).
* @param [value] - The `value` parameter represents the value associated with the key that is being
* added to the binary tree.
* @param [count=1] - The `count` parameter represents the number of occurrences of the key/value
* pair that will be added to the binary tree. It has a default value of 1, which means that if no
* count is specified, the default count will be 1.
* @returns The function `add` returns a value of type `N | null | undefined`.
*/
override swapLocation(srcNode: N, destNode: N): N {
const {key, val, count, height} = destNode;
const tempNode = this.createNode(key, val, count);
if (tempNode) {
tempNode.height = height;
destNode.key = srcNode.key;
destNode.val = srcNode.val;
destNode.count = srcNode.count;
destNode.height = srcNode.height;
srcNode.key = tempNode.key;
srcNode.val = tempNode.val;
srcNode.count = tempNode.count;
srcNode.height = tempNode.height;
}
return destNode;
}
/**
* The `add` function adds a new node to a binary search tree, maintaining the tree's properties and balancing if
* necessary.
* @param {BinaryTreeNodeKey | N} keyOrNode - The `keyOrNode` parameter can be either a `BinaryTreeNodeKey` or a `N` (which
* represents a `BinaryTreeNode`).
* @param [val] - The `val` parameter represents the value to be added to the binary tree node.
* @param {number} [count] - The `count` parameter is an optional parameter that specifies the number of times the
* value should be added to the binary tree. If the `count` parameter is not provided, it defaults to 1.
* @returns The method `add` returns either the inserted node (`N`), `null`, or `undefined`.
*/
override add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val'], count = 1): N | null | undefined {
override add(keyOrNode: BTNKey | N | null, value?: V, count = 1): N | null | undefined {
let inserted: N | null | undefined = undefined,
newNode: N | null;
if (keyOrNode instanceof TreeMultisetNode) {
newNode = this.createNode(keyOrNode.key, keyOrNode.val, keyOrNode.count);
newNode = this.createNode(keyOrNode.key, keyOrNode.value, keyOrNode.count);
} else if (keyOrNode === null) {
newNode = null;
} else {
newNode = this.createNode(keyOrNode, val, count);
newNode = this.createNode(keyOrNode, value, count);
}
if (!this.root) {
this._setRoot(newNode);
this._setSize(this.size + 1);
this._size = this.size + 1;
newNode && this._setCount(this.count + newNode.count);
inserted = this.root;
} else {
@ -127,7 +102,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
if (cur) {
if (newNode) {
if (this._compare(cur.key, newNode.key) === CP.eq) {
cur.val = newNode.val;
cur.value = newNode.value;
cur.count += newNode.count;
this._setCount(this.count + newNode.count);
traversing = false;
@ -137,7 +112,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
if (cur.left === undefined) {
//Add to the left of the current node
cur.left = newNode;
this._setSize(this.size + 1);
this._size = this.size + 1;
this._setCount(this.count + newNode.count);
traversing = false;
@ -151,7 +126,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
if (cur.right === undefined) {
//Add to the right of the current node
cur.right = newNode;
this._setSize(this.size + 1);
this._size = this.size + 1;
this._setCount(this.count + newNode.count);
traversing = false;
@ -174,20 +149,19 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
/**
* The function adds a new node to a binary tree if there is an available slot on the left or right side of the parent
* node.
* @param {N | null} newNode - The `newNode` parameter represents the node that needs to be added to the tree. It can
* be either a node object (`N`) or `null`.
* @param {N} parent - The `parent` parameter represents the parent node to which the new node will be added as a
* child.
* @returns The method returns either the `parent.left`, `parent.right`, or `undefined`.
* The function adds a new node to a binary tree if there is an available slot in the parent node.
* @param {N | null} newNode - The `newNode` parameter represents the node that needs to be added to
* the tree. It can be either a node object (`N`) or `null`.
* @param {N} parent - The `parent` parameter represents the parent node to which the new node will
* be added as a child.
* @returns The method `_addTo` returns either the `parent.left`, `parent.right`, or `undefined`.
*/
override _addTo(newNode: N | null, parent: N): N | null | undefined {
if (parent) {
if (parent.left === undefined) {
parent.left = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._size = this.size + 1;
this._setCount(this.count + newNode.count);
}
@ -195,7 +169,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
} else if (parent.right === undefined) {
parent.right = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._size = (this.size + 1);
this._setCount(this.count + newNode.count);
}
return parent.right;
@ -208,31 +182,28 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
/**
* The `addMany` function takes an array of node IDs or nodes and adds them to the tree multiset, returning an array of
* the inserted nodes.
* @param {(BinaryTreeNodeKey | null)[] | (N | null)[]} keysOrNodes - An array of BinaryTreeNodeKey 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 being added. It is used when adding nodes using the `keyOrNode` and `data` arguments in the `this.add()`
* method. If provided, the `data` array should
* The `addMany` function adds multiple keys or nodes to a TreeMultiset and returns an array of the
* inserted nodes.
* @param {(BTNKey | null)[] | (N | null)[]} keysOrNodes - An array of keys or nodes to be
* added to the multiset. Each element can be either a BTNKey or a TreeMultisetNode.
* @param {V[]} [data] - The `data` parameter is an optional array of values that correspond
* to the keys or nodes being added to the multiset. It is used to associate additional data with
* each key or node.
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
override addMany(
keysOrNodes: (BinaryTreeNodeKey | null)[] | (N | null)[],
data?: N['val'][]
): (N | null | undefined)[] {
override addMany(keysOrNodes: (BTNKey | null)[] | (N | null)[], data?: V[]): (N | null | undefined)[] {
const inserted: (N | null | undefined)[] = [];
for (let i = 0; i < keysOrNodes.length; i++) {
const keyOrNode = keysOrNodes[i];
if (keyOrNode instanceof TreeMultisetNode) {
inserted.push(this.add(keyOrNode.key, keyOrNode.val, keyOrNode.count));
inserted.push(this.add(keyOrNode.key, keyOrNode.value, keyOrNode.count));
continue;
}
if (keyOrNode === null) {
inserted.push(this.add(NaN, null, 0));
inserted.push(this.add(NaN, undefined, 0));
continue;
}
@ -242,23 +213,26 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
/**
* The `perfectlyBalance` function takes a binary tree, performs a depth-first search to sort the nodes, and then
* constructs a balanced binary search tree using either a recursive or iterative approach.
* @returns The function `perfectlyBalance()` returns a boolean value.
* The `perfectlyBalance` function in TypeScript takes a sorted array of nodes and builds a balanced
* binary search tree using either a recursive or iterative approach.
* @param iterationType - The `iterationType` parameter is an optional parameter that specifies the
* type of iteration to use when building a balanced binary search tree. It can have two possible
* values:
* @returns a boolean value.
*/
override perfectlyBalance(): boolean {
const sorted = this.dfs('in', 'node'),
override perfectlyBalance(iterationType = this.iterationType): boolean {
const sorted = this.dfs(node => node, 'in'),
n = sorted.length;
if (sorted.length < 1) return false;
this.clear();
if (this.loopType === LoopType.RECURSIVE) {
if (iterationType === IterationType.RECURSIVE) {
const buildBalanceBST = (l: number, r: number) => {
if (l > r) return;
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add(midNode.key, midNode.val, midNode.count);
this.add(midNode.key, midNode.value, midNode.count);
buildBalanceBST(l, m - 1);
buildBalanceBST(m + 1, r);
};
@ -274,7 +248,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
if (l <= r) {
const m = l + Math.floor((r - l) / 2);
const midNode = sorted[m];
this.add(midNode.key, midNode.val, midNode.count);
this.add(midNode.key, midNode.value, midNode.count);
stack.push([m + 1, r]);
stack.push([l, m - 1]);
}
@ -285,19 +259,30 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
/**
* The `remove` function removes a node from a binary search tree and returns the deleted node along with the parent
* node that needs to be balanced.
* @param {N | BinaryTreeNodeKey | null} nodeOrKey - The `nodeOrKey` parameter can be one of the following:
* @param {boolean} [ignoreCount] - The `ignoreCount` parameter is an optional boolean parameter that determines
* whether to ignore the count of the node being removed. If `ignoreCount` is set to `true`, the count of the node will
* not be taken into account when removing it. If `ignoreCount` is set to `false
* @returns The function `remove` returns an array of `BinaryTreeDeletedResult<N>` objects.
* The `delete` function in a binary search tree deletes a node from the tree and returns the deleted
* node along with the parent node that needs to be balanced.
* @param {ReturnType<C>} identifier - The `identifier` parameter is either a
* `BTNKey` or a generic type `N`. It represents the property of the node that we are
* searching for. It can be a specific key value or any other property of the node.
* @param callback - The `callback` parameter is a function that takes a node as input and returns a
* value. This value is compared with the `identifier` parameter to determine if the node should be
* included in the result. The `callback` parameter has a default value of
* `((node: N) => node.key)`
* @param [ignoreCount=false] - A boolean flag indicating whether to ignore the count of the node
* being deleted. If set to true, the count of the node will not be considered and the node will be
* deleted regardless of its count. If set to false (default), the count of the node will be
* decremented by 1 and
* @returns The method `delete` returns an array of `BinaryTreeDeletedResult<N>` objects.
*/
override remove(nodeOrKey: N | BinaryTreeNodeKey, ignoreCount = false): BinaryTreeDeletedResult<N>[] {
override delete<C extends BTNCallback<N>>(
identifier: ReturnType<C>,
callback: C = ((node: N) => node.key) as C,
ignoreCount = false
): BinaryTreeDeletedResult<N>[] {
const bstDeletedResult: BinaryTreeDeletedResult<N>[] = [];
if (!this.root) return bstDeletedResult;
const curr: N | null = this.get(nodeOrKey);
const curr: N | null = this.get(identifier, callback);
if (!curr) return bstDeletedResult;
const parent: N | null = curr?.parent ? curr.parent : null;
@ -324,7 +309,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
const leftSubTreeRightMost = curr.left ? this.getRightMost(curr.left) : null;
if (leftSubTreeRightMost) {
const parentOfLeftSubTreeMax = leftSubTreeRightMost.parent;
orgCurrent = this.swapLocation(curr, leftSubTreeRightMost);
orgCurrent = this._swap(curr, leftSubTreeRightMost);
if (parentOfLeftSubTreeMax) {
if (parentOfLeftSubTreeMax.right === leftSubTreeRightMost) {
parentOfLeftSubTreeMax.right = leftSubTreeRightMost.left;
@ -335,7 +320,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
}
}
this._setSize(this.size - 1);
this._size = this.size - 1;
// TODO How to handle when the count of target node is lesser than current node's count
this._setCount(this.count - orgCurrent.count);
}
@ -350,328 +335,7 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
/**
* 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.
* @param {BinaryTreeNodeKey | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeKey` or a
* `N`. It represents the property of the nodes that you want to search for.
* @param {boolean} [onlyOne] - The `onlyOne` parameter is an optional boolean parameter that determines whether to
* return only one node that matches the `nodeProperty` or all nodes that match the `nodeProperty`. If `onlyOne` is set
* to `true`, the function will return only one node. If `onlyOne`
* @returns an array of nodes that match the given nodeProperty.
*/
getNodesByCount(nodeProperty: BinaryTreeNodeKey | N, onlyOne = false): N[] {
if (!this.root) return [];
const result: N[] = [];
if (this.loopType === LoopType.RECURSIVE) {
const _traverse = (cur: N) => {
if (cur.count === nodeProperty) {
result.push(cur);
if (onlyOne) return;
}
if (!cur.left && !cur.right) return;
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
};
_traverse(this.root);
} else {
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
if (cur.count === nodeProperty) {
result.push(cur);
if (onlyOne) return result;
}
cur.left && queue.push(cur.left);
cur.right && queue.push(cur.right);
}
}
}
return result;
}
/**
* The BFSCount function returns an array of counts from a breadth-first search of nodes.
* @returns The BFSCount() function returns an array of numbers, specifically the count property of each node in the
* bfs traversal.
*/
BFSCount(): number[] {
const nodes = super.bfs('node');
return nodes.map(node => node.count);
}
/**
* The function "listLevelsCount" takes a node and returns an array of arrays, where each inner array contains the
* count property of each node at that level.
* @param {N | null} node - The parameter `node` is of type `N | null`. This means that it can either be an instance of
* the class `N` or `null`.
* @returns a 2D array of numbers. Each inner array represents a level in the binary tree, and each number in the inner
* array represents the count property of a node in that level.
*/
listLevelsCount(node: N | null): number[][] {
const levels = super.listLevels(node, 'node');
return levels.map(level => level.map(node => node.count));
}
/**
* The `morrisCount` function returns an array of counts for each node in a binary tree, based on a specified traversal
* pattern.
* @param {'in' | 'pre' | 'post'} [pattern] - The `pattern` parameter is an optional parameter that specifies the
* traversal pattern for the Morris traversal algorithm. It can have one of three values: 'in', 'pre', or 'post'.
* @returns The function `morrisCount` returns an array of numbers.
*/
morrisCount(pattern: DFSOrderPattern = 'in'): number[] {
const nodes = super.morris(pattern, 'node');
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'.
* @returns The dfsCountIterative function returns an array of numbers, which represents the count property of each node
* in the dfs traversal.
*/
dfsCountIterative(pattern: DFSOrderPattern = 'in'): number[] {
const nodes = super.dfsIterative(pattern, 'node');
return nodes.map(node => node.count);
}
/**
* The dfsCount function returns an array of counts for each node in a depth-first search traversal.
* @param {DFSOrderPattern} [pattern] - The pattern parameter is an optional parameter that specifies the order in which
* the Depth-First Search (dfs) algorithm should traverse the nodes. It can have one of the following values:
* @returns The dfsCount function returns an array of numbers, specifically the count property of each node in the dfs
* traversal.
*/
dfsCount(pattern: DFSOrderPattern = 'in'): number[] {
const nodes = super.dfs(pattern, 'node');
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.
* The clear() function clears the contents of a data structure and sets the count to zero.
*/
clear() {
super.clear();
@ -679,7 +343,34 @@ export class TreeMultiset<N extends TreeMultisetNode<N['val'], N> = TreeMultiset
}
/**
* The function "_setCount" is used to set the value of the "_count" property.
* The function swaps the values of two nodes in a binary tree.
* @param {N} srcNode - The source node that needs to be swapped with the destination node.
* @param {N} destNode - The `destNode` parameter represents the destination node where the values
* from `srcNode` will be swapped into.
* @returns The method is returning the `destNode` after swapping its properties with the `srcNode`.
*/
protected override _swap(srcNode: N, destNode: N): N {
const {key, value, count, height} = destNode;
const tempNode = this.createNode(key, value, count);
if (tempNode) {
tempNode.height = height;
destNode.key = srcNode.key;
destNode.value = srcNode.value;
destNode.count = srcNode.count;
destNode.height = srcNode.height;
srcNode.key = tempNode.key;
srcNode.value = tempNode.value;
srcNode.count = tempNode.count;
srcNode.height = tempNode.height;
}
return destNode;
}
/**
* The function sets the value of the "_count" property.
* @param {number} v - number
*/
protected _setCount(v: number) {

View file

@ -9,77 +9,45 @@ 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<V = any> {
key: VertexKey;
value: V | undefined;
/**
* The function is a protected constructor that takes an key and an optional value as parameters.
* @param {VertexKey} key - The `key` parameter is of type `VertexKey` and represents the identifier of the vertex. It is
* used to uniquely identify the vertex object.
* @param {V} [val] - The parameter "val" is an optional parameter of type V. It is used to assign a value to the
* @param {V} [value] - The parameter "value" is an optional parameter of type V. It is used to assign a value to the
* vertex. If no value is provided, it will be set to undefined.
*/
protected constructor(key: VertexKey, val?: V) {
this._key = key;
this._val = val;
protected constructor(key: VertexKey, value?: V) {
this.key = key;
this.value = value;
}
private _key: VertexKey;
get key(): VertexKey {
return this._key;
}
set key(v: VertexKey) {
this._key = v;
}
private _val: V | undefined;
get val(): V | undefined {
return this._val;
}
set val(value: V | undefined) {
this._val = value;
}
}
export abstract class AbstractEdge<V = any> {
export abstract class AbstractEdge<E = any> {
value: E | undefined;
weight: number;
/**
* The above function is a protected constructor that initializes the weight, value, and hash code properties of an
* object.
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the object. If
* a value is provided, it will be assigned to the `_weight` property. If no value is provided, the default value of 1
* will be assigned.
* @param {V} [val] - The `val` parameter is of type `V`, which means it can be any type. It is an optional parameter,
* @param {VO} [value] - The `value` parameter is of type `VO`, which means it can be any type. It is an optional parameter,
* meaning it can be omitted when creating an instance of the class.
*/
protected constructor(weight?: number, val?: V) {
this._weight = weight !== undefined ? weight : 1;
this._val = val;
protected constructor(weight?: number, value?: E) {
this.weight = weight !== undefined ? weight : 1;
this.value = value;
this._hashCode = uuidV4();
}
private _val: V | undefined;
get val(): V | undefined {
return this._val;
}
set val(value: V | undefined) {
this._val = value;
}
private _weight: number;
get weight(): number {
return this._weight;
}
set weight(v: number) {
this._weight = v;
}
protected _hashCode: string;
get hashCode(): string {
@ -90,25 +58,17 @@ export abstract class AbstractEdge<V = any> {
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
*/
/**
* The function sets the value of the _hashCode property to the provided string.
* @param {string} v - The parameter "v" is of type string and represents the value that will be assigned to the
* "_hashCode" property.
*/
protected _setHashCode(v: string) {
this._hashCode = v;
}
}
export abstract class AbstractGraph<
V extends AbstractVertex<any> = AbstractVertex<any>,
E extends AbstractEdge<any> = AbstractEdge<any>
> implements IGraph<V, E>
{
private _vertices: Map<VertexKey, V> = new Map<VertexKey, V>();
V = any,
E = any,
VO extends AbstractVertex<V> = AbstractVertex<V>,
EO extends AbstractEdge<E> = AbstractEdge<E>
> implements IGraph<V, E, VO, EO> {
protected _vertices: Map<VertexKey, VO> = new Map<VertexKey, VO>();
get vertices(): Map<VertexKey, V> {
get vertices(): Map<VertexKey, VO> {
return this._vertices;
}
@ -116,9 +76,9 @@ export abstract class AbstractGraph<
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
* This means that using abstract methods in the parent class cannot constrain the grandchild classes. Defining methods within an interface also cannot constrain the descendant classes. When inheriting from this class, developers need to be aware that this method needs to be overridden.
* @param key
* @param val
* @param value
*/
abstract createVertex(key: VertexKey, val?: V): V;
abstract createVertex(key: VertexKey, value?: V): VO;
/**
* In TypeScript, a subclass inherits the interface implementation of its parent class, without needing to implement the same interface again in the subclass. This behavior differs from Java's approach. In Java, if a parent class implements an interface, the subclass needs to explicitly implement the same interface, even if the parent class has already implemented it.
@ -126,23 +86,23 @@ export abstract class AbstractGraph<
* @param srcOrV1
* @param destOrV2
* @param weight
* @param val
* @param value
*/
abstract createEdge(srcOrV1: VertexKey | string, destOrV2: VertexKey | string, weight?: number, val?: E): E;
abstract createEdge(srcOrV1: VertexKey, destOrV2: VertexKey, weight?: number, value?: E): EO;
abstract removeEdge(edge: E): E | null;
abstract deleteEdge(edge: EO): EO | null;
abstract getEdge(srcOrKey: V | VertexKey, destOrKey: V | VertexKey): E | null;
abstract getEdge(srcOrKey: VO | VertexKey, destOrKey: VO | VertexKey): EO | null;
abstract degreeOf(vertexOrKey: V | VertexKey): number;
abstract degreeOf(vertexOrKey: VO | VertexKey): number;
abstract edgeSet(): E[];
abstract edgeSet(): EO[];
abstract edgesOf(vertexOrKey: V | VertexKey): E[];
abstract edgesOf(vertexOrKey: VO | VertexKey): EO[];
abstract getNeighbors(vertexOrKey: V | VertexKey): V[];
abstract getNeighbors(vertexOrKey: VO | VertexKey): VO[];
abstract getEndsOfEdge(edge: E): [V, V] | null;
abstract getEndsOfEdge(edge: EO): [VO, VO] | null;
/**
* The function "getVertex" returns the vertex with the specified ID or null if it doesn't exist.
@ -151,77 +111,77 @@ export abstract class AbstractGraph<
* @returns The method `getVertex` returns the vertex with the specified `vertexKey` if it exists in the `_vertices`
* map. If the vertex does not exist, it returns `null`.
*/
getVertex(vertexKey: VertexKey): V | null {
getVertex(vertexKey: VertexKey): VO | null {
return this._vertices.get(vertexKey) || null;
}
/**
* The function checks if a vertex exists in a graph.
* @param {V | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`V`) or a vertex ID
* @param {VO | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`VO`) or a vertex ID
* (`VertexKey`).
* @returns a boolean value.
*/
hasVertex(vertexOrKey: V | VertexKey): boolean {
hasVertex(vertexOrKey: VO | VertexKey): boolean {
return this._vertices.has(this._getVertexKey(vertexOrKey));
}
addVertex(vertex: V): boolean;
addVertex(vertex: VO): boolean;
addVertex(key: VertexKey, val?: V['val']): boolean;
addVertex(key: VertexKey, value?: V): boolean;
addVertex(keyOrVertex: VertexKey | V, val?: V['val']): boolean {
addVertex(keyOrVertex: VertexKey | VO, value?: V): boolean {
if (keyOrVertex instanceof AbstractVertex) {
return this._addVertexOnly(keyOrVertex);
} else {
const newVertex = this.createVertex(keyOrVertex, val);
const newVertex = this.createVertex(keyOrVertex, value);
return this._addVertexOnly(newVertex);
}
}
/**
* The `removeVertex` function removes a vertex from a graph by its ID or by the vertex object itself.
* @param {V | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`V`) or a vertex ID
* The `deleteVertex` function removes a vertex from a graph by its ID or by the vertex object itself.
* @param {VO | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`VO`) or a vertex ID
* (`VertexKey`).
* @returns The method is returning a boolean value.
*/
removeVertex(vertexOrKey: V | VertexKey): boolean {
deleteVertex(vertexOrKey: VO | VertexKey): boolean {
const vertexKey = this._getVertexKey(vertexOrKey);
return this._vertices.delete(vertexKey);
}
/**
* The function removes all vertices from a graph and returns a boolean indicating if any vertices were removed.
* @param {V[] | VertexKey[]} vertices - The `vertices` parameter can be either an array of vertices (`V[]`) or an array
* @param {VO[] | VertexKey[]} vertices - The `vertices` parameter can be either an array of vertices (`VO[]`) or an array
* of vertex IDs (`VertexKey[]`).
* @returns a boolean value. It returns true if at least one vertex was successfully removed, and false if no vertices
* were removed.
*/
removeAllVertices(vertices: V[] | VertexKey[]): boolean {
removeManyVertices(vertices: VO[] | VertexKey[]): boolean {
const removed: boolean[] = [];
for (const v of vertices) {
removed.push(this.removeVertex(v));
removed.push(this.deleteVertex(v));
}
return removed.length > 0;
}
/**
* The function checks if there is an edge between two vertices and returns a boolean value indicating the result.
* @param {VertexKey | V} v1 - The parameter v1 can be either a VertexKey or a V. A VertexKey represents the unique
* identifier of a vertex in a graph, while V represents the type of the vertex object itself.
* @param {VertexKey | V} v2 - The parameter `v2` represents the second vertex in the edge. It can be either a
* `VertexKey` or a `V` type, which represents the type of the vertex.
* @param {VertexKey | VO} v1 - The parameter v1 can be either a VertexKey or a VO. A VertexKey represents the unique
* identifier of a vertex in a graph, while VO represents the type of the vertex object itself.
* @param {VertexKey | VO} v2 - The parameter `v2` represents the second vertex in the edge. It can be either a
* `VertexKey` or a `VO` type, which represents the type of the vertex.
* @returns A boolean value is being returned.
*/
hasEdge(v1: VertexKey | V, v2: VertexKey | V): boolean {
hasEdge(v1: VertexKey | VO, v2: VertexKey | VO): boolean {
const edge = this.getEdge(v1, v2);
return !!edge;
}
addEdge(edge: E): boolean;
addEdge(edge: EO): boolean;
addEdge(src: V | VertexKey, dest: V | VertexKey, weight?: number, val?: E['val']): boolean;
addEdge(src: VO | VertexKey, dest: VO | VertexKey, weight?: number, value?: E): boolean;
addEdge(srcOrEdge: V | VertexKey | E, dest?: V | VertexKey, weight?: number, val?: E['val']): boolean {
addEdge(srcOrEdge: VO | VertexKey | EO, dest?: VO | VertexKey, weight?: number, value?: E): boolean {
if (srcOrEdge instanceof AbstractEdge) {
return this._addEdgeOnly(srcOrEdge);
} else {
@ -229,7 +189,7 @@ export abstract class AbstractGraph<
if (!(this.hasVertex(srcOrEdge) && this.hasVertex(dest))) return false;
if (srcOrEdge instanceof AbstractVertex) srcOrEdge = srcOrEdge.key;
if (dest instanceof AbstractVertex) dest = dest.key;
const newEdge = this.createEdge(srcOrEdge, dest, weight, val);
const newEdge = this.createEdge(srcOrEdge, dest, weight, value);
return this._addEdgeOnly(newEdge);
} else {
throw new Error('dest must be a Vertex or vertex key while srcOrEdge is an Edge');
@ -239,16 +199,16 @@ export abstract class AbstractGraph<
/**
* The function sets the weight of an edge between two vertices in a graph.
* @param {VertexKey | V} srcOrKey - The `srcOrKey` parameter can be either a `VertexKey` or a `V` object. It represents
* @param {VertexKey | VO} srcOrKey - The `srcOrKey` parameter can be either a `VertexKey` or a `VO` object. It represents
* the source vertex of the edge.
* @param {VertexKey | V} destOrKey - The `destOrKey` parameter represents the destination vertex of the edge. It can be
* either a `VertexKey` or a vertex object `V`.
* @param {VertexKey | VO} destOrKey - The `destOrKey` parameter represents the destination vertex of the edge. It can be
* either a `VertexKey` or a vertex object `VO`.
* @param {number} weight - The weight parameter represents the weight of the edge between the source vertex (srcOrKey)
* and the destination vertex (destOrKey).
* @returns a boolean value. If the edge exists between the source and destination vertices, the function will update
* the weight of the edge and return true. If the edge does not exist, the function will return false.
*/
setEdgeWeight(srcOrKey: VertexKey | V, destOrKey: VertexKey | V, weight: number): boolean {
setEdgeWeight(srcOrKey: VertexKey | VO, destOrKey: VertexKey | VO, weight: number): boolean {
const edge = this.getEdge(srcOrKey, destOrKey);
if (edge) {
edge.weight = weight;
@ -260,20 +220,20 @@ export abstract class AbstractGraph<
/**
* The function `getAllPathsBetween` finds all paths between two vertices in a graph using depth-first search.
* @param {V | VertexKey} v1 - The parameter `v1` represents either a vertex object (`V`) or a vertex ID (`VertexKey`).
* @param {VO | VertexKey} v1 - The parameter `v1` represents either a vertex object (`VO`) or a vertex ID (`VertexKey`).
* It is the starting vertex for finding paths.
* @param {V | VertexKey} v2 - The parameter `v2` represents either a vertex object (`V`) or a vertex ID (`VertexKey`).
* @returns The function `getAllPathsBetween` returns an array of arrays of vertices (`V[][]`).
* @param {VO | VertexKey} v2 - The parameter `v2` represents either a vertex object (`VO`) or a vertex ID (`VertexKey`).
* @returns The function `getAllPathsBetween` returns an array of arrays of vertices (`VO[][]`).
*/
getAllPathsBetween(v1: V | VertexKey, v2: V | VertexKey): V[][] {
const paths: V[][] = [];
getAllPathsBetween(v1: VO | VertexKey, v2: VO | VertexKey): VO[][] {
const paths: VO[][] = [];
const vertex1 = this._getVertex(v1);
const vertex2 = this._getVertex(v2);
if (!(vertex1 && vertex2)) {
return [];
}
const dfs = (cur: V, dest: V, visiting: Map<V, boolean>, path: V[]) => {
const dfs = (cur: VO, dest: VO, visiting: Map<VO, boolean>, path: VO[]) => {
visiting.set(cur, true);
if (cur === dest) {
@ -285,23 +245,23 @@ export abstract class AbstractGraph<
if (!visiting.get(neighbor)) {
path.push(neighbor);
dfs(neighbor, dest, visiting, path);
arrayRemove(path, (vertex: V) => vertex === neighbor);
arrayRemove(path, (vertex: VO) => vertex === neighbor);
}
}
visiting.set(cur, false);
};
dfs(vertex1, vertex2, new Map<V, boolean>(), []);
dfs(vertex1, vertex2, new Map<VO, boolean>(), []);
return paths;
}
/**
* The function calculates the sum of weights along a given path.
* @param {V[]} path - An array of vertices (V) representing a path in a graph.
* @param {VO[]} path - An array of vertices (VO) representing a path in a graph.
* @returns The function `getPathSumWeight` returns the sum of the weights of the edges in the given path.
*/
getPathSumWeight(path: V[]): number {
getPathSumWeight(path: VO[]): number {
let sum = 0;
for (let i = 0; i < path.length; i++) {
sum += this.getEdge(path[i], path[i + 1])?.weight || 0;
@ -312,8 +272,8 @@ export abstract class AbstractGraph<
/**
* The function `getMinCostBetween` calculates the minimum cost between two vertices in a graph, either based on edge
* weights or using a breadth-first search algorithm.
* @param {V | VertexKey} v1 - The parameter `v1` represents the starting vertex or its ID.
* @param {V | VertexKey} v2 - The parameter `v2` represents the destination vertex or its ID. It is the vertex to which
* @param {VO | VertexKey} v1 - The parameter `v1` represents the starting vertex or its ID.
* @param {VO | VertexKey} v2 - The parameter `v2` represents the destination vertex or its ID. It is the vertex to which
* you want to find the minimum cost or weight from the source vertex `v1`.
* @param {boolean} [isWeight] - isWeight is an optional parameter that indicates whether the graph edges have weights.
* If isWeight is set to true, the function will calculate the minimum cost between v1 and v2 based on the weights of
@ -323,7 +283,7 @@ export abstract class AbstractGraph<
* vertices. If `isWeight` is `false` or not provided, it uses a breadth-first search (BFS) algorithm to calculate the
* minimum number of
*/
getMinCostBetween(v1: V | VertexKey, v2: V | VertexKey, isWeight?: boolean): number | null {
getMinCostBetween(v1: VO | VertexKey, v2: VO | VertexKey, isWeight?: boolean): number | null {
if (isWeight === undefined) isWeight = false;
if (isWeight) {
@ -341,12 +301,12 @@ export abstract class AbstractGraph<
return null;
}
const visited: Map<V, boolean> = new Map();
const queue: V[] = [vertex1];
const visited: Map<VO, boolean> = new Map();
const queue = new Queue<VO>([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;
@ -371,17 +331,17 @@ export abstract class AbstractGraph<
/**
* The function `getMinPathBetween` returns the minimum path between two vertices in a graph, either based on weight or
* using a breadth-first search algorithm.
* @param {V | VertexKey} v1 - The parameter `v1` represents the starting vertex of the path. It can be either a vertex
* object (`V`) or a vertex ID (`VertexKey`).
* @param {V | VertexKey} v2 - V | VertexKey - The second vertex or vertex ID between which we want to find the minimum
* @param {VO | VertexKey} v1 - The parameter `v1` represents the starting vertex of the path. It can be either a vertex
* object (`VO`) or a vertex ID (`VertexKey`).
* @param {VO | VertexKey} v2 - VO | VertexKey - The second vertex or vertex ID between which we want to find the minimum
* path.
* @param {boolean} [isWeight] - A boolean flag indicating whether to consider the weight of edges in finding the
* minimum path. If set to true, the function will use Dijkstra's algorithm to find the minimum weighted path. If set
* to false, the function will use breadth-first search (BFS) to find the minimum path.
* @returns The function `getMinPathBetween` returns an array of vertices (`V[]`) representing the minimum path between
* @returns The function `getMinPathBetween` returns an array of vertices (`VO[]`) representing the minimum path between
* two vertices (`v1` and `v2`). If there is no path between the vertices, it returns `null`.
*/
getMinPathBetween(v1: V | VertexKey, v2: V | VertexKey, isWeight?: boolean): V[] | null {
getMinPathBetween(v1: VO | VertexKey, v2: VO | VertexKey, isWeight?: boolean): VO[] | null {
if (isWeight === undefined) isWeight = false;
if (isWeight) {
@ -400,14 +360,14 @@ export abstract class AbstractGraph<
return allPaths[minIndex] || null;
} else {
// BFS
let minPath: V[] = [];
let minPath: VO[] = [];
const vertex1 = this._getVertex(v1);
const vertex2 = this._getVertex(v2);
if (!(vertex1 && vertex2)) {
return [];
}
const dfs = (cur: V, dest: V, visiting: Map<V, boolean>, path: V[]) => {
const dfs = (cur: VO, dest: VO, visiting: Map<VO, boolean>, path: VO[]) => {
visiting.set(cur, true);
if (cur === dest) {
@ -420,29 +380,29 @@ export abstract class AbstractGraph<
if (!visiting.get(neighbor)) {
path.push(neighbor);
dfs(neighbor, dest, visiting, path);
arrayRemove(path, (vertex: V) => vertex === neighbor);
arrayRemove(path, (vertex: VO) => vertex === neighbor);
}
}
visiting.set(cur, false);
};
dfs(vertex1, vertex2, new Map<V, boolean>(), []);
dfs(vertex1, vertex2, new Map<VO, boolean>(), []);
return minPath;
}
}
/**
* Dijkstra algorithm time: O(VE) space: O(V + E)
* Dijkstra algorithm time: O(VE) space: O(VO + EO)
* /
/**
* Dijkstra algorithm time: O(VE) space: O(V + E)
* Dijkstra algorithm time: O(VE) space: O(VO + EO)
* The function `dijkstraWithoutHeap` implements Dijkstra's algorithm to find the shortest path between two vertices in
* a graph without using a heap data structure.
* @param {V | VertexKey} src - The source vertex from which to start the Dijkstra's algorithm. It can be either a
* @param {VO | VertexKey} src - The source vertex from which to start the Dijkstra's algorithm. It can be either a
* vertex object or a vertex ID.
* @param {V | VertexKey | null} [dest] - The `dest` parameter in the `dijkstraWithoutHeap` function is an optional
* @param {VO | VertexKey | null} [dest] - The `dest` parameter in the `dijkstraWithoutHeap` function is an optional
* parameter that specifies the destination vertex for the Dijkstra algorithm. It can be either a vertex object or its
* identifier. If no destination is provided, the value is set to `null`.
* @param {boolean} [getMinDist] - The `getMinDist` parameter is a boolean flag that determines whether the minimum
@ -451,27 +411,27 @@ export abstract class AbstractGraph<
* @param {boolean} [genPaths] - The `genPaths` parameter is a boolean flag that determines whether or not to generate
* paths in the Dijkstra algorithm. If `genPaths` is set to `true`, the algorithm will calculate and return the
* shortest paths from the source vertex to all other vertices in the graph. If `genPaths
* @returns The function `dijkstraWithoutHeap` returns an object of type `DijkstraResult<V>`.
* @returns The function `dijkstraWithoutHeap` returns an object of type `DijkstraResult<VO>`.
*/
dijkstraWithoutHeap(
src: V | VertexKey,
dest?: V | VertexKey | null,
src: VO | VertexKey,
dest?: VO | VertexKey | null,
getMinDist?: boolean,
genPaths?: boolean
): DijkstraResult<V> {
): DijkstraResult<VO> {
if (getMinDist === undefined) getMinDist = false;
if (genPaths === undefined) genPaths = false;
if (dest === undefined) dest = null;
let minDist = Infinity;
let minDest: V | null = null;
let minPath: V[] = [];
const paths: V[][] = [];
let minDest: VO | null = null;
let minPath: VO[] = [];
const paths: VO[][] = [];
const vertices = this._vertices;
const distMap: Map<V, number> = new Map();
const seen: Set<V> = new Set();
const preMap: Map<V, V | null> = new Map(); // predecessor
const distMap: Map<VO, number> = new Map();
const seen: Set<VO> = new Set();
const preMap: Map<VO, VO | null> = new Map(); // predecessor
const srcVertex = this._getVertex(src);
const destVertex = dest ? this._getVertex(dest) : null;
@ -489,11 +449,11 @@ export abstract class AbstractGraph<
const getMinOfNoSeen = () => {
let min = Infinity;
let minV: V | null = null;
for (const [key, val] of distMap) {
let minV: VO | null = null;
for (const [key, value] of distMap) {
if (!seen.has(key)) {
if (val < min) {
min = val;
if (value < min) {
min = value;
minV = key;
}
}
@ -501,12 +461,12 @@ export abstract class AbstractGraph<
return minV;
};
const getPaths = (minV: V | null) => {
const getPaths = (minV: VO | null) => {
for (const vertex of vertices) {
const vertexOrKey = vertex[1];
if (vertexOrKey instanceof AbstractVertex) {
const path: V[] = [vertexOrKey];
const path: VO[] = [vertexOrKey];
let parent = preMap.get(vertexOrKey);
while (parent) {
path.push(parent);
@ -553,14 +513,14 @@ export abstract class AbstractGraph<
}
getMinDist &&
distMap.forEach((d, v) => {
if (v !== srcVertex) {
if (d < minDist) {
minDist = d;
if (genPaths) minDest = v;
}
distMap.forEach((d, v) => {
if (v !== srcVertex) {
if (d < minDist) {
minDist = d;
if (genPaths) minDest = v;
}
});
}
});
genPaths && getPaths(minDest);
@ -568,11 +528,11 @@ export abstract class AbstractGraph<
}
/**
* Dijkstra algorithm time: O(logVE) space: O(V + E)
* Dijkstra algorithm time: O(logVE) space: O(VO + EO)
*
* Dijkstra's algorithm only solves the single-source shortest path problem, while the Bellman-Ford algorithm and Floyd-Warshall algorithm can address shortest paths between all pairs of nodes.
* Dijkstra's algorithm is suitable for graphs with non-negative edge weights, whereas the Bellman-Ford algorithm and Floyd-Warshall algorithm can handle negative-weight edges.
* The time complexity of Dijkstra's algorithm and the Bellman-Ford algorithm depends on the size of the graph, while the time complexity of the Floyd-Warshall algorithm is O(V^3), where V is the number of nodes. For dense graphs, Floyd-Warshall might become slower.
* The time complexity of Dijkstra's algorithm and the Bellman-Ford algorithm depends on the size of the graph, while the time complexity of the Floyd-Warshall algorithm is O(VO^3), where VO is the number of nodes. For dense graphs, Floyd-Warshall might become slower.
*
* /
@ -580,9 +540,9 @@ export abstract class AbstractGraph<
* Dijkstra's algorithm is used to find the shortest paths from a source node to all other nodes in a graph. Its basic idea is to repeatedly choose the node closest to the source node and update the distances of other nodes using this node as an intermediary. Dijkstra's algorithm requires that the edge weights in the graph are non-negative.
* The `dijkstra` function implements Dijkstra's algorithm to find the shortest path between a source vertex and an
* optional destination vertex, and optionally returns the minimum distance, the paths, and other information.
* @param {V | VertexKey} src - The `src` parameter represents the source vertex from which the Dijkstra algorithm will
* @param {VO | VertexKey} src - The `src` parameter represents the source vertex from which the Dijkstra algorithm will
* start. It can be either a vertex object or a vertex ID.
* @param {V | VertexKey | null} [dest] - The `dest` parameter is the destination vertex or vertex ID. It specifies the
* @param {VO | VertexKey | null} [dest] - The `dest` parameter is the destination vertex or vertex ID. It specifies the
* vertex to which the shortest path is calculated from the source vertex. If no destination is provided, the algorithm
* will calculate the shortest paths to all other vertices from the source vertex.
* @param {boolean} [getMinDist] - The `getMinDist` parameter is a boolean flag that determines whether the minimum
@ -591,26 +551,26 @@ export abstract class AbstractGraph<
* @param {boolean} [genPaths] - The `genPaths` parameter is a boolean flag that determines whether or not to generate
* paths in the Dijkstra algorithm. If `genPaths` is set to `true`, the algorithm will calculate and return the
* shortest paths from the source vertex to all other vertices in the graph. If `genPaths
* @returns The function `dijkstra` returns an object of type `DijkstraResult<V>`.
* @returns The function `dijkstra` returns an object of type `DijkstraResult<VO>`.
*/
dijkstra(
src: V | VertexKey,
dest?: V | VertexKey | null,
src: VO | VertexKey,
dest?: VO | VertexKey | null,
getMinDist?: boolean,
genPaths?: boolean
): DijkstraResult<V> {
): DijkstraResult<VO> {
if (getMinDist === undefined) getMinDist = false;
if (genPaths === undefined) genPaths = false;
if (dest === undefined) dest = null;
let minDist = Infinity;
let minDest: V | null = null;
let minPath: V[] = [];
const paths: V[][] = [];
let minDest: VO | null = null;
let minPath: VO[] = [];
const paths: VO[][] = [];
const vertices = this._vertices;
const distMap: Map<V, number> = new Map();
const seen: Set<V> = new Set();
const preMap: Map<V, V | null> = new Map(); // predecessor
const distMap: Map<VO, number> = new Map();
const seen: Set<VO> = new Set();
const preMap: Map<VO, VO | null> = new Map(); // predecessor
const srcVertex = this._getVertex(src);
const destVertex = dest ? this._getVertex(dest) : null;
@ -622,22 +582,22 @@ export abstract class AbstractGraph<
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Infinity);
}
const heap = new PriorityQueue<{key: number; val: V}>((a, b) => a.key - b.key);
heap.add({key: 0, val: srcVertex});
const heap = new PriorityQueue<{ key: number; value: VO }>({comparator: (a, b) => a.key - b.key});
heap.add({key: 0, value: srcVertex});
distMap.set(srcVertex, 0);
preMap.set(srcVertex, null);
/**
* The function `getPaths` retrieves all paths from vertices to a specified minimum vertex.
* @param {V | null} minV - The parameter `minV` is of type `V | null`. It represents the minimum vertex value or
* @param {VO | null} minV - The parameter `minV` is of type `VO | null`. It represents the minimum vertex value or
* null.
*/
const getPaths = (minV: V | null) => {
const getPaths = (minV: VO | null) => {
for (const vertex of vertices) {
const vertexOrKey = vertex[1];
if (vertexOrKey instanceof AbstractVertex) {
const path: V[] = [vertexOrKey];
const path: VO[] = [vertexOrKey];
let parent = preMap.get(vertexOrKey);
while (parent) {
path.push(parent);
@ -653,7 +613,7 @@ export abstract class AbstractGraph<
while (heap.size > 0) {
const curHeapNode = heap.poll();
const dist = curHeapNode?.key;
const cur = curHeapNode?.val;
const cur = curHeapNode?.value;
if (dist !== undefined) {
if (cur) {
seen.add(cur);
@ -674,7 +634,7 @@ export abstract class AbstractGraph<
const distSrcToNeighbor = distMap.get(neighbor);
if (distSrcToNeighbor) {
if (dist + weight < distSrcToNeighbor) {
heap.add({key: dist + weight, val: neighbor});
heap.add({key: dist + weight, value: neighbor});
preMap.set(neighbor, cur);
distMap.set(neighbor, dist + weight);
}
@ -705,17 +665,17 @@ export abstract class AbstractGraph<
}
/**
* BellmanFord time:O(VE) space:O(V)
* BellmanFord time:O(VE) space:O(VO)
* one to rest pairs
* /
/**
* BellmanFord time:O(VE) space:O(V)
* BellmanFord time:O(VE) space:O(VO)
* one to rest pairs
* The Bellman-Ford algorithm is also used to find the shortest paths from a source node to all other nodes in a graph. Unlike Dijkstra's algorithm, it can handle edge weights that are negative. Its basic idea involves iterative relaxation of all edges for several rounds to gradually approximate the shortest paths. Due to its ability to handle negative-weight edges, the Bellman-Ford algorithm is more flexible in some scenarios.
* The `bellmanFord` function implements the Bellman-Ford algorithm to find the shortest path from a source vertex to
* all other vertices in a graph, and optionally detects negative cycles and generates the minimum path.
* @param {V | VertexKey} src - The `src` parameter is the source vertex from which the Bellman-Ford algorithm will
* @param {VO | VertexKey} src - The `src` parameter is the source vertex from which the Bellman-Ford algorithm will
* start calculating the shortest paths. It can be either a vertex object or a vertex ID.
* @param {boolean} [scanNegativeCycle] - A boolean flag indicating whether to scan for negative cycles in the graph.
* @param {boolean} [getMin] - The `getMin` parameter is a boolean flag that determines whether the algorithm should
@ -725,16 +685,16 @@ export abstract class AbstractGraph<
* vertex.
* @returns The function `bellmanFord` returns an object with the following properties:
*/
bellmanFord(src: V | VertexKey, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
bellmanFord(src: VO | VertexKey, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
if (getMin === undefined) getMin = false;
if (genPath === undefined) genPath = false;
const srcVertex = this._getVertex(src);
const paths: V[][] = [];
const distMap: Map<V, number> = new Map();
const preMap: Map<V, V> = new Map(); // predecessor
const paths: VO[][] = [];
const distMap: Map<VO, number> = new Map();
const preMap: Map<VO, VO> = new Map(); // predecessor
let min = Infinity;
let minPath: V[] = [];
let minPath: VO[] = [];
// TODO
let hasNegativeCycle: boolean | undefined;
if (scanNegativeCycle) hasNegativeCycle = false;
@ -769,7 +729,7 @@ export abstract class AbstractGraph<
}
}
let minDest: V | null = null;
let minDest: VO | null = null;
if (getMin) {
distMap.forEach((d, v) => {
if (v !== srcVertex) {
@ -785,7 +745,7 @@ export abstract class AbstractGraph<
for (const vertex of vertices) {
const vertexOrKey = vertex[1];
if (vertexOrKey instanceof AbstractVertex) {
const path: V[] = [vertexOrKey];
const path: VO[] = [vertexOrKey];
let parent = preMap.get(vertexOrKey);
while (parent !== undefined) {
path.push(parent);
@ -814,34 +774,34 @@ export abstract class AbstractGraph<
}
/**
* Dijkstra algorithm time: O(logVE) space: O(V + E)
* Dijkstra algorithm time: O(logVE) space: O(VO + EO)
* /
/**
* Dijkstra algorithm time: O(logVE) space: O(V + E)
* Dijkstra algorithm time: O(logVE) space: O(VO + EO)
* Dijkstra's algorithm is used to find the shortest paths from a source node to all other nodes in a graph. Its basic idea is to repeatedly choose the node closest to the source node and update the distances of other nodes using this node as an intermediary. Dijkstra's algorithm requires that the edge weights in the graph are non-negative.
*/
/**
* BellmanFord time:O(VE) space:O(V)
* BellmanFord time:O(VE) space:O(VO)
* one to rest pairs
* The Bellman-Ford algorithm is also used to find the shortest paths from a source node to all other nodes in a graph. Unlike Dijkstra's algorithm, it can handle edge weights that are negative. Its basic idea involves iterative relaxation of all edges for several rounds to gradually approximate the shortest paths. Due to its ability to handle negative-weight edges, the Bellman-Ford algorithm is more flexible in some scenarios.
* The `bellmanFord` function implements the Bellman-Ford algorithm to find the shortest path from a source vertex to
*/
/**
* Floyd algorithm time: O(V^3) space: O(V^2), not support graph with negative weight cycle
* Floyd algorithm time: O(VO^3) space: O(VO^2), not support graph with negative weight cycle
* all pairs
* The Floyd-Warshall algorithm is used to find the shortest paths between all pairs of nodes in a graph. It employs dynamic programming to compute the shortest paths from any node to any other node. The Floyd-Warshall algorithm's advantage lies in its ability to handle graphs with negative-weight edges, and it can simultaneously compute shortest paths between any two nodes.
*/
/**
* Floyd algorithm time: O(V^3) space: O(V^2), not support graph with negative weight cycle
* Floyd algorithm time: O(VO^3) space: O(VO^2), not support graph with negative weight cycle
* all pairs
* /
/**
* Floyd algorithm time: O(V^3) space: O(V^2), not support graph with negative weight cycle
* Floyd algorithm time: O(VO^3) space: O(VO^2), not support graph with negative weight cycle
* all pairs
* The Floyd-Warshall algorithm is used to find the shortest paths between all pairs of nodes in a graph. It employs dynamic programming to compute the shortest paths from any node to any other node. The Floyd-Warshall algorithm's advantage lies in its ability to handle graphs with negative-weight edges, and it can simultaneously compute shortest paths between any two nodes.
* The function implements the Floyd-Warshall algorithm to find the shortest path between all pairs of vertices in a
@ -851,12 +811,12 @@ export abstract class AbstractGraph<
* `predecessor` property is a 2D array of vertices (or `null`) representing the predecessor vertices in the shortest
* path between vertices in the
*/
floyd(): {costs: number[][]; predecessor: (V | null)[][]} {
floyd(): { costs: number[][]; predecessor: (VO | null)[][] } {
const idAndVertices = [...this._vertices];
const n = idAndVertices.length;
const costs: number[][] = [];
const predecessor: (V | null)[][] = [];
const predecessor: (VO | null)[][] = [];
// successors
for (let i = 0; i < n; i++) {
@ -926,8 +886,8 @@ export abstract class AbstractGraph<
if (needSCCs === undefined) needSCCs = defaultConfig;
if (needCycles === undefined) needCycles = defaultConfig;
const dfnMap: Map<V, number> = new Map();
const lowMap: Map<V, number> = new Map();
const dfnMap: Map<VO, number> = new Map();
const lowMap: Map<VO, number> = new Map();
const vertices = this._vertices;
vertices.forEach(v => {
dfnMap.set(v, -1);
@ -936,10 +896,10 @@ export abstract class AbstractGraph<
const [root] = vertices.values();
const articulationPoints: V[] = [];
const bridges: E[] = [];
const articulationPoints: VO[] = [];
const bridges: EO[] = [];
let dfn = 0;
const dfs = (cur: V, parent: V | null) => {
const dfs = (cur: VO, parent: VO | null) => {
dfn++;
dfnMap.set(cur, dfn);
lowMap.set(cur, dfn);
@ -982,10 +942,10 @@ export abstract class AbstractGraph<
dfs(root, null);
let SCCs: Map<number, V[]> = new Map();
let SCCs: Map<number, VO[]> = new Map();
const getSCCs = () => {
const SCCs: Map<number, V[]> = new Map();
const SCCs: Map<number, VO[]> = new Map();
lowMap.forEach((low, vertex) => {
if (!SCCs.has(low)) {
SCCs.set(low, [vertex]);
@ -1000,9 +960,9 @@ export abstract class AbstractGraph<
SCCs = getSCCs();
}
const cycles: Map<number, V[]> = new Map();
const cycles: Map<number, VO[]> = new Map();
if (needCycles) {
let SCCs: Map<number, V[]> = new Map();
let SCCs: Map<number, VO[]> = new Map();
if (SCCs.size < 1) {
SCCs = getSCCs();
}
@ -1017,9 +977,9 @@ export abstract class AbstractGraph<
return {dfnMap, lowMap, bridges, articulationPoints, SCCs, cycles};
}
protected abstract _addEdgeOnly(edge: E): boolean;
protected abstract _addEdgeOnly(edge: EO): boolean;
protected _addVertexOnly(newVertex: V): boolean {
protected _addVertexOnly(newVertex: VO): boolean {
if (this.hasVertex(newVertex)) {
return false;
// throw (new Error('Duplicated vertex key is not allowed'));
@ -1028,16 +988,13 @@ export abstract class AbstractGraph<
return true;
}
protected _getVertex(vertexOrKey: VertexKey | V): V | null {
protected _getVertex(vertexOrKey: VertexKey | VO): VO | null {
const vertexKey = this._getVertexKey(vertexOrKey);
return this._vertices.get(vertexKey) || null;
}
protected _getVertexKey(vertexOrKey: V | VertexKey): VertexKey {
protected _getVertexKey(vertexOrKey: VO | VertexKey): VertexKey {
return vertexOrKey instanceof AbstractVertex ? vertexOrKey.key : vertexOrKey;
}
protected _setVertices(value: Map<VertexKey, V>) {
this._vertices = value;
}
}

View file

@ -15,15 +15,18 @@ export class DirectedVertex<V = any> extends AbstractVertex<V> {
* The constructor function initializes a vertex with an optional value.
* @param {VertexKey} key - The `key` parameter is of type `VertexKey` and represents the identifier of the vertex. It is
* used to uniquely identify the vertex within a graph or data structure.
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to initialize the value of the
* @param {V} [value] - The "value" parameter is an optional parameter of type V. It is used to initialize the value of the
* vertex. If no value is provided, the vertex will be initialized with a default value.
*/
constructor(key: VertexKey, val?: V) {
super(key, val);
constructor(key: VertexKey, value?: V) {
super(key, value);
}
}
export class DirectedEdge<V = any> extends AbstractEdge<V> {
export class DirectedEdge<E = any> extends AbstractEdge<E> {
src: VertexKey;
dest: VertexKey;
/**
* The constructor function initializes the source and destination vertices of an edge, along with an optional weight
* and value.
@ -32,40 +35,24 @@ export class DirectedEdge<V = any> extends AbstractEdge<V> {
* @param {VertexKey} dest - The `dest` parameter represents the destination vertex of an edge. It is of type
* `VertexKey`, which is likely a unique identifier for a vertex in a graph.
* @param {number} [weight] - The weight parameter is an optional number that represents the weight of the edge.
* @param {V} [val] - The `val` parameter is an optional parameter of type `V`. It represents the value associated with
* @param {E} [value] - The `value` parameter is an optional parameter of type `E`. It represents the value associated with
* the edge.
*/
constructor(src: VertexKey, dest: VertexKey, weight?: number, val?: V) {
super(weight, val);
this._src = src;
this._dest = dest;
}
private _src: VertexKey;
get src(): VertexKey {
return this._src;
}
set src(v: VertexKey) {
this._src = v;
}
private _dest: VertexKey;
get dest(): VertexKey {
return this._dest;
}
set dest(v: VertexKey) {
this._dest = v;
constructor(src: VertexKey, dest: VertexKey, weight?: number, value?: E) {
super(weight, value);
this.src = src;
this.dest = dest;
}
}
export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E extends DirectedEdge<any> = DirectedEdge>
extends AbstractGraph<V, E>
implements IGraph<V, E>
{
export class DirectedGraph<
V = any,
E = any,
VO extends DirectedVertex<V> = DirectedVertex<V>,
EO extends DirectedEdge<E> = DirectedEdge<E>
>
extends AbstractGraph<V, E, VO, EO>
implements IGraph<V, E, VO, EO> {
/**
* The constructor function initializes an instance of a class.
*/
@ -73,15 +60,15 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
super();
}
private _outEdgeMap: Map<V, E[]> = new Map<V, E[]>();
protected _outEdgeMap: Map<VO, EO[]> = new Map<VO, EO[]>();
get outEdgeMap(): Map<V, E[]> {
get outEdgeMap(): Map<VO, EO[]> {
return this._outEdgeMap;
}
private _inEdgeMap: Map<V, E[]> = new Map<V, E[]>();
protected _inEdgeMap: Map<VO, EO[]> = new Map<VO, EO[]>();
get inEdgeMap(): Map<V, E[]> {
get inEdgeMap(): Map<VO, EO[]> {
return this._inEdgeMap;
}
@ -94,13 +81,13 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
* The function creates a new vertex with an optional value and returns it.
* @param {VertexKey} key - The `key` parameter is the unique identifier for the vertex. It is of type `VertexKey`, which
* could be a number or a string depending on how you want to identify your vertices.
* @param [val] - The 'val' parameter is an optional value that can be assigned to the vertex. If a value is provided,
* it will be assigned to the 'val' property of the vertex. If no value is provided, the 'val' property will be
* @param [value] - The 'value' parameter is an optional value that can be assigned to the vertex. If a value is provided,
* it will be assigned to the 'value' property of the vertex. If no value is provided, the 'value' property will be
* assigned the same value as the 'key' parameter
* @returns a new instance of a DirectedVertex object, casted as type V.
* @returns a new instance of a DirectedVertex object, casted as type VO.
*/
createVertex(key: VertexKey, val?: V['val']): V {
return new DirectedVertex(key, val ?? key) as V;
createVertex(key: VertexKey, value?: V): VO {
return new DirectedVertex(key, value ?? key) as VO;
}
/**
@ -114,28 +101,28 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
* @param {VertexKey} dest - The `dest` parameter is the identifier of the destination vertex for the edge.
* @param {number} [weight] - The weight parameter is an optional number that represents the weight of the edge. If no
* weight is provided, it defaults to 1.
* @param [val] - The 'val' parameter is an optional value that can be assigned to the edge. It can be of any type and
* @param [value] - The 'value' parameter is an optional value that can be assigned to the edge. It can be of any type and
* is used to store additional information or data associated with the edge.
* @returns a new instance of a DirectedEdge object, casted as type E.
* @returns a new instance of a DirectedEdge object, casted as type EO.
*/
createEdge(src: VertexKey, dest: VertexKey, weight?: number, val?: E['val']): E {
return new DirectedEdge(src, dest, weight ?? 1, val) as E;
createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO {
return new DirectedEdge(src, dest, weight ?? 1, value) as EO;
}
/**
* The `getEdge` function retrieves an edge between two vertices based on their source and destination IDs.
* @param {V | null | VertexKey} srcOrKey - The source vertex or its ID. It can be either a vertex object or a vertex ID.
* @param {V | null | VertexKey} destOrKey - The `destOrKey` parameter in the `getEdge` function represents the
* destination vertex of the edge. It can be either a vertex object (`V`), a vertex ID (`VertexKey`), or `null` if the
* @param {VO | VertexKey | null} srcOrKey - The source vertex or its ID. It can be either a vertex object or a vertex ID.
* @param {VO | VertexKey | null} destOrKey - The `destOrKey` parameter in the `getEdge` function represents the
* destination vertex of the edge. It can be either a vertex object (`VO`), a vertex ID (`VertexKey`), or `null` if the
* destination is not specified.
* @returns the first edge found between the source and destination vertices, or null if no such edge is found.
*/
getEdge(srcOrKey: V | null | VertexKey, destOrKey: V | null | VertexKey): E | null {
let edges: E[] = [];
getEdge(srcOrKey: VO | VertexKey | null, destOrKey: VO | VertexKey | null): EO | null {
let edges: EO[] = [];
if (srcOrKey !== null && destOrKey !== null) {
const src: V | null = this._getVertex(srcOrKey);
const dest: V | null = this._getVertex(destOrKey);
const src: VO | null = this._getVertex(srcOrKey);
const dest: VO | null = this._getVertex(destOrKey);
if (src && dest) {
const srcOutEdges = this._outEdgeMap.get(src);
@ -150,49 +137,49 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function removes an edge between two vertices in a graph and returns the removed edge.
* @param {V | VertexKey} srcOrKey - The source vertex or its ID.
* @param {V | VertexKey} destOrKey - The `destOrKey` parameter represents the destination vertex or its ID.
* @returns the removed edge (E) if it exists, or null if either the source or destination vertex does not exist.
* @param {VO | VertexKey} srcOrKey - The source vertex or its ID.
* @param {VO | VertexKey} destOrKey - The `destOrKey` parameter represents the destination vertex or its ID.
* @returns the removed edge (EO) if it exists, or null if either the source or destination vertex does not exist.
*/
removeEdgeSrcToDest(srcOrKey: V | VertexKey, destOrKey: V | VertexKey): E | null {
const src: V | null = this._getVertex(srcOrKey);
const dest: V | null = this._getVertex(destOrKey);
let removed: E | null = null;
deleteEdgeSrcToDest(srcOrKey: VO | VertexKey, destOrKey: VO | VertexKey): EO | null {
const src: VO | null = this._getVertex(srcOrKey);
const dest: VO | null = this._getVertex(destOrKey);
let removed: EO | null = null;
if (!src || !dest) {
return null;
}
const srcOutEdges = this._outEdgeMap.get(src);
if (srcOutEdges) {
arrayRemove<E>(srcOutEdges, (edge: E) => edge.dest === dest.key);
arrayRemove<EO>(srcOutEdges, (edge: EO) => edge.dest === dest.key);
}
const destInEdges = this._inEdgeMap.get(dest);
if (destInEdges) {
removed = arrayRemove<E>(destInEdges, (edge: E) => edge.src === src.key)[0] || null;
removed = arrayRemove<EO>(destInEdges, (edge: EO) => edge.src === src.key)[0] || null;
}
return removed;
}
/**
* The function removes an edge from a graph and returns the removed edge, or null if the edge was not found.
* @param {E} edge - The `edge` parameter is an object that represents an edge in a graph. It has two properties: `src`
* @param {EO} edge - The `edge` parameter is an object that represents an edge in a graph. It has two properties: `src`
* and `dest`, which represent the source and destination vertices of the edge, respectively.
* @returns The method `removeEdge` returns the removed edge (`E`) if it exists, or `null` if the edge does not exist.
* @returns The method `deleteEdge` returns the removed edge (`EO`) if it exists, or `null` if the edge does not exist.
*/
removeEdge(edge: E): E | null {
let removed: E | null = null;
deleteEdge(edge: EO): EO | null {
let removed: EO | null = null;
const src = this._getVertex(edge.src);
const dest = this._getVertex(edge.dest);
if (src && dest) {
const srcOutEdges = this._outEdgeMap.get(src);
if (srcOutEdges && srcOutEdges.length > 0) {
arrayRemove(srcOutEdges, (edge: E) => edge.src === src.key);
arrayRemove(srcOutEdges, (edge: EO) => edge.src === src.key);
}
const destInEdges = this._inEdgeMap.get(dest);
if (destInEdges && destInEdges.length > 0) {
removed = arrayRemove(destInEdges, (edge: E) => edge.dest === dest.key)[0];
removed = arrayRemove(destInEdges, (edge: EO) => edge.dest === dest.key)[0];
}
}
@ -201,18 +188,18 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function removes edges between two vertices and returns the removed edges.
* @param {VertexKey | V} v1 - The parameter `v1` can be either a `VertexKey` or a `V`. A `VertexKey` represents the
* unique identifier of a vertex in a graph, while `V` represents the actual vertex object.
* @param {VertexKey | V} v2 - The parameter `v2` represents either a `VertexKey` or a `V` object. It is used to specify
* @param {VertexKey | VO} v1 - The parameter `v1` can be either a `VertexKey` or a `VO`. A `VertexKey` represents the
* unique identifier of a vertex in a graph, while `VO` represents the actual vertex object.
* @param {VertexKey | VO} v2 - The parameter `v2` represents either a `VertexKey` or a `VO` object. It is used to specify
* the second vertex in the edge that needs to be removed.
* @returns an array of removed edges (E[]).
* @returns an array of removed edges (EO[]).
*/
removeEdgesBetween(v1: VertexKey | V, v2: VertexKey | V): E[] {
const removed: E[] = [];
deleteEdgesBetween(v1: VertexKey | VO, v2: VertexKey | VO): EO[] {
const removed: EO[] = [];
if (v1 && v2) {
const v1ToV2 = this.removeEdgeSrcToDest(v1, v2);
const v2ToV1 = this.removeEdgeSrcToDest(v2, v1);
const v1ToV2 = this.deleteEdgeSrcToDest(v1, v2);
const v2ToV1 = this.deleteEdgeSrcToDest(v2, v1);
v1ToV2 && removed.push(v1ToV2);
v2ToV1 && removed.push(v2ToV1);
@ -223,11 +210,11 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function `incomingEdgesOf` returns an array of incoming edges for a given vertex or vertex ID.
* @param {V | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`V`) or a vertex ID
* @param {VO | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`VO`) or a vertex ID
* (`VertexKey`).
* @returns The method `incomingEdgesOf` returns an array of edges (`E[]`).
* @returns The method `incomingEdgesOf` returns an array of edges (`EO[]`).
*/
incomingEdgesOf(vertexOrKey: V | VertexKey): E[] {
incomingEdgesOf(vertexOrKey: VO | VertexKey): EO[] {
const target = this._getVertex(vertexOrKey);
if (target) {
return this.inEdgeMap.get(target) || [];
@ -237,11 +224,11 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function `outgoingEdgesOf` returns an array of outgoing edges from a given vertex or vertex ID.
* @param {V | VertexKey} vertexOrKey - The parameter `vertexOrKey` can accept either a vertex object (`V`) or a vertex ID
* @param {VO | VertexKey} vertexOrKey - The parameter `vertexOrKey` can accept either a vertex object (`VO`) or a vertex ID
* (`VertexKey`).
* @returns The method `outgoingEdgesOf` returns an array of edges (`E[]`).
* @returns The method `outgoingEdgesOf` returns an array of edges (`EO[]`).
*/
outgoingEdgesOf(vertexOrKey: V | VertexKey): E[] {
outgoingEdgesOf(vertexOrKey: VO | VertexKey): EO[] {
const target = this._getVertex(vertexOrKey);
if (target) {
return this._outEdgeMap.get(target) || [];
@ -251,69 +238,69 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function "degreeOf" returns the total degree of a vertex, which is the sum of its out-degree and in-degree.
* @param {VertexKey | V} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `V`.
* @param {VertexKey | VO} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `VO`.
* @returns The sum of the out-degree and in-degree of the specified vertex or vertex ID.
*/
degreeOf(vertexOrKey: VertexKey | V): number {
degreeOf(vertexOrKey: VertexKey | VO): number {
return this.outDegreeOf(vertexOrKey) + this.inDegreeOf(vertexOrKey);
}
/**
* The function "inDegreeOf" returns the number of incoming edges for a given vertex.
* @param {VertexKey | V} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `V`.
* @param {VertexKey | VO} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `VO`.
* @returns The number of incoming edges of the specified vertex or vertex ID.
*/
inDegreeOf(vertexOrKey: VertexKey | V): number {
inDegreeOf(vertexOrKey: VertexKey | VO): number {
return this.incomingEdgesOf(vertexOrKey).length;
}
/**
* The function `outDegreeOf` returns the number of outgoing edges from a given vertex.
* @param {VertexKey | V} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `V`.
* @param {VertexKey | VO} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `VO`.
* @returns The number of outgoing edges from the specified vertex or vertex ID.
*/
outDegreeOf(vertexOrKey: VertexKey | V): number {
outDegreeOf(vertexOrKey: VertexKey | VO): number {
return this.outgoingEdgesOf(vertexOrKey).length;
}
/**
* The function "edgesOf" returns an array of both outgoing and incoming edges of a given vertex or vertex ID.
* @param {VertexKey | V} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `V`.
* @param {VertexKey | VO} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `VO`.
* @returns The function `edgesOf` returns an array of edges.
*/
edgesOf(vertexOrKey: VertexKey | V): E[] {
edgesOf(vertexOrKey: VertexKey | VO): EO[] {
return [...this.outgoingEdgesOf(vertexOrKey), ...this.incomingEdgesOf(vertexOrKey)];
}
/**
* The function "getEdgeSrc" returns the source vertex of an edge, or null if the edge does not exist.
* @param {E} e - The parameter "e" is of type E, which represents an edge in a graph.
* @returns either a vertex object (V) or null.
* @param {EO} e - The parameter "e" is of type EO, which represents an edge in a graph.
* @returns either a vertex object (VO) or null.
*/
getEdgeSrc(e: E): V | null {
getEdgeSrc(e: EO): VO | null {
return this._getVertex(e.src);
}
/**
* The function "getEdgeDest" returns the destination vertex of an edge.
* @param {E} e - The parameter "e" is of type "E", which represents an edge in a graph.
* @returns either a vertex object of type V or null.
* @param {EO} e - The parameter "e" is of type "EO", which represents an edge in a graph.
* @returns either a vertex object of type VO or null.
*/
getEdgeDest(e: E): V | null {
getEdgeDest(e: EO): VO | null {
return this._getVertex(e.dest);
}
/**
* The function `getDestinations` returns an array of destination vertices connected to a given vertex.
* @param {V | VertexKey | null} vertex - The `vertex` parameter represents the starting vertex from which we want to
* find the destinations. It can be either a `V` object, a `VertexKey` value, or `null`.
* @returns an array of vertices (V[]).
* @param {VO | VertexKey | null} vertex - The `vertex` parameter represents the starting vertex from which we want to
* find the destinations. It can be either a `VO` object, a `VertexKey` value, or `null`.
* @returns an array of vertices (VO[]).
*/
getDestinations(vertex: V | VertexKey | null): V[] {
getDestinations(vertex: VO | VertexKey | null): VO[] {
if (vertex === null) {
return [];
}
const destinations: V[] = [];
const destinations: VO[] = [];
const outgoingEdges = this.outgoingEdgesOf(vertex);
for (const outEdge of outgoingEdges) {
const child = this.getEdgeDest(outEdge);
@ -332,18 +319,18 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
* specified, the vertices themselves will be used for sorting. If 'key' is specified, the ids of
* @returns an array of vertices or vertex IDs in topological order. If there is a cycle in the graph, it returns null.
*/
topologicalSort(propertyName?: 'vertex' | 'key'): Array<V | VertexKey> | null {
topologicalSort(propertyName?: 'vertex' | 'key'): Array<VO | VertexKey> | null {
propertyName = propertyName ?? 'key';
// When judging whether there is a cycle in the undirected graph, all nodes with degree of **<= 1** are enqueued
// When judging whether there is a cycle in the directed graph, all nodes with **in degree = 0** are enqueued
const statusMap: Map<V | VertexKey, TopologicalStatus> = new Map<V | VertexKey, TopologicalStatus>();
const statusMap: Map<VO | VertexKey, TopologicalStatus> = new Map<VO | VertexKey, TopologicalStatus>();
for (const entry of this.vertices) {
statusMap.set(entry[1], 0);
}
let sorted: (V | VertexKey)[] = [];
let sorted: (VO | VertexKey)[] = [];
let hasCycle = false;
const dfs = (cur: V | VertexKey) => {
const dfs = (cur: VO | VertexKey) => {
statusMap.set(cur, 1);
const children = this.getDestinations(cur);
for (const child of children) {
@ -372,10 +359,10 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The `edgeSet` function returns an array of all the edges in the graph.
* @returns The `edgeSet()` method returns an array of edges (`E[]`).
* @returns The `edgeSet()` method returns an array of edges (`EO[]`).
*/
edgeSet(): E[] {
let edges: E[] = [];
edgeSet(): EO[] {
let edges: EO[] = [];
this._outEdgeMap.forEach(outEdges => {
edges = [...edges, ...outEdges];
});
@ -384,12 +371,12 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function `getNeighbors` returns an array of neighboring vertices of a given vertex or vertex ID in a graph.
* @param {V | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`V`) or a vertex ID
* @param {VO | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`VO`) or a vertex ID
* (`VertexKey`).
* @returns an array of vertices (V[]).
* @returns an array of vertices (VO[]).
*/
getNeighbors(vertexOrKey: V | VertexKey): V[] {
const neighbors: V[] = [];
getNeighbors(vertexOrKey: VO | VertexKey): VO[] {
const neighbors: VO[] = [];
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
const outEdges = this.outgoingEdgesOf(vertex);
@ -407,11 +394,11 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function "getEndsOfEdge" returns the source and destination vertices of an edge if it exists in the graph,
* otherwise it returns null.
* @param {E} edge - The parameter `edge` is of type `E`, which represents an edge in a graph.
* @returns The function `getEndsOfEdge` returns an array containing two vertices `[V, V]` if the edge exists in the
* @param {EO} edge - The parameter `edge` is of type `EO`, which represents an edge in a graph.
* @returns The function `getEndsOfEdge` returns an array containing two vertices `[VO, VO]` if the edge exists in the
* graph. If the edge does not exist, it returns `null`.
*/
getEndsOfEdge(edge: E): [V, V] | null {
getEndsOfEdge(edge: EO): [VO, VO] | null {
if (!this.hasEdge(edge.src, edge.dest)) {
return null;
}
@ -426,12 +413,12 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
/**
* The function `_addEdgeOnly` adds an edge to a graph if the source and destination vertices exist.
* @param {E} edge - The parameter `edge` is of type `E`, which represents an edge in a graph. It is the edge that
* @param {EO} edge - The parameter `edge` is of type `EO`, which represents an edge in a graph. It is the edge that
* needs to be added to the graph.
* @returns a boolean value. It returns true if the edge was successfully added to the graph, and false if either the
* source or destination vertex does not exist in the graph.
*/
protected _addEdgeOnly(edge: E): boolean {
protected _addEdgeOnly(edge: EO): boolean {
if (!(this.hasVertex(edge.src) && this.hasVertex(edge.dest))) {
return false;
}
@ -459,12 +446,4 @@ export class DirectedGraph<V extends DirectedVertex<any> = DirectedVertex, E ext
return false;
}
}
protected _setOutEdgeMap(value: Map<V, E[]>) {
this._outEdgeMap = value;
}
protected _setInEdgeMap(value: Map<V, E[]>) {
this._inEdgeMap = value;
}
}

View file

@ -2,6 +2,9 @@ import {MapGraphCoordinate, VertexKey} from '../../types';
import {DirectedEdge, DirectedGraph, DirectedVertex} from './directed-graph';
export class MapVertex<V = any> extends DirectedVertex<V> {
lat: number;
long: number;
/**
* The constructor function initializes an object with an key, latitude, longitude, and an optional value.
* @param {VertexKey} key - The `key` parameter is of type `VertexKey` and represents the identifier of the vertex.
@ -11,37 +14,17 @@ export class MapVertex<V = any> extends DirectedVertex<V> {
* @param {number} long - The "long" parameter represents the longitude of a location. Longitude is a geographic
* coordinate that specifies the east-west position of a point on the Earth's surface. It is measured in degrees, with
* values ranging from -180 to 180.
* @param {V} [val] - The "val" parameter is an optional value of type V. It is not required to be provided when
* @param {V} [value] - The "value" parameter is an optional value of type V. It is not required to be provided when
* creating an instance of the class.
*/
constructor(key: VertexKey, lat: number, long: number, val?: V) {
super(key, val);
this._lat = lat;
this._long = long;
}
private _lat: number;
get lat(): number {
return this._lat;
}
set lat(value: number) {
this._lat = value;
}
private _long: number;
get long(): number {
return this._long;
}
set long(value: number) {
this._long = value;
constructor(key: VertexKey, value: V, lat: number, long: number) {
super(key, value);
this.lat = lat;
this.long = long;
}
}
export class MapEdge<V = any> extends DirectedEdge<V> {
export class MapEdge<E = any> extends DirectedEdge<E> {
/**
* The constructor function initializes a new instance of a class with the given source, destination, weight, and
* value.
@ -49,18 +32,20 @@ export class MapEdge<V = any> extends DirectedEdge<V> {
* a graph.
* @param {VertexKey} dest - The `dest` parameter is the identifier of the destination vertex for an edge.
* @param {number} [weight] - The weight parameter is an optional number that represents the weight of the edge.
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to store additional
* @param {E} [value] - The "value" parameter is an optional parameter of type E. It is used to store additional
* information or data associated with the edge.
*/
constructor(src: VertexKey, dest: VertexKey, weight?: number, val?: V) {
super(src, dest, weight, val);
constructor(src: VertexKey, dest: VertexKey, weight?: number, value?: E) {
super(src, dest, weight, value);
}
}
export class MapGraph<V extends MapVertex<V['val']> = MapVertex, E extends MapEdge = MapEdge> extends DirectedGraph<
V,
E
> {
export class MapGraph<
V = any,
E = any,
VO extends MapVertex<V> = MapVertex<V>,
EO extends MapEdge<E> = MapEdge<E>
> extends DirectedGraph<V, E, VO, EO> {
/**
* The constructor function initializes the origin and bottomRight properties of a MapGraphCoordinate object.
* @param {MapGraphCoordinate} origin - The `origin` parameter is a `MapGraphCoordinate` object that represents the
@ -76,44 +61,31 @@ export class MapGraph<V extends MapVertex<V['val']> = MapVertex, E extends MapEd
this._bottomRight = bottomRight;
}
private _origin: MapGraphCoordinate = [0, 0];
protected _origin: MapGraphCoordinate = [0, 0];
get origin(): MapGraphCoordinate {
return this._origin;
}
set origin(value: MapGraphCoordinate) {
this._origin = value;
}
private _bottomRight: MapGraphCoordinate | undefined;
protected _bottomRight: MapGraphCoordinate | undefined;
get bottomRight(): MapGraphCoordinate | undefined {
return this._bottomRight;
}
set bottomRight(value: MapGraphCoordinate | undefined) {
this._bottomRight = value;
}
/**
* The function creates a new vertex with the given key, value, latitude, and longitude.
* @param {VertexKey} key - The key parameter is the unique identifier for the vertex. It is of type VertexKey, which could
* be a string or a number depending on how you define it in your code.
* @param [val] - The `val` parameter is an optional value that can be assigned to the `val` property of the vertex. It
* is of type `V['val']`, which means it should be of the same type as the `val` property of the vertex class `V`.
* @param [value] - The `value` parameter is an optional value that can be assigned to the `value` property of the vertex. It
* is of type `V`, which means it should be of the same type as the `value` property of the vertex class `VO`.
* @param {number} lat - The `lat` parameter represents the latitude of the vertex. It is a number that specifies the
* position of the vertex on the Earth's surface in the north-south direction.
* @param {number} long - The `long` parameter represents the longitude coordinate of the vertex.
* @returns The method is returning a new instance of the `MapVertex` class, casted as type `V`.
* @returns The method is returning a new instance of the `MapVertex` class, casted as type `VO`.
*/
override createVertex(
key: VertexKey,
val?: V['val'],
lat: number = this.origin[0],
long: number = this.origin[1]
): V {
return new MapVertex(key, lat, long, val) as V;
override createVertex(key: VertexKey, value?: V, lat: number = this.origin[0], long: number = this.origin[1]): VO {
return new MapVertex(key, value, lat, long) as VO;
}
/**
@ -124,11 +96,11 @@ export class MapGraph<V extends MapVertex<V['val']> = MapVertex, E extends MapEd
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the edge. It
* is used to assign a numerical value to the edge, which can be used in algorithms such as shortest path algorithms.
* If the weight is not provided, it can be set to a default value or left undefined.
* @param [val] - The `val` parameter is an optional value that can be assigned to the edge. It can be of any type,
* @param [value] - The `value` parameter is an optional value that can be assigned to the edge. It can be of any type,
* depending on the specific implementation of the `MapEdge` class.
* @returns a new instance of the `MapEdge` class, cast as type `E`.
* @returns a new instance of the `MapEdge` class, cast as type `EO`.
*/
override createEdge(src: VertexKey, dest: VertexKey, weight?: number, val?: E['val']): E {
return new MapEdge(src, dest, weight, val) as E;
override createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO {
return new MapEdge(src, dest, weight, value) as EO;
}
}

View file

@ -15,15 +15,17 @@ export class UndirectedVertex<V = any> extends AbstractVertex<V> {
* The constructor function initializes a vertex with an optional value.
* @param {VertexKey} key - The `key` parameter is of type `VertexKey` and represents the identifier of the vertex. It is
* used to uniquely identify the vertex within a graph or network.
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to initialize the value of the
* @param {V} [value] - The "value" parameter is an optional parameter of type V. It is used to initialize the value of the
* vertex. If no value is provided, the vertex will be initialized with a default value.
*/
constructor(key: VertexKey, val?: V) {
super(key, val);
constructor(key: VertexKey, value?: V) {
super(key, value);
}
}
export class UndirectedEdge<V = number> extends AbstractEdge<V> {
export class UndirectedEdge<E = number> extends AbstractEdge<E> {
vertices: [VertexKey, VertexKey];
/**
* The constructor function creates an instance of a class with two vertex IDs, an optional weight, and an optional
* value.
@ -31,43 +33,34 @@ export class UndirectedEdge<V = number> extends AbstractEdge<V> {
* @param {VertexKey} v2 - The parameter `v2` is a `VertexKey`, which represents the identifier of the second vertex in a
* graph edge.
* @param {number} [weight] - The weight parameter is an optional number that represents the weight of the edge.
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to store a value associated
* @param {E} [value] - The "value" parameter is an optional parameter of type E. It is used to store a value associated
* with the edge.
*/
constructor(v1: VertexKey, v2: VertexKey, weight?: number, val?: V) {
super(weight, val);
this._vertices = [v1, v2];
}
private _vertices: [VertexKey, VertexKey];
get vertices() {
return this._vertices;
}
set vertices(v: [VertexKey, VertexKey]) {
this._vertices = v;
constructor(v1: VertexKey, v2: VertexKey, weight?: number, value?: E) {
super(weight, value);
this.vertices = [v1, v2];
}
}
export class UndirectedGraph<
V extends UndirectedVertex<any> = UndirectedVertex,
E extends UndirectedEdge<any> = UndirectedEdge
>
extends AbstractGraph<V, E>
implements IGraph<V, E>
{
V = any,
E = any,
VO extends UndirectedVertex<V> = UndirectedVertex<V>,
EO extends UndirectedEdge<E> = UndirectedEdge<E>
>
extends AbstractGraph<V, E, VO, EO>
implements IGraph<V, E, VO, EO> {
/**
* The constructor initializes a new Map object to store edges.
*/
constructor() {
super();
this._edges = new Map<V, E[]>();
this._edges = new Map<VO, EO[]>();
}
protected _edges: Map<V, E[]>;
protected _edges: Map<VO, EO[]>;
get edges(): Map<V, E[]> {
get edges(): Map<VO, EO[]> {
return this._edges;
}
@ -75,13 +68,13 @@ export class UndirectedGraph<
* The function creates a new vertex with an optional value and returns it.
* @param {VertexKey} key - The `key` parameter is the unique identifier for the vertex. It is used to distinguish one
* vertex from another in the graph.
* @param [val] - The `val` parameter is an optional value that can be assigned to the vertex. If a value is provided,
* @param [value] - The `value` parameter is an optional value that can be assigned to the vertex. If a value is provided,
* it will be used as the value of the vertex. If no value is provided, the `key` parameter will be used as the value of
* the vertex.
* @returns The method is returning a new instance of the `UndirectedVertex` class, casted as type `V`.
* @returns The method is returning a new instance of the `UndirectedVertex` class, casted as type `VO`.
*/
override createVertex(key: VertexKey, val?: V['val']): V {
return new UndirectedVertex(key, val ?? key) as V;
override createVertex(key: VertexKey, value?: VO['value']): VO {
return new UndirectedVertex(key, value ?? key) as VO;
}
/**
@ -90,28 +83,28 @@ export class UndirectedGraph<
* @param {VertexKey} v2 - The parameter `v2` represents the second vertex of the edge.
* @param {number} [weight] - The `weight` parameter is an optional number that represents the weight of the edge. If
* no weight is provided, it defaults to 1.
* @param [val] - The `val` parameter is an optional value that can be assigned to the edge. It can be of any type and
* @param [value] - The `value` parameter is an optional value that can be assigned to the edge. It can be of any type and
* is used to store additional information or data associated with the edge.
* @returns a new instance of the `UndirectedEdge` class, which is casted as type `E`.
* @returns a new instance of the `UndirectedEdge` class, which is casted as type `EO`.
*/
override createEdge(v1: VertexKey, v2: VertexKey, weight?: number, val?: E['val']): E {
return new UndirectedEdge(v1, v2, weight ?? 1, val) as E;
override createEdge(v1: VertexKey, v2: VertexKey, weight?: number, value?: EO['value']): EO {
return new UndirectedEdge(v1, v2, weight ?? 1, value) as EO;
}
/**
* The function `getEdge` returns the first edge that connects two vertices, or null if no such edge exists.
* @param {V | null | VertexKey} v1 - The parameter `v1` represents a vertex or vertex ID. It can be of type `V` (vertex
* @param {VO | VertexKey | null} v1 - The parameter `v1` represents a vertex or vertex ID. It can be of type `VO` (vertex
* object), `null`, or `VertexKey` (a string or number representing the ID of a vertex).
* @param {V | null | VertexKey} v2 - The parameter `v2` represents a vertex or vertex ID. It can be of type `V` (vertex
* @param {VO | VertexKey | null} v2 - The parameter `v2` represents a vertex or vertex ID. It can be of type `VO` (vertex
* object), `null`, or `VertexKey` (vertex ID).
* @returns an edge (E) or null.
* @returns an edge (EO) or null.
*/
getEdge(v1: V | null | VertexKey, v2: V | null | VertexKey): E | null {
let edges: E[] | undefined = [];
getEdge(v1: VO | VertexKey | null, v2: VO | VertexKey | null): EO | null {
let edges: EO[] | undefined = [];
if (v1 !== null && v2 !== null) {
const vertex1: V | null = this._getVertex(v1);
const vertex2: V | null = this._getVertex(v2);
const vertex1: VO | null = this._getVertex(v1);
const vertex2: VO | null = this._getVertex(v2);
if (vertex1 && vertex2) {
edges = this._edges.get(vertex1)?.filter(e => e.vertices.includes(vertex2.key));
@ -123,48 +116,48 @@ export class UndirectedGraph<
/**
* The function removes an edge between two vertices in a graph and returns the removed edge.
* @param {V | VertexKey} v1 - The parameter `v1` represents either a vertex object (`V`) or a vertex ID (`VertexKey`).
* @param {V | VertexKey} v2 - V | VertexKey - This parameter can be either a vertex object (V) or a vertex ID
* @param {VO | VertexKey} v1 - The parameter `v1` represents either a vertex object (`VO`) or a vertex ID (`VertexKey`).
* @param {VO | VertexKey} v2 - VO | VertexKey - This parameter can be either a vertex object (VO) or a vertex ID
* (VertexKey). It represents the second vertex of the edge that needs to be removed.
* @returns the removed edge (E) if it exists, or null if either of the vertices (V) does not exist.
* @returns the removed edge (EO) if it exists, or null if either of the vertices (VO) does not exist.
*/
removeEdgeBetween(v1: V | VertexKey, v2: V | VertexKey): E | null {
const vertex1: V | null = this._getVertex(v1);
const vertex2: V | null = this._getVertex(v2);
deleteEdgeBetween(v1: VO | VertexKey, v2: VO | VertexKey): EO | null {
const vertex1: VO | null = this._getVertex(v1);
const vertex2: VO | null = this._getVertex(v2);
if (!vertex1 || !vertex2) {
return null;
}
const v1Edges = this._edges.get(vertex1);
let removed: E | null = null;
let removed: EO | null = null;
if (v1Edges) {
removed = arrayRemove<E>(v1Edges, (e: E) => e.vertices.includes(vertex2.key))[0] || null;
removed = arrayRemove<EO>(v1Edges, (e: EO) => e.vertices.includes(vertex2.key))[0] || null;
}
const v2Edges = this._edges.get(vertex2);
if (v2Edges) {
arrayRemove<E>(v2Edges, (e: E) => e.vertices.includes(vertex1.key));
arrayRemove<EO>(v2Edges, (e: EO) => e.vertices.includes(vertex1.key));
}
return removed;
}
/**
* The removeEdge function removes an edge between two vertices in a graph.
* @param {E} edge - The parameter "edge" is of type E, which represents an edge in a graph.
* @returns The method is returning either the removed edge (of type E) or null if the edge was not found.
* The deleteEdge function removes an edge between two vertices in a graph.
* @param {EO} edge - The parameter "edge" is of type EO, which represents an edge in a graph.
* @returns The method is returning either the removed edge (of type EO) or null if the edge was not found.
*/
removeEdge(edge: E): E | null {
return this.removeEdgeBetween(edge.vertices[0], edge.vertices[1]);
deleteEdge(edge: EO): EO | null {
return this.deleteEdgeBetween(edge.vertices[0], edge.vertices[1]);
}
/**
* The function `degreeOf` returns the degree of a vertex in a graph, which is the number of edges connected to that
* vertex.
* @param {VertexKey | V} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `V`.
* @param {VertexKey | VO} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `VO`.
* @returns The function `degreeOf` returns the degree of a vertex in a graph. The degree of a vertex is the number of
* edges connected to that vertex.
*/
degreeOf(vertexOrKey: VertexKey | V): number {
degreeOf(vertexOrKey: VertexKey | VO): number {
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
return this._edges.get(vertex)?.length || 0;
@ -175,11 +168,11 @@ export class UndirectedGraph<
/**
* The function returns the edges of a given vertex or vertex ID.
* @param {VertexKey | V} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `V`. A `VertexKey` is a
* unique identifier for a vertex in a graph, while `V` represents the type of the vertex.
* @param {VertexKey | VO} vertexOrKey - The parameter `vertexOrKey` can be either a `VertexKey` or a `VO`. A `VertexKey` is a
* unique identifier for a vertex in a graph, while `VO` represents the type of the vertex.
* @returns an array of edges.
*/
edgesOf(vertexOrKey: VertexKey | V): E[] {
edgesOf(vertexOrKey: VertexKey | VO): EO[] {
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
return this._edges.get(vertex) || [];
@ -190,10 +183,10 @@ export class UndirectedGraph<
/**
* The function "edgeSet" returns an array of unique edges from a set of edges.
* @returns The method `edgeSet()` returns an array of type `E[]`.
* @returns The method `edgeSet()` returns an array of type `EO[]`.
*/
edgeSet(): E[] {
const edgeSet: Set<E> = new Set();
edgeSet(): EO[] {
const edgeSet: Set<EO> = new Set();
this._edges.forEach(edges => {
edges.forEach(edge => {
edgeSet.add(edge);
@ -204,12 +197,12 @@ export class UndirectedGraph<
/**
* The function "getNeighbors" returns an array of neighboring vertices for a given vertex or vertex ID.
* @param {V | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`V`) or a vertex ID
* @param {VO | VertexKey} vertexOrKey - The parameter `vertexOrKey` can be either a vertex object (`VO`) or a vertex ID
* (`VertexKey`).
* @returns an array of vertices (V[]).
* @returns an array of vertices (VO[]).
*/
getNeighbors(vertexOrKey: V | VertexKey): V[] {
const neighbors: V[] = [];
getNeighbors(vertexOrKey: VO | VertexKey): VO[] {
const neighbors: VO[] = [];
const vertex = this._getVertex(vertexOrKey);
if (vertex) {
const neighborEdges = this.edgesOf(vertex);
@ -226,11 +219,11 @@ export class UndirectedGraph<
/**
* The function "getEndsOfEdge" returns the vertices at the ends of an edge if the edge exists in the graph, otherwise
* it returns null.
* @param {E} edge - The parameter "edge" is of type E, which represents an edge in a graph.
* @returns The function `getEndsOfEdge` returns an array containing two vertices `[V, V]` if the edge exists in the
* @param {EO} edge - The parameter "edge" is of type EO, which represents an edge in a graph.
* @returns The function `getEndsOfEdge` returns an array containing two vertices `[VO, VO]` if the edge exists in the
* graph. If the edge does not exist, it returns `null`.
*/
getEndsOfEdge(edge: E): [V, V] | null {
getEndsOfEdge(edge: EO): [VO, VO] | null {
if (!this.hasEdge(edge.vertices[0], edge.vertices[1])) {
return null;
}
@ -245,10 +238,10 @@ export class UndirectedGraph<
/**
* The function adds an edge to the graph by updating the adjacency list with the vertices of the edge.
* @param {E} edge - The parameter "edge" is of type E, which represents an edge in a graph.
* @param {EO} edge - The parameter "edge" is of type EO, which represents an edge in a graph.
* @returns a boolean value.
*/
protected _addEdgeOnly(edge: E): boolean {
protected _addEdgeOnly(edge: EO): boolean {
for (const end of edge.vertices) {
const endVertex = this._getVertex(end);
if (endVertex === null) return false;
@ -263,12 +256,4 @@ export class UndirectedGraph<
}
return true;
}
/**
* The function sets the edges of a graph.
* @param v - A map where the keys are of type V and the values are arrays of type E.
*/
protected _setEdges(v: Map<V, E[]>) {
this._edges = v;
}
}

View file

@ -60,8 +60,4 @@ export class CoordinateMap<V> extends Map<any, V> {
override delete(key: number[]) {
return super.delete(key.join(this._joint));
}
protected _setJoint(v: string) {
this._joint = v;
}
}

View file

@ -49,8 +49,4 @@ export class CoordinateSet extends Set<any> {
override delete(value: number[]) {
return super.delete(value.join(this._joint));
}
protected _setJoint(v: string) {
this._joint = v;
}
}

View file

@ -8,60 +8,6 @@ import {HashFunction} from '../../types';
* @license MIT License
*/
export class HashMap<K, V> {
get hashFn(): HashFunction<K> {
return this._hashFn;
}
set hashFn(value: HashFunction<K>) {
this._hashFn = value;
}
get table(): Array<Array<[K, V]>> {
return this._table;
}
set table(value: Array<Array<[K, V]>>) {
this._table = value;
}
get capacityMultiplier(): number {
return this._capacityMultiplier;
}
set capacityMultiplier(value: number) {
this._capacityMultiplier = value;
}
get loadFactor(): number {
return this._loadFactor;
}
set loadFactor(value: number) {
this._loadFactor = value;
}
get initialCapacity(): number {
return this._initialCapacity;
}
set initialCapacity(value: number) {
this._initialCapacity = value;
}
get size(): number {
return this._size;
}
set size(value: number) {
this._size = value;
}
private _initialCapacity: number;
private _loadFactor: number;
private _capacityMultiplier: number;
private _size: number;
private _table: Array<Array<[K, V]>>;
private _hashFn: HashFunction<K>;
/**
* The constructor initializes the properties of a hash table, including the initial capacity, load factor, capacity
* multiplier, size, table array, and hash function.
@ -92,31 +38,40 @@ export class HashMap<K, V> {
});
}
private _hash(key: K): number {
return this._hashFn(key);
protected _initialCapacity: number;
get initialCapacity(): number {
return this._initialCapacity;
}
/**
* The `resizeTable` function resizes the table used in a hash map by creating a new table with a specified capacity and
* rehashing the key-value pairs from the old table into the new table.
* @param {number} newCapacity - The newCapacity parameter is the desired capacity for the resized table. It represents
* the number of buckets that the new table should have.
*/
private resizeTable(newCapacity: number): void {
const newTable = new Array(newCapacity);
for (const bucket of this._table) {
// Note that this is this._table
if (bucket) {
for (const [key, value] of bucket) {
const newIndex = this._hash(key) % newCapacity;
if (!newTable[newIndex]) {
newTable[newIndex] = [];
}
newTable[newIndex].push([key, value]);
}
}
}
this._table = newTable; // Again, here is this._table
protected _loadFactor: number;
get loadFactor(): number {
return this._loadFactor;
}
protected _capacityMultiplier: number;
get capacityMultiplier(): number {
return this._capacityMultiplier;
}
protected _size: number;
get size(): number {
return this._size;
}
protected _table: Array<Array<[K, V]>>;
get table(): Array<Array<[K, V]>> {
return this._table;
}
protected _hashFn: HashFunction<K>;
get hashFn(): HashFunction<K> {
return this._hashFn;
}
set(key: K, value: V): void {
@ -139,7 +94,7 @@ export class HashMap<K, V> {
}
this.table[index].push([key, value]);
this.size++;
this._size++;
}
get(key: K): V | undefined {
@ -157,7 +112,7 @@ export class HashMap<K, V> {
return undefined;
}
remove(key: K): void {
delete(key: K): void {
const index = this._hash(key);
if (!this.table[index]) {
return;
@ -166,7 +121,7 @@ export class HashMap<K, V> {
for (let i = 0; i < this.table[index].length; i++) {
if (this.table[index][i][0] === key) {
this.table[index].splice(i, 1);
this.size--;
this._size--;
// Check if the table needs to be resized down
const loadFactor = this.size / this.table.length;
@ -178,7 +133,7 @@ export class HashMap<K, V> {
}
}
*entries(): IterableIterator<[K, V]> {
* entries(): IterableIterator<[K, V]> {
for (const bucket of this.table) {
if (bucket) {
for (const [key, value] of bucket) {
@ -193,11 +148,38 @@ export class HashMap<K, V> {
}
clear(): void {
this.size = 0;
this.table = new Array(this.initialCapacity);
this._size = 0;
this._table = new Array(this.initialCapacity);
}
isEmpty(): boolean {
return this.size === 0;
}
protected _hash(key: K): number {
return this._hashFn(key);
}
/**
* The `resizeTable` function resizes the table used in a hash map by creating a new table with a specified capacity and
* rehashing the key-value pairs from the old table into the new table.
* @param {number} newCapacity - The newCapacity parameter is the desired capacity for the resized table. It represents
* the number of buckets that the new table should have.
*/
protected resizeTable(newCapacity: number): void {
const newTable = new Array(newCapacity);
for (const bucket of this._table) {
// Note that this is this._table
if (bucket) {
for (const [key, value] of bucket) {
const newIndex = this._hash(key) % newCapacity;
if (!newTable[newIndex]) {
newTable[newIndex] = [];
}
newTable[newIndex].push([key, value]);
}
}
}
this._table = newTable; // Again, here is this._table
}
}

View file

@ -8,12 +8,12 @@
export class HashTableNode<K, V> {
key: K;
val: V;
value: V;
next: HashTableNode<K, V> | null;
constructor(key: K, val: V) {
constructor(key: K, value: V) {
this.key = key;
this.val = val;
this.value = value;
this.next = null;
}
}
@ -21,37 +21,8 @@ export class HashTableNode<K, V> {
import {HashFunction} from '../../types';
export class HashTable<K, V> {
get hashFn(): HashFunction<K> {
return this._hashFn;
}
set hashFn(value: HashFunction<K>) {
this._hashFn = value;
}
get buckets(): Array<HashTableNode<K, V> | null> {
return this._buckets;
}
set buckets(value: Array<HashTableNode<K, V> | null>) {
this._buckets = value;
}
get capacity(): number {
return this._capacity;
}
set capacity(value: number) {
this._capacity = value;
}
private static readonly DEFAULT_CAPACITY = 16;
private static readonly LOAD_FACTOR = 0.75;
private _capacity: number;
private _size: number;
private _buckets: Array<HashTableNode<K, V> | null>;
private _hashFn: HashFunction<K>;
protected static readonly DEFAULT_CAPACITY = 16;
protected static readonly LOAD_FACTOR = 0.75;
constructor(capacity: number = HashTable.DEFAULT_CAPACITY, hashFn?: HashFunction<K>) {
this._hashFn = hashFn || this._defaultHashFn;
@ -60,6 +31,118 @@ export class HashTable<K, V> {
this._buckets = new Array<HashTableNode<K, V> | null>(this._capacity).fill(null);
}
protected _capacity: number;
get capacity(): number {
return this._capacity;
}
protected _size: number;
get size(): number {
return this._size;
}
protected _buckets: Array<HashTableNode<K, V> | null>;
get buckets(): Array<HashTableNode<K, V> | null> {
return this._buckets;
}
protected _hashFn: HashFunction<K>;
get hashFn(): HashFunction<K> {
return this._hashFn;
}
/**
* The set function adds a key-value pair to the hash table, handling collisions and resizing if necessary.
* @param {K} key - The key parameter represents the key of the key-value pair that you want to insert into the hash
* table. It is of type K, which is a generic type representing the key's data type.
* @param {V} value - The parameter `value` represents the value that you want to associate with the given key in the hash
* table.
* @returns Nothing is being returned. The return type of the `put` method is `void`, which means it does not return any
* value.
*/
set(key: K, value: V): void {
const index = this._hash(key);
const newNode = new HashTableNode<K, V>(key, value);
if (!this._buckets[index]) {
this._buckets[index] = newNode;
} else {
// Handle collisions, consider using open addressing, etc.
let currentNode = this._buckets[index]!;
while (currentNode) {
if (currentNode.key === key) {
// If the key already exists, update the value
currentNode.value = value;
return;
}
if (!currentNode.next) {
break;
}
currentNode = currentNode.next;
}
// Add to the end of the linked list
currentNode.next = newNode;
}
this._size++;
// If the load factor is too high, resize the hash table
if (this._size / this._capacity >= HashTable.LOAD_FACTOR) {
this._expand();
}
}
/**
* The `get` function retrieves the value associated with a given key from a hash table.
* @param {K} key - The `key` parameter represents the key of the element that we want to retrieve from the data
* structure.
* @returns The method is returning the value associated with the given key if it exists in the hash table. If the key is
* not found, it returns `undefined`.
*/
get(key: K): V | undefined {
const index = this._hash(key);
let currentNode = this._buckets[index];
while (currentNode) {
if (currentNode.key === key) {
return currentNode.value;
}
currentNode = currentNode.next;
}
return undefined; // Key not found
}
/**
* The delete function removes a key-value pair from a hash table.
* @param {K} key - The `key` parameter represents the key of the key-value pair that needs to be removed from the hash
* table.
* @returns Nothing is being returned. The `delete` method has a return type of `void`, which means it does not return
* any value.
*/
delete(key: K): void {
const index = this._hash(key);
let currentNode = this._buckets[index];
let prevNode: HashTableNode<K, V> | null = null;
while (currentNode) {
if (currentNode.key === key) {
if (prevNode) {
prevNode.next = currentNode.next;
} else {
this._buckets[index] = currentNode.next;
}
this._size--;
currentNode.next = null; // Release memory
return;
}
prevNode = currentNode;
currentNode = currentNode.next;
}
}
/**
* The function `_defaultHashFn` calculates the hash value of a given key and returns the remainder when divided by the
* capacity of the data structure.
@ -152,94 +235,6 @@ export class HashTable<K, V> {
return this._stringHash(JSON.stringify(key));
}
/**
* The set function adds a key-value pair to the hash table, handling collisions and resizing if necessary.
* @param {K} key - The key parameter represents the key of the key-value pair that you want to insert into the hash
* table. It is of type K, which is a generic type representing the key's data type.
* @param {V} val - The parameter `val` represents the value that you want to associate with the given key in the hash
* table.
* @returns Nothing is being returned. The return type of the `put` method is `void`, which means it does not return any
* value.
*/
set(key: K, val: V): void {
const index = this._hash(key);
const newNode = new HashTableNode<K, V>(key, val);
if (!this._buckets[index]) {
this._buckets[index] = newNode;
} else {
// Handle collisions, consider using open addressing, etc.
let currentNode = this._buckets[index]!;
while (currentNode) {
if (currentNode.key === key) {
// If the key already exists, update the value
currentNode.val = val;
return;
}
if (!currentNode.next) {
break;
}
currentNode = currentNode.next;
}
// Add to the end of the linked list
currentNode.next = newNode;
}
this._size++;
// If the load factor is too high, resize the hash table
if (this._size / this._capacity >= HashTable.LOAD_FACTOR) {
this._expand();
}
}
/**
* The `get` function retrieves the value associated with a given key from a hash table.
* @param {K} key - The `key` parameter represents the key of the element that we want to retrieve from the data
* structure.
* @returns The method is returning the value associated with the given key if it exists in the hash table. If the key is
* not found, it returns `undefined`.
*/
get(key: K): V | undefined {
const index = this._hash(key);
let currentNode = this._buckets[index];
while (currentNode) {
if (currentNode.key === key) {
return currentNode.val;
}
currentNode = currentNode.next;
}
return undefined; // Key not found
}
/**
* The remove function removes a key-value pair from a hash table.
* @param {K} key - The `key` parameter represents the key of the key-value pair that needs to be removed from the hash
* table.
* @returns Nothing is being returned. The `remove` method has a return type of `void`, which means it does not return
* any value.
*/
remove(key: K): void {
const index = this._hash(key);
let currentNode = this._buckets[index];
let prevNode: HashTableNode<K, V> | null = null;
while (currentNode) {
if (currentNode.key === key) {
if (prevNode) {
prevNode.next = currentNode.next;
} else {
this._buckets[index] = currentNode.next;
}
this._size--;
currentNode.next = null; // Release memory
return;
}
prevNode = currentNode;
currentNode = currentNode.next;
}
}
/**
* The `expand` function increases the capacity of a hash table by creating a new array of buckets with double the
* capacity and rehashing all the existing key-value pairs into the new buckets.
@ -252,7 +247,7 @@ export class HashTable<K, V> {
let currentNode = bucket;
while (currentNode) {
const newIndex = this._hash(currentNode.key);
const newNode = new HashTableNode<K, V>(currentNode.key, currentNode.val);
const newNode = new HashTableNode<K, V>(currentNode.key, currentNode.value);
if (!newBuckets[newIndex]) {
newBuckets[newIndex] = newNode;
@ -270,8 +265,4 @@ export class HashTable<K, V> {
this._buckets = newBuckets;
this._capacity = newCapacity;
}
get size(): number {
return this._size;
}
}

View file

@ -1 +1,2 @@
export class TreeMap {}
export class TreeMap {
}

View file

@ -1 +1,2 @@
export class TreeSet {}
export class TreeSet {
}

View file

@ -5,33 +5,78 @@
* @license MIT License
*/
import type {HeapComparator, HeapDFSOrderPattern} from '../../types';
import type {Comparator, DFSOrderPattern} from '../../types';
export class Heap<E> {
protected nodes: E[] = [];
private readonly comparator: HeapComparator<E>;
export class Heap<E = any> {
constructor(options: { comparator: Comparator<E>; nodes?: E[] }) {
this._comparator = options.comparator;
if (options.nodes && options.nodes.length > 0) {
this._nodes = options.nodes;
this.fix();
}
}
constructor(comparator: HeapComparator<E>) {
this.comparator = comparator;
protected _nodes: E[] = [];
get nodes(): E[] {
return this._nodes;
}
protected _comparator: Comparator<E>;
get comparator(): Comparator<E> {
return this._comparator;
}
/**
* Get the size (number of elements) of the heap.
*/
get size(): number {
return this.nodes.length;
}
/**
* Get the last element in the heap, which is not necessarily a leaf node.
* @returns The last element or undefined if the heap is empty.
*/
get leaf(): E | undefined {
return this.nodes[this.size - 1] ?? undefined;
}
/**
* Static method that creates a binary heap from an array of nodes and a comparison function.
* @returns A new Heap instance.
* @param options
*/
static heapify<E>(options: { nodes: E[]; comparator: Comparator<E> }): Heap<E> {
return new Heap<E>(options);
}
/**
* Insert an element into the heap and maintain the heap properties.
* @param value - The element to be inserted.
* @param element - The element to be inserted.
*/
add(value: E): Heap<E> {
this.nodes.push(value);
add(element: E): Heap<E> {
return this.push(element);
}
/**
* Insert an element into the heap and maintain the heap properties.
* @param element - The element to be inserted.
*/
push(element: E): Heap<E> {
this.nodes.push(element);
this.bubbleUp(this.nodes.length - 1);
return this;
}
/**
* Remove and return the top element (smallest or largest element) from the heap.
* @returns The top element or null if the heap is empty.
* @returns The top element or undefined if the heap is empty.
*/
poll(): E | null {
poll(): E | undefined {
if (this.nodes.length === 0) {
return null;
return undefined;
}
if (this.nodes.length === 1) {
return this.nodes.pop() as E;
@ -43,6 +88,126 @@ export class Heap<E> {
return topValue;
}
/**
* Remove and return the top element (smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
pop(): E | undefined {
return this.poll();
}
/**
* Peek at the top element of the heap without removing it.
* @returns The top element or undefined if the heap is empty.
*/
peek(): E | undefined {
if (this.nodes.length === 0) {
return undefined;
}
return this.nodes[0];
}
/**
* Check if the heap is empty.
* @returns True if the heap is empty, otherwise false.
*/
isEmpty() {
return this.size === 0;
}
/**
* Reset the nodes of the heap. Make the nodes empty.
*/
clear() {
this._nodes = [];
}
/**
* Clear and add nodes of the heap
* @param nodes
*/
refill(nodes: E[]) {
this._nodes = nodes;
this.fix();
}
/**
* Use a comparison function to check whether a binary heap contains a specific element.
* @param element - the element to check.
* @returns Returns true if the specified element is contained; otherwise, returns false.
*/
has(element: E): boolean {
return this.nodes.includes(element);
}
/**
* Depth-first search (DFS) method, different traversal orders can be selected
* @param order - Traverse order parameter: 'in' (in-order), 'pre' (pre-order) or 'post' (post-order).
* @returns An array containing elements traversed in the specified order.
*/
dfs(order: DFSOrderPattern): E[] {
const result: E[] = [];
// Auxiliary recursive function, traverses the binary heap according to the traversal order
const dfsHelper = (index: number) => {
if (index < this.size) {
if (order === 'in') {
dfsHelper(2 * index + 1);
result.push(this.nodes[index]);
dfsHelper(2 * index + 2);
} else if (order === 'pre') {
result.push(this.nodes[index]);
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
} else if (order === 'post') {
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
result.push(this.nodes[index]);
}
}
};
dfsHelper(0); // Traverse starting from the root node
return result;
}
/**
* Convert the heap to an array.
* @returns An array containing the elements of the heap.
*/
toArray(): E[] {
return [...this.nodes];
}
getNodes(): E[] {
return this.nodes;
}
/**
* Clone the heap, creating a new heap with the same elements.
* @returns A new Heap instance containing the same elements.
*/
clone(): Heap<E> {
const clonedHeap = new Heap<E>({comparator: this.comparator});
clonedHeap._nodes = [...this.nodes];
return clonedHeap;
}
/**
* Sort the elements in the heap and return them as an array.
* @returns An array containing the elements sorted in ascending order.
*/
sort(): E[] {
const visitedNode: E[] = [];
const cloned = this.clone();
while (cloned.size !== 0) {
const top = cloned.poll();
if (top) visitedNode.push(top);
}
return visitedNode;
}
/**
* Float operation to maintain heap properties after adding an element.
* @param index - The index of the newly added element.
@ -93,144 +258,332 @@ export class Heap<E> {
protected fix() {
for (let i = Math.floor(this.size / 2); i >= 0; i--) this.sinkDown(i);
}
}
/**
* Peek at the top element of the heap without removing it.
* @returns The top element or null if the heap is empty.
*/
peek(): E | null {
if (this.nodes.length === 0) {
return null;
export class FibonacciHeapNode<E> {
element: E;
degree: number;
left?: FibonacciHeapNode<E>;
right?: FibonacciHeapNode<E>;
child?: FibonacciHeapNode<E>;
parent?: FibonacciHeapNode<E>;
marked: boolean;
constructor(element: E, degree = 0) {
this.element = element;
this.degree = degree;
this.marked = false;
}
}
export class FibonacciHeap<E> {
constructor(comparator?: Comparator<E>) {
this.clear();
this._comparator = comparator || this.defaultComparator;
if (typeof this.comparator !== 'function') {
throw new Error('FibonacciHeap constructor: given comparator should be a function.');
}
return this.nodes[0];
}
protected _root?: FibonacciHeapNode<E>;
get root(): FibonacciHeapNode<E> | undefined {
return this._root;
}
protected _size = 0;
get size(): number {
return this._size;
}
protected _min?: FibonacciHeapNode<E>;
get min(): FibonacciHeapNode<E> | undefined {
return this._min;
}
protected _comparator: Comparator<E>;
get comparator(): Comparator<E> {
return this._comparator;
}
/**
* Get the size (number of elements) of the heap.
* @returns {number} The size of the heap. Returns 0 if the heap is empty. Returns -1 if the heap is invalid.
*/
get size(): number {
return this.nodes.length;
clear(): void {
this._root = undefined;
this._min = undefined;
this._size = 0;
}
/**
* Get the last element in the heap, which is not necessarily a leaf node.
* @returns The last element or null if the heap is empty.
* O(1) time operation.
* Insert an element into the heap and maintain the heap properties.
* @param element
* @returns {FibonacciHeap<E>} FibonacciHeap<E> - The heap itself.
*/
get leaf(): E | null {
return this.nodes[this.size - 1] ?? null;
add(element: E): FibonacciHeap<E> {
return this.push(element);
}
/**
* Check if the heap is empty.
* @returns True if the heap is empty, otherwise false.
* O(1) time operation.
* Insert an element into the heap and maintain the heap properties.
* @param element
* @returns {FibonacciHeap<E>} FibonacciHeap<E> - The heap itself.
*/
isEmpty() {
return this.size === 0;
}
push(element: E): FibonacciHeap<E> {
const node = this.createNode(element);
node.left = node;
node.right = node;
this.mergeWithRoot(node);
/**
* Reset the nodes of the heap. Make the nodes empty.
*/
clear() {
this.nodes = [];
}
/**
* Clear and add nodes of the heap
* @param nodes
*/
refill(nodes: E[]) {
this.nodes = nodes;
this.fix();
}
/**
* Use a comparison function to check whether a binary heap contains a specific element.
* @param value - the element to check.
* @returns Returns true if the specified element is contained; otherwise, returns false.
*/
has(value: E): boolean {
return this.nodes.includes(value);
}
/**
* Depth-first search (DFS) method, different traversal orders can be selected
* @param order - Traversal order parameter: 'in' (in-order), 'pre' (pre-order) or 'post' (post-order).
* @returns An array containing elements traversed in the specified order.
*/
dfs(order: HeapDFSOrderPattern): E[] {
const result: E[] = [];
// Auxiliary recursive function, traverses the binary heap according to the traversal order
const dfsHelper = (index: number) => {
if (index < this.size) {
if (order === 'in') {
dfsHelper(2 * index + 1);
result.push(this.nodes[index]);
dfsHelper(2 * index + 2);
} else if (order === 'pre') {
result.push(this.nodes[index]);
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
} else if (order === 'post') {
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
result.push(this.nodes[index]);
}
}
};
dfsHelper(0); // Traverse starting from the root node
return result;
}
/**
* Convert the heap to an array.
* @returns An array containing the elements of the heap.
*/
toArray(): E[] {
return [...this.nodes];
}
getNodes(): E[] {
return this.nodes;
}
/**
* Clone the heap, creating a new heap with the same elements.
* @returns A new Heap instance containing the same elements.
*/
clone(): Heap<E> {
const clonedHeap = new Heap<E>(this.comparator);
clonedHeap.nodes = [...this.nodes];
return clonedHeap;
}
/**
* Sort the elements in the heap and return them as an array.
* @returns An array containing the elements sorted in ascending order.
*/
sort(): E[] {
const visitedNode: E[] = [];
const cloned = this.clone();
while (cloned.size !== 0) {
const top = cloned.poll();
if (top) visitedNode.push(top);
if (!this.min || this.comparator(node.element, this.min.element) <= 0) {
this._min = node;
}
return visitedNode;
this._size++;
return this;
}
/**
* Static method that creates a binary heap from an array of nodes and a comparison function.
* @param nodes
* @param comparator - Comparison function.
* @returns A new Heap instance.
* O(1) time operation.
* Peek at the top element of the heap without removing it.
* @returns The top element or undefined if the heap is empty.
* @protected
*/
static heapify<E>(nodes: E[], comparator: HeapComparator<E>): Heap<E> {
const binaryHeap = new Heap<E>(comparator);
binaryHeap.nodes = [...nodes];
binaryHeap.fix(); // Fix heap properties
return binaryHeap;
peek(): E | undefined {
return this.min ? this.min.element : undefined;
}
/**
* O(1) time operation.
* Get the size (number of elements) of the heap.
* @param {FibonacciHeapNode<E>} head - The head of the linked list.
* @protected
* @returns FibonacciHeapNode<E>[] - An array containing the nodes of the linked list.
*/
consumeLinkedList(head?: FibonacciHeapNode<E>): FibonacciHeapNode<E>[] {
const nodes: FibonacciHeapNode<E>[] = [];
if (!head) return nodes;
let node: FibonacciHeapNode<E> | undefined = head;
let flag = false;
while (true) {
if (node === head && flag) break;
else if (node === head) flag = true;
if (node) {
nodes.push(node);
node = node.right;
}
}
return nodes;
}
/**
* O(log n) time operation.
* Remove and return the top element (smallest or largest element) from the heap.
* @param parent
* @param node
*/
mergeWithChild(parent: FibonacciHeapNode<E>, node: FibonacciHeapNode<E>): void {
if (!parent.child) {
parent.child = node;
} else {
node.right = parent.child.right;
node.left = parent.child;
parent.child.right!.left = node;
parent.child.right = node;
}
}
/**
* O(log n) time operation.
* Remove and return the top element (smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
poll(): E | undefined {
return this.pop();
}
/**
* O(log n) time operation.
* Remove and return the top element (smallest or largest element) from the heap.
* @returns The top element or undefined if the heap is empty.
*/
pop(): E | undefined {
if (this.size === 0) return undefined;
const z = this.min!;
if (z.child) {
const nodes = this.consumeLinkedList(z.child);
for (const node of nodes) {
this.mergeWithRoot(node);
node.parent = undefined;
}
}
this.removeFromRoot(z);
if (z === z.right) {
this._min = undefined;
this._root = undefined;
} else {
this._min = z.right;
this.consolidate();
}
this._size--;
return z.element;
}
/**
* O(log n) time operation.
* merge two heaps. The heap that is merged will be cleared. The heap that is merged into will remain.
* @param heapToMerge
*/
merge(heapToMerge: FibonacciHeap<E>): void {
if (heapToMerge.size === 0) {
return; // Nothing to merge
}
// Merge the root lists of the two heaps
if (this.root && heapToMerge.root) {
const thisRoot = this.root;
const otherRoot = heapToMerge.root;
const thisRootRight = thisRoot.right!;
const otherRootLeft = otherRoot.left!;
thisRoot.right = otherRoot;
otherRoot.left = thisRoot;
thisRootRight.left = otherRootLeft;
otherRootLeft.right = thisRootRight;
}
// Update the minimum node
if (!this.min || (heapToMerge.min && this.comparator(heapToMerge.min.element, this.min.element) < 0)) {
this._min = heapToMerge.min;
}
// Update the size
this._size += heapToMerge.size;
// Clear the heap that was merged
heapToMerge.clear();
}
/**
* Default comparator function used by the heap.
* @param {E} a
* @param {E} b
* @protected
*/
protected defaultComparator(a: E, b: E): number {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
/**
* Create a new node.
* @param element
* @protected
*/
protected createNode(element: E): FibonacciHeapNode<E> {
return new FibonacciHeapNode<E>(element);
}
/**
* Merge the given node with the root list.
* @param node - The node to be merged.
*/
protected mergeWithRoot(node: FibonacciHeapNode<E>): void {
if (!this.root) {
this._root = node;
} else {
node.right = this.root.right;
node.left = this.root;
this.root.right!.left = node;
this.root.right = node;
}
}
/**
* O(log n) time operation.
* Remove and return the top element (smallest or largest element) from the heap.
* @param node - The node to be removed.
* @protected
*/
protected removeFromRoot(node: FibonacciHeapNode<E>): void {
if (this.root === node) this._root = node.right;
if (node.left) node.left.right = node.right;
if (node.right) node.right.left = node.left;
}
/**
* O(log n) time operation.
* Remove and return the top element (smallest or largest element) from the heap.
* @param y
* @param x
* @protected
*/
protected link(y: FibonacciHeapNode<E>, x: FibonacciHeapNode<E>): void {
this.removeFromRoot(y);
y.left = y;
y.right = y;
this.mergeWithChild(x, y);
x.degree++;
y.parent = x;
}
/**
* O(log n) time operation.
* Remove and return the top element (smallest or largest element) from the heap.
* @protected
*/
protected consolidate(): void {
const A: (FibonacciHeapNode<E> | undefined)[] = new Array(this.size);
const nodes = this.consumeLinkedList(this.root);
let x: FibonacciHeapNode<E> | undefined,
y: FibonacciHeapNode<E> | undefined,
d: number,
t: FibonacciHeapNode<E> | undefined;
for (const node of nodes) {
x = node;
d = x.degree;
while (A[d]) {
y = A[d] as FibonacciHeapNode<E>;
if (this.comparator(x.element, y.element) > 0) {
t = x;
x = y;
y = t;
}
this.link(y, x);
A[d] = undefined;
d++;
}
A[d] = x;
}
for (let i = 0; i < this.size; i++) {
if (A[i] && this.comparator(A[i]!.element, this.min!.element) <= 0) {
this._min = A[i]!;
}
}
}
}

View file

@ -7,18 +7,20 @@
*/
import {Heap} from './heap';
import type {HeapComparator} from '../../types';
import type {Comparator} from '../../types';
export class MaxHeap<E = any> extends Heap<E> {
constructor(
comparator: HeapComparator<E> = (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return b - a;
options: { comparator: Comparator<E>; nodes?: E[] } = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return b - a;
}
}
}
) {
super(comparator);
super(options);
}
}

View file

@ -7,18 +7,20 @@
*/
import {Heap} from './heap';
import type {HeapComparator} from '../../types';
import type {Comparator} from '../../types';
export class MinHeap<E = any> extends Heap<E> {
constructor(
comparator: HeapComparator<E> = (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
options: { comparator: Comparator<E>; nodes?: E[] } = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
}
}
}
) {
super(comparator);
super(options);
}
}

View file

@ -6,45 +6,19 @@
* @license MIT License
*/
export class DoublyLinkedListNode<E = any> {
value: E;
next: DoublyLinkedListNode<E> | null;
prev: DoublyLinkedListNode<E> | null;
/**
* The constructor function initializes the value, next, and previous properties of an object.
* @param {E} val - The "val" parameter is the value that will be stored in the node. It can be of any data type, as it
* @param {E} value - The "value" parameter is the value that will be stored in the node. It can be of any data type, as it
* is defined as a generic type "E".
*/
constructor(val: E) {
this._val = val;
this._next = null;
this._prev = null;
}
private _val: E;
get val(): E {
return this._val;
}
set val(value: E) {
this._val = value;
}
private _next: DoublyLinkedListNode<E> | null;
get next(): DoublyLinkedListNode<E> | null {
return this._next;
}
set next(value: DoublyLinkedListNode<E> | null) {
this._next = value;
}
private _prev: DoublyLinkedListNode<E> | null;
get prev(): DoublyLinkedListNode<E> | null {
return this._prev;
}
set prev(value: DoublyLinkedListNode<E> | null) {
this._prev = value;
constructor(value: E) {
this.value = value;
this.next = null;
this.prev = null;
}
}
@ -58,32 +32,28 @@ export class DoublyLinkedList<E = any> {
this._length = 0;
}
private _head: DoublyLinkedListNode<E> | null;
protected _head: DoublyLinkedListNode<E> | null;
get head(): DoublyLinkedListNode<E> | null {
return this._head;
}
set head(value: DoublyLinkedListNode<E> | null) {
this._head = value;
}
private _tail: DoublyLinkedListNode<E> | null;
protected _tail: DoublyLinkedListNode<E> | null;
get tail(): DoublyLinkedListNode<E> | null {
return this._tail;
}
set tail(value: DoublyLinkedListNode<E> | null) {
this._tail = value;
}
private _length: number;
protected _length: number;
get length(): number {
return this._length;
}
get size(): number {
return this.length;
}
/**
* The `fromArray` function creates a new instance of a DoublyLinkedList and populates it with the elements from the
* given array.
@ -100,54 +70,54 @@ export class DoublyLinkedList<E = any> {
/**
* The push function adds a new node with the given value to the end of the doubly linked list.
* @param {E} val - The value to be added to the linked list.
* @param {E} value - The value to be added to the linked list.
*/
push(val: E): void {
const newNode = new DoublyLinkedListNode(val);
push(value: E): void {
const newNode = new DoublyLinkedListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
this._head = newNode;
this._tail = newNode;
} else {
newNode.prev = this.tail;
this.tail!.next = newNode;
this.tail = newNode;
this._tail = newNode;
}
this._length++;
}
/**
* The addLast function adds a new node with the given value to the end of the doubly linked list.
* @param {E} val - The value to be added to the linked list.
* @param {E} value - The value to be added to the linked list.
*/
addLast(val: E): void {
this.push(val);
addLast(value: E): void {
this.push(value);
}
/**
* The `pop()` function removes and returns the value of the last node in a doubly linked list.
* @returns The method is returning the value of the removed node (removedNode.val) if the list is not empty. If the
* @returns The method is returning the value of the removed node (removedNode.value) if the list is not empty. If the
* list is empty, it returns null.
*/
pop(): E | undefined {
if (!this.tail) return undefined;
const removedNode = this.tail;
if (this.head === this.tail) {
this.head = null;
this.tail = null;
this._head = null;
this._tail = null;
} else {
this.tail = removedNode.prev;
this._tail = removedNode.prev;
this.tail!.next = null;
}
this._length--;
return removedNode.val;
return removedNode.value;
}
/**
* The `pollLast()` function removes and returns the value of the last node in a doubly linked list.
* @returns The method is returning the value of the removed node (removedNode.val) if the list is not empty. If the
* The `popLast()` function removes and returns the value of the last node in a doubly linked list.
* @returns The method is returning the value of the removed node (removedNode.value) if the list is not empty. If the
* list is empty, it returns null.
*/
pollLast(): E | undefined {
popLast(): E | undefined {
return this.pop();
}
@ -160,70 +130,66 @@ export class DoublyLinkedList<E = any> {
if (!this.head) return undefined;
const removedNode = this.head;
if (this.head === this.tail) {
this.head = null;
this.tail = null;
this._head = null;
this._tail = null;
} else {
this.head = removedNode.next;
this._head = removedNode.next;
this.head!.prev = null;
}
this._length--;
return removedNode.val;
return removedNode.value;
}
/**
* The `pollFirst()` function removes and returns the value of the first node in a doubly linked list.
* The `popFirst()` function removes and returns the value of the first node in a doubly linked list.
* @returns The method `shift()` returns the value of the node that is removed from the beginning of the doubly linked
* list.
*/
pollFirst(): E | undefined {
popFirst(): E | undefined {
return this.shift();
}
/**
* The unshift function adds a new node with the given value to the beginning of a doubly linked list.
* @param {E} val - The `val` parameter represents the value of the new node that will be added to the beginning of the
* @param {E} value - The `value` parameter represents the value of the new node that will be added to the beginning of the
* doubly linked list.
*/
unshift(val: E): void {
const newNode = new DoublyLinkedListNode(val);
unshift(value: E): void {
const newNode = new DoublyLinkedListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
this._head = newNode;
this._tail = newNode;
} else {
newNode.next = this.head;
this.head!.prev = newNode;
this.head = newNode;
this._head = newNode;
}
this._length++;
}
/**
* The addFirst function adds a new node with the given value to the beginning of a doubly linked list.
* @param {E} val - The `val` parameter represents the value of the new node that will be added to the beginning of the
* @param {E} value - The `value` parameter represents the value of the new node that will be added to the beginning of the
* doubly linked list.
*/
addFirst(val: E): void {
this.unshift(val);
addFirst(value: E): void {
this.unshift(value);
}
/**
* The `peekFirst` function returns the first node in a doubly linked list, or null if the list is empty.
* @returns The method `peekFirst()` returns the first node of the doubly linked list, or `null` if the list is empty.
* The `getFirst` function returns the first node in a doubly linked list, or null if the list is empty.
* @returns The method `getFirst()` returns the first node of the doubly linked list, or `null` if the list is empty.
*/
peekFirst(): E | undefined {
return this.head?.val;
getFirst(): E | undefined {
return this.head?.value;
}
/**
* The `peekLast` function returns the last node in a doubly linked list, or null if the list is empty.
* @returns The method `peekLast()` returns the last node of the doubly linked list, or `null` if the list is empty.
* The `getLast` function returns the last node in a doubly linked list, or null if the list is empty.
* @returns The method `getLast()` returns the last node of the doubly linked list, or `null` if the list is empty.
*/
peekLast(): E | undefined {
return this.tail?.val;
}
get size(): number {
return this.length;
getLast(): E | undefined {
return this.tail?.value;
}
/**
@ -239,7 +205,7 @@ export class DoublyLinkedList<E = any> {
for (let i = 0; i < index; i++) {
current = current!.next;
}
return current!.val;
return current!.value;
}
/**
@ -262,15 +228,15 @@ export class DoublyLinkedList<E = any> {
/**
* The function `findNodeByValue` searches for a node with a specific value in a doubly linked list and returns the
* node if found, otherwise it returns null.
* @param {E} val - The `val` parameter is the value that we want to search for in the doubly linked list.
* @returns The function `findNodeByValue` returns a `DoublyLinkedListNode<E>` if a node with the specified value `val`
* @param {E} value - The `value` parameter is the value that we want to search for in the doubly linked list.
* @returns The function `findNodeByValue` returns a `DoublyLinkedListNode<E>` if a node with the specified value `value`
* is found in the linked list. If no such node is found, it returns `null`.
*/
findNode(val: E): DoublyLinkedListNode<E> | null {
getNode(value: E | null): DoublyLinkedListNode<E> | null {
let current = this.head;
while (current) {
if (current.val === val) {
if (current.value === value) {
return current;
}
current = current.next;
@ -283,23 +249,23 @@ export class DoublyLinkedList<E = any> {
* The `insert` function inserts a value at a specified index in a doubly linked list.
* @param {number} index - The index parameter represents the position at which the new value should be inserted in the
* DoublyLinkedList. It is of type number.
* @param {E} val - The `val` parameter represents the value that you want to insert into the Doubly Linked List at the
* @param {E} value - The `value` parameter represents the value that you want to insert into the Doubly Linked List at the
* specified index.
* @returns The `insert` method returns a boolean value. It returns `true` if the insertion is successful, and `false`
* if the index is out of bounds.
*/
insertAt(index: number, val: E): boolean {
insertAt(index: number, value: E): boolean {
if (index < 0 || index > this.length) return false;
if (index === 0) {
this.unshift(val);
this.unshift(value);
return true;
}
if (index === this.length) {
this.push(val);
this.push(value);
return true;
}
const newNode = new DoublyLinkedListNode(val);
const newNode = new DoublyLinkedListNode(value);
const prevNode = this.getNodeAt(index - 1);
const nextNode = prevNode!.next;
newNode.prev = prevNode;
@ -310,6 +276,43 @@ export class DoublyLinkedList<E = any> {
return true;
}
/**
* The `insertBefore` function inserts a new value before an existing value or node in a doubly linked list.
* @param {E | DoublyLinkedListNode<E>} existingValueOrNode - The existing value or node in the doubly linked list
* before which the new value will be inserted. It can be either the value of the existing node or the existing node
* itself.
* @param {E} newValue - The `newValue` parameter represents the value that you want to insert into the doubly linked
* list.
* @returns The method returns a boolean value. It returns `true` if the insertion is successful, and `false` if the
* insertion fails.
*/
insertBefore(existingValueOrNode: E | DoublyLinkedListNode<E>, newValue: E): boolean {
let existingNode;
if (existingValueOrNode instanceof DoublyLinkedListNode) {
existingNode = existingValueOrNode;
} else {
existingNode = this.getNode(existingValueOrNode);
}
if (existingNode) {
const newNode = new DoublyLinkedListNode(newValue);
newNode.prev = existingNode.prev;
if (existingNode.prev) {
existingNode.prev.next = newNode;
}
newNode.next = existingNode;
existingNode.prev = newNode;
if (existingNode === this.head) {
this._head = newNode;
}
this._length++;
return true;
}
return false;
}
/**
* The `deleteAt` function removes an element at a specified index from a linked list and returns the removed element.
* @param {number} index - The index parameter represents the position of the element that needs to be deleted in the
@ -328,12 +331,9 @@ export class DoublyLinkedList<E = any> {
prevNode!.next = nextNode;
nextNode!.prev = prevNode;
this._length--;
return removedNode!.val;
return removedNode!.value;
}
delete(valOrNode: E): boolean;
delete(valOrNode: DoublyLinkedListNode<E>): boolean;
/**
* The `delete` function removes a node from a doubly linked list based on either the node itself or its value.
* @param {E | DoublyLinkedListNode<E>} valOrNode - The `valOrNode` parameter can accept either a value of type `E` or
@ -341,13 +341,13 @@ export class DoublyLinkedList<E = any> {
* @returns The `delete` method returns a boolean value. It returns `true` if the value or node was successfully
* deleted from the doubly linked list, and `false` if the value or node was not found in the list.
*/
delete(valOrNode: E | DoublyLinkedListNode<E>): boolean {
delete(valOrNode: E | DoublyLinkedListNode<E> | null): boolean {
let node: DoublyLinkedListNode<E> | null;
if (valOrNode instanceof DoublyLinkedListNode) {
node = valOrNode;
} else {
node = this.findNode(valOrNode);
node = this.getNode(valOrNode);
}
if (node) {
@ -375,7 +375,7 @@ export class DoublyLinkedList<E = any> {
const array: E[] = [];
let current = this.head;
while (current) {
array.push(current.val);
array.push(current.value);
current = current.next;
}
return array;
@ -405,11 +405,11 @@ export class DoublyLinkedList<E = any> {
* @returns The method `find` returns the first element in the linked list that satisfies the condition specified by
* the callback function. If no element satisfies the condition, it returns `null`.
*/
find(callback: (val: E) => boolean): E | null {
find(callback: (value: E) => boolean): E | null {
let current = this.head;
while (current) {
if (callback(current.val)) {
return current.val;
if (callback(current.value)) {
return current.value;
}
current = current.next;
}
@ -418,16 +418,16 @@ export class DoublyLinkedList<E = any> {
/**
* The function returns the index of the first occurrence of a given value in a linked list.
* @param {E} val - The parameter `val` is of type `E`, which means it can be any data type. It represents the value
* @param {E} value - The parameter `value` is of type `E`, which means it can be any data type. It represents the value
* that we are searching for in the linked list.
* @returns The method `indexOf` returns the index of the first occurrence of the specified value `val` in the linked
* @returns The method `indexOf` returns the index of the first occurrence of the specified value `value` in the linked
* list. If the value is not found, it returns -1.
*/
indexOf(val: E): number {
indexOf(value: E): number {
let index = 0;
let current = this.head;
while (current) {
if (current.val === val) {
if (current.value === value) {
return index;
}
index++;
@ -437,18 +437,18 @@ export class DoublyLinkedList<E = any> {
}
/**
* The `findLast` function iterates through a linked list from the last node to the first node and returns the last
* The `findBackward` function iterates through a linked list from the last node to the first node and returns the last
* value that satisfies the given callback function, or null if no value satisfies the callback.
* @param callback - A function that takes a value of type E as its parameter and returns a boolean value. This
* function is used to determine whether a given value satisfies a certain condition.
* @returns The method `findLast` returns the last value in the linked list that satisfies the condition specified by
* @returns The method `findBackward` returns the last value in the linked list that satisfies the condition specified by
* the callback function. If no value satisfies the condition, it returns `null`.
*/
findLast(callback: (val: E) => boolean): E | null {
findBackward(callback: (value: E) => boolean): E | null {
let current = this.tail;
while (current) {
if (callback(current.val)) {
return current.val;
if (callback(current.value)) {
return current.value;
}
current = current.prev;
}
@ -456,14 +456,14 @@ export class DoublyLinkedList<E = any> {
}
/**
* The `toArrayReverse` function converts a doubly linked list into an array in reverse order.
* @returns The `toArrayReverse()` function returns an array of type `E[]`.
* The `toArrayBackward` function converts a doubly linked list into an array in reverse order.
* @returns The `toArrayBackward()` function returns an array of type `E[]`.
*/
toArrayReverse(): E[] {
toArrayBackward(): E[] {
const array: E[] = [];
let current = this.tail;
while (current) {
array.push(current.val);
array.push(current.value);
current = current.prev;
}
return array;
@ -474,7 +474,7 @@ export class DoublyLinkedList<E = any> {
*/
reverse(): void {
let current = this.head;
[this.head, this.tail] = [this.tail, this.head];
[this._head, this._tail] = [this.tail, this.head];
while (current) {
const next = current.next;
[current.prev, current.next] = [current.next, current.prev];
@ -484,15 +484,15 @@ export class DoublyLinkedList<E = any> {
/**
* The `forEach` function iterates over each element in a linked list and applies a callback function to each element.
* @param callback - The callback parameter is a function that takes two arguments: val and index. The val argument
* @param callback - The callback parameter is a function that takes two arguments: value and index. The value argument
* represents the value of the current node in the linked list, and the index argument represents the index of the
* current node in the linked list.
*/
forEach(callback: (val: E, index: number) => void): void {
forEach(callback: (value: E, index: number) => void): void {
let current = this.head;
let index = 0;
while (current) {
callback(current.val, index);
callback(current.value, index);
current = current.next;
index++;
}
@ -506,11 +506,11 @@ export class DoublyLinkedList<E = any> {
* DoublyLinkedList).
* @returns The `map` function is returning a new instance of `DoublyLinkedList<U>` that contains the mapped values.
*/
map<U>(callback: (val: E) => U): DoublyLinkedList<U> {
map<U>(callback: (value: E) => U): DoublyLinkedList<U> {
const mappedList = new DoublyLinkedList<U>();
let current = this.head;
while (current) {
mappedList.push(callback(current.val));
mappedList.push(callback(current.value));
current = current.next;
}
return mappedList;
@ -523,12 +523,12 @@ export class DoublyLinkedList<E = any> {
* It is used to determine whether a value should be included in the filtered list or not.
* @returns The filtered list, which is an instance of the DoublyLinkedList class.
*/
filter(callback: (val: E) => boolean): DoublyLinkedList<E> {
filter(callback: (value: E) => boolean): DoublyLinkedList<E> {
const filteredList = new DoublyLinkedList<E>();
let current = this.head;
while (current) {
if (callback(current.val)) {
filteredList.push(current.val);
if (callback(current.value)) {
filteredList.push(current.value);
}
current = current.next;
}
@ -538,26 +538,23 @@ export class DoublyLinkedList<E = any> {
/**
* The `reduce` function iterates over a linked list and applies a callback function to each element, accumulating a
* single value.
* @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` and `val`. It is
* @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` and `value`. It is
* used to perform a specific operation on each element of the linked list.
* @param {U} initialValue - The `initialValue` parameter is the initial value of the accumulator. It is the starting
* point for the reduction operation.
* @returns The `reduce` method is returning the final value of the accumulator after iterating through all the
* elements in the linked list.
*/
reduce<U>(callback: (accumulator: U, val: E) => U, initialValue: U): U {
reduce<U>(callback: (accumulator: U, value: E) => U, initialValue: U): U {
let accumulator = initialValue;
let current = this.head;
while (current) {
accumulator = callback(accumulator, current.val);
accumulator = callback(accumulator, current.value);
current = current.next;
}
return accumulator;
}
insertAfter(existingValueOrNode: E, newValue: E): boolean;
insertAfter(existingValueOrNode: DoublyLinkedListNode<E>, newValue: E): boolean;
/**
* The `insertAfter` function inserts a new node with a given value after an existing node in a doubly linked list.
* @param {E | DoublyLinkedListNode<E>} existingValueOrNode - The existing value or node in the doubly linked list
@ -573,7 +570,7 @@ export class DoublyLinkedList<E = any> {
if (existingValueOrNode instanceof DoublyLinkedListNode) {
existingNode = existingValueOrNode;
} else {
existingNode = this.findNode(existingValueOrNode);
existingNode = this.getNode(existingValueOrNode);
}
if (existingNode) {
@ -585,7 +582,7 @@ export class DoublyLinkedList<E = any> {
newNode.prev = existingNode;
existingNode.next = newNode;
if (existingNode === this.tail) {
this.tail = newNode;
this._tail = newNode;
}
this._length++;
return true;
@ -594,43 +591,15 @@ export class DoublyLinkedList<E = any> {
return false;
}
insertBefore(existingValueOrNode: E, newValue: E): boolean;
insertBefore(existingValueOrNode: DoublyLinkedListNode<E>, newValue: E): boolean;
/**
* The `insertBefore` function inserts a new value before an existing value or node in a doubly linked list.
* @param {E | DoublyLinkedListNode<E>} existingValueOrNode - The existing value or node in the doubly linked list
* before which the new value will be inserted. It can be either the value of the existing node or the existing node
* itself.
* @param {E} newValue - The `newValue` parameter represents the value that you want to insert into the doubly linked
* list.
* @returns The method returns a boolean value. It returns `true` if the insertion is successful, and `false` if the
* insertion fails.
* The function returns an iterator that iterates over the values of a linked list.
*/
insertBefore(existingValueOrNode: E | DoublyLinkedListNode<E>, newValue: E): boolean {
let existingNode;
* [Symbol.iterator]() {
let current = this.head;
if (existingValueOrNode instanceof DoublyLinkedListNode) {
existingNode = existingValueOrNode;
} else {
existingNode = this.findNode(existingValueOrNode);
while (current) {
yield current.value;
current = current.next;
}
if (existingNode) {
const newNode = new DoublyLinkedListNode(newValue);
newNode.prev = existingNode.prev;
if (existingNode.prev) {
existingNode.prev.next = newNode;
}
newNode.next = existingNode;
existingNode.prev = newNode;
if (existingNode === this.head) {
this.head = newNode;
}
this._length++;
return true;
}
return false;
}
}

View file

@ -6,34 +6,17 @@
* @license MIT License
*/
export class SinglyLinkedListNode<E = any> {
value: E;
next: SinglyLinkedListNode<E> | null;
/**
* The constructor function initializes an instance of a class with a given value and sets the next property to null.
* @param {E} val - The "val" parameter is of type E, which means it can be any data type. It represents the value that
* @param {E} value - The "value" parameter is of type E, which means it can be any data type. It represents the value that
* will be stored in the node of a linked list.
*/
constructor(val: E) {
this._val = val;
this._next = null;
}
private _val: E;
get val(): E {
return this._val;
}
set val(value: E) {
this._val = value;
}
private _next: SinglyLinkedListNode<E> | null;
get next(): SinglyLinkedListNode<E> | null {
return this._next;
}
set next(value: SinglyLinkedListNode<E> | null) {
this._next = value;
constructor(value: E) {
this.value = value;
this.next = null;
}
}
@ -47,27 +30,19 @@ export class SinglyLinkedList<E = any> {
this._length = 0;
}
private _head: SinglyLinkedListNode<E> | null;
protected _head: SinglyLinkedListNode<E> | null;
get head(): SinglyLinkedListNode<E> | null {
return this._head;
}
set head(value: SinglyLinkedListNode<E> | null) {
this._head = value;
}
private _tail: SinglyLinkedListNode<E> | null;
protected _tail: SinglyLinkedListNode<E> | null;
get tail(): SinglyLinkedListNode<E> | null {
return this._tail;
}
set tail(value: SinglyLinkedListNode<E> | null) {
this._tail = value;
}
private _length: number;
protected _length: number;
get length(): number {
return this._length;
@ -87,25 +62,30 @@ export class SinglyLinkedList<E = any> {
return singlyLinkedList;
}
getLength(): number {
return this._length;
/**
* The `push` function adds a new node with the given value to the end of a singly linked list.
* @param {E} value - The "value" parameter represents the value that you want to add to the linked list. It can be of
* any type (E) as specified in the generic type declaration of the class or function.
*/
push(value: E): void {
const newNode = new SinglyLinkedListNode(value);
if (!this.head) {
this._head = newNode;
this._tail = newNode;
} else {
this.tail!.next = newNode;
this._tail = newNode;
}
this._length++;
}
/**
* The `push` function adds a new node with the given data to the end of a singly linked list.
* @param {E} data - The "data" parameter represents the value that you want to add to the linked list. It can be of
* The `push` function adds a new node with the given value to the end of a singly linked list.
* @param {E} value - The "value" parameter represents the value that you want to add to the linked list. It can be of
* any type (E) as specified in the generic type declaration of the class or function.
*/
push(data: E): void {
const newNode = new SinglyLinkedListNode(data);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail!.next = newNode;
this.tail = newNode;
}
this._length++;
addLast(value: E): void {
this.push(value);
}
/**
@ -117,22 +97,32 @@ export class SinglyLinkedList<E = any> {
pop(): E | undefined {
if (!this.head) return undefined;
if (this.head === this.tail) {
const val = this.head.val;
this.head = null;
this.tail = null;
const value = this.head.value;
this._head = null;
this._tail = null;
this._length--;
return val;
return value;
}
let current = this.head;
while (current.next !== this.tail) {
current = current.next!;
}
const val = this.tail!.val;
const value = this.tail!.value;
current.next = null;
this.tail = current;
this._tail = current;
this._length--;
return val;
return value;
}
/**
* The `popLast()` function removes and returns the value of the last element in a linked list, updating the head and tail
* pointers accordingly.
* @returns The method `pop()` returns the value of the node that is being removed from the end of the linked list. If
* the linked list is empty, it returns `null`.
*/
popLast(): E | undefined {
return this.pop();
}
/**
@ -142,28 +132,45 @@ export class SinglyLinkedList<E = any> {
shift(): E | undefined {
if (!this.head) return undefined;
const removedNode = this.head;
this.head = this.head.next;
this._head = this.head.next;
this._length--;
return removedNode.val;
return removedNode.value;
}
/**
* The `popFirst()` function removes and returns the value of the first node in a linked list.
* @returns The value of the node that is being removed from the beginning of the linked list.
*/
popFirst(): E | undefined {
return this.shift();
}
/**
* The unshift function adds a new node with the given value to the beginning of a singly linked list.
* @param {E} val - The parameter "val" represents the value of the new node that will be added to the beginning of the
* @param {E} value - The parameter "value" represents the value of the new node that will be added to the beginning of the
* linked list.
*/
unshift(val: E): void {
const newNode = new SinglyLinkedListNode(val);
unshift(value: E): void {
const newNode = new SinglyLinkedListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
this._head = newNode;
this._tail = newNode;
} else {
newNode.next = this.head;
this.head = newNode;
this._head = newNode;
}
this._length++;
}
/**
* The addFirst function adds a new node with the given value to the beginning of a singly linked list.
* @param {E} value - The parameter "value" represents the value of the new node that will be added to the beginning of the
* linked list.
*/
addFirst(value: E): void {
this.unshift(value);
}
/**
* The function `getAt` returns the value at a specified index in a linked list, or null if the index is out of range.
* @param {number} index - The index parameter is a number that represents the position of the element we want to
@ -177,7 +184,7 @@ export class SinglyLinkedList<E = any> {
for (let i = 0; i < index; i++) {
current = current!.next;
}
return current!.val;
return current!.value;
}
/**
@ -211,12 +218,9 @@ export class SinglyLinkedList<E = any> {
const removedNode = prevNode!.next;
prevNode!.next = removedNode!.next;
this._length--;
return removedNode!.val;
return removedNode!.value;
}
delete(valueOrNode: E): boolean;
delete(valueOrNode: SinglyLinkedListNode<E>): boolean;
/**
* The delete function removes a node with a specific value from a singly linked list.
* @param {E | SinglyLinkedListNode<E>} valueOrNode - The `valueOrNode` parameter can accept either a value of type `E`
@ -224,10 +228,11 @@ export class SinglyLinkedList<E = any> {
* @returns The `delete` method returns a boolean value. It returns `true` if the value or node is found and
* successfully deleted from the linked list, and `false` if the value or node is not found in the linked list.
*/
delete(valueOrNode: E | SinglyLinkedListNode<E>): boolean {
delete(valueOrNode: E | SinglyLinkedListNode<E> | null | undefined): boolean {
if (!valueOrNode) return false;
let value: E;
if (valueOrNode instanceof SinglyLinkedListNode) {
value = valueOrNode.val;
value = valueOrNode.value;
} else {
value = valueOrNode;
}
@ -235,16 +240,16 @@ export class SinglyLinkedList<E = any> {
prev = null;
while (current) {
if (current.val === value) {
if (current.value === value) {
if (prev === null) {
this.head = current.next;
this._head = current.next;
if (current === this.tail) {
this.tail = null;
this._tail = null;
}
} else {
prev.next = current.next;
if (current === this.tail) {
this.tail = prev;
this._tail = prev;
}
}
this._length--;
@ -261,23 +266,23 @@ export class SinglyLinkedList<E = any> {
* The `insertAt` function inserts a value at a specified index in a singly linked list.
* @param {number} index - The index parameter represents the position at which the new value should be inserted in the
* linked list. It is of type number.
* @param {E} val - The `val` parameter represents the value that you want to insert into the linked list at the
* @param {E} value - The `value` parameter represents the value that you want to insert into the linked list at the
* specified index.
* @returns The `insert` method returns a boolean value. It returns `true` if the insertion is successful, and `false`
* if the index is out of bounds.
*/
insertAt(index: number, val: E): boolean {
insertAt(index: number, value: E): boolean {
if (index < 0 || index > this.length) return false;
if (index === 0) {
this.unshift(val);
this.unshift(value);
return true;
}
if (index === this.length) {
this.push(val);
this.push(value);
return true;
}
const newNode = new SinglyLinkedListNode(val);
const newNode = new SinglyLinkedListNode(value);
const prevNode = this.getNodeAt(index - 1);
newNode.next = prevNode!.next;
prevNode!.next = newNode;
@ -311,7 +316,7 @@ export class SinglyLinkedList<E = any> {
const array: E[] = [];
let current = this.head;
while (current) {
array.push(current.val);
array.push(current.value);
current = current.next;
}
return array;
@ -335,7 +340,7 @@ export class SinglyLinkedList<E = any> {
current = next;
}
[this.head, this.tail] = [this.tail!, this.head!];
[this._head, this._tail] = [this.tail!, this.head!];
}
/**
@ -345,11 +350,11 @@ export class SinglyLinkedList<E = any> {
* @returns The method `find` returns the first element in the linked list that satisfies the condition specified by
* the callback function. If no element satisfies the condition, it returns `null`.
*/
find(callback: (val: E) => boolean): E | null {
find(callback: (value: E) => boolean): E | null {
let current = this.head;
while (current) {
if (callback(current.val)) {
return current.val;
if (callback(current.value)) {
return current.value;
}
current = current.next;
}
@ -367,7 +372,7 @@ export class SinglyLinkedList<E = any> {
let current = this.head;
while (current) {
if (current.val === value) {
if (current.value === value) {
return index;
}
index++;
@ -384,11 +389,11 @@ export class SinglyLinkedList<E = any> {
* @returns a `SinglyLinkedListNode<E>` if a node with the specified value is found in the linked list. If no node with
* the specified value is found, the function returns `null`.
*/
findNode(value: E): SinglyLinkedListNode<E> | null {
getNode(value: E): SinglyLinkedListNode<E> | null {
let current = this.head;
while (current) {
if (current.val === value) {
if (current.value === value) {
return current;
}
current = current.next;
@ -397,9 +402,6 @@ export class SinglyLinkedList<E = any> {
return null;
}
insertBefore(existingValue: E, newValue: E): boolean;
insertBefore(existingValue: SinglyLinkedListNode<E>, newValue: E): boolean;
/**
* The `insertBefore` function inserts a new value before an existing value in a singly linked list.
* @param {E | SinglyLinkedListNode<E>} existingValueOrNode - The existing value or node that you want to insert the
@ -413,18 +415,18 @@ export class SinglyLinkedList<E = any> {
let existingValue: E;
if (existingValueOrNode instanceof SinglyLinkedListNode) {
existingValue = existingValueOrNode.val;
existingValue = existingValueOrNode.value;
} else {
existingValue = existingValueOrNode;
}
if (this.head.val === existingValue) {
if (this.head.value === existingValue) {
this.unshift(newValue);
return true;
}
let current = this.head;
while (current.next) {
if (current.next.val === existingValue) {
if (current.next.value === existingValue) {
const newNode = new SinglyLinkedListNode(newValue);
newNode.next = current.next;
current.next = newNode;
@ -437,9 +439,6 @@ export class SinglyLinkedList<E = any> {
return false;
}
insertAfter(existingValueOrNode: E, newValue: E): boolean;
insertAfter(existingValueOrNode: SinglyLinkedListNode<E>, newValue: E): boolean;
/**
* The `insertAfter` function inserts a new node with a given value after an existing node in a singly linked list.
* @param {E | SinglyLinkedListNode<E>} existingValueOrNode - The existing value or node in the linked list after which
@ -454,7 +453,7 @@ export class SinglyLinkedList<E = any> {
if (existingValueOrNode instanceof SinglyLinkedListNode) {
existingNode = existingValueOrNode;
} else {
existingNode = this.findNode(existingValueOrNode);
existingNode = this.getNode(existingValueOrNode);
}
if (existingNode) {
@ -462,7 +461,7 @@ export class SinglyLinkedList<E = any> {
newNode.next = existingNode.next;
existingNode.next = newNode;
if (existingNode === this.tail) {
this.tail = newNode;
this._tail = newNode;
}
this._length++;
return true;
@ -481,7 +480,7 @@ export class SinglyLinkedList<E = any> {
let current = this.head;
while (current) {
if (current.val === value) {
if (current.value === value) {
count++;
}
current = current.next;
@ -490,11 +489,87 @@ export class SinglyLinkedList<E = any> {
return count;
}
*[Symbol.iterator]() {
/**
* The `forEach` function iterates over each element in a linked list and applies a callback function to each element.
* @param callback - The callback parameter is a function that takes two arguments: value and index. The value argument
* represents the value of the current node in the linked list, and the index argument represents the index of the
* current node in the linked list.
*/
forEach(callback: (value: E, index: number) => void): void {
let current = this.head;
let index = 0;
while (current) {
callback(current.value, index);
current = current.next;
index++;
}
}
/**
* The `map` function takes a callback function and applies it to each element in the SinglyLinkedList, returning a new
* SinglyLinkedList with the transformed values.
* @param callback - The callback parameter is a function that takes a value of type E (the type of values stored in
* the original SinglyLinkedList) and returns a value of type U (the type of values that will be stored in the mapped
* SinglyLinkedList).
* @returns The `map` function is returning a new instance of `SinglyLinkedList<U>` that contains the mapped values.
*/
map<U>(callback: (value: E) => U): SinglyLinkedList<U> {
const mappedList = new SinglyLinkedList<U>();
let current = this.head;
while (current) {
mappedList.push(callback(current.value));
current = current.next;
}
return mappedList;
}
/**
* The `filter` function iterates through a SinglyLinkedList and returns a new SinglyLinkedList containing only the
* elements that satisfy the given callback function.
* @param callback - The `callback` parameter is a function that takes a value of type `E` and returns a boolean value.
* It is used to determine whether a value should be included in the filtered list or not.
* @returns The filtered list, which is an instance of the SinglyLinkedList class.
*/
filter(callback: (value: E) => boolean): SinglyLinkedList<E> {
const filteredList = new SinglyLinkedList<E>();
let current = this.head;
while (current) {
if (callback(current.value)) {
filteredList.push(current.value);
}
current = current.next;
}
return filteredList;
}
/**
* The `reduce` function iterates over a linked list and applies a callback function to each element, accumulating a
* single value.
* @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` and `value`. It is
* used to perform a specific operation on each element of the linked list.
* @param {U} initialValue - The `initialValue` parameter is the initial value of the accumulator. It is the starting
* point for the reduction operation.
* @returns The `reduce` method is returning the final value of the accumulator after iterating through all the
* elements in the linked list.
*/
reduce<U>(callback: (accumulator: U, value: E) => U, initialValue: U): U {
let accumulator = initialValue;
let current = this.head;
while (current) {
accumulator = callback(accumulator, current.value);
current = current.next;
}
return accumulator;
}
/**
* The function returns an iterator that iterates over the values of a linked list.
*/
* [Symbol.iterator]() {
let current = this.head;
while (current) {
yield current.val;
yield current.value;
current = current.next;
}
}

View file

@ -19,39 +19,6 @@ export class SkipListNode<K, V> {
}
export class SkipList<K, V> {
get probability(): number {
return this._probability;
}
set probability(value: number) {
this._probability = value;
}
get maxLevel(): number {
return this._maxLevel;
}
set maxLevel(value: number) {
this._maxLevel = value;
}
get level(): number {
return this._level;
}
set level(value: number) {
this._level = value;
}
get head(): SkipListNode<K, V> {
return this._head;
}
set head(value: SkipListNode<K, V>) {
this._head = value;
}
private _head: SkipListNode<K, V>;
private _level: number;
private _maxLevel: number;
private _probability: number;
/**
* The constructor initializes a SkipList with a specified maximum level and probability.
* @param [maxLevel=16] - The `maxLevel` parameter represents the maximum level that a skip list can have. It determines
@ -66,16 +33,28 @@ export class SkipList<K, V> {
this._probability = probability;
}
/**
* The function "randomLevel" generates a random level based on a given probability and maximum level.
* @returns the level, which is a number.
*/
private randomLevel(): number {
let level = 1;
while (Math.random() < this.probability && level < this.maxLevel) {
level++;
}
return level;
protected _head: SkipListNode<K, V>;
get head(): SkipListNode<K, V> {
return this._head;
}
protected _level: number;
get level(): number {
return this._level;
}
protected _maxLevel: number;
get maxLevel(): number {
return this._maxLevel;
}
protected _probability: number;
get probability(): number {
return this._probability;
}
/**
@ -85,7 +64,7 @@ export class SkipList<K, V> {
* List.
*/
add(key: K, value: V): void {
const newNode = new SkipListNode(key, value, this.randomLevel());
const newNode = new SkipListNode(key, value, this._randomLevel());
const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
let current = this.head;
@ -102,7 +81,7 @@ export class SkipList<K, V> {
}
if (newNode.forward[0] !== null) {
this.level = Math.max(this.level, newNode.forward.length);
this._level = Math.max(this.level, newNode.forward.length);
}
}
@ -129,13 +108,17 @@ export class SkipList<K, V> {
return undefined;
}
has(key: K): boolean {
return this.get(key) !== undefined;
}
/**
* The `remove` function removes a node with a specific key from a Skip List data structure.
* The `delete` function removes a node with a specific key from a Skip List data structure.
* @param {K} key - The key parameter represents the key of the node that needs to be removed from the skip list.
* @returns The `remove` method returns a boolean value. It returns `true` if the key was successfully removed from the
* @returns The `delete` method returns a boolean value. It returns `true` if the key was successfully removed from the
* skip list, and `false` if the key was not found in the skip list.
*/
remove(key: K): boolean {
delete(key: K): boolean {
const update: SkipListNode<K, V>[] = new Array(this.maxLevel).fill(this.head);
let current = this.head;
@ -156,11 +139,83 @@ export class SkipList<K, V> {
update[i].forward[i] = current.forward[i];
}
while (this.level > 0 && this.head.forward[this.level - 1] === null) {
this.level--;
this._level--;
}
return true;
}
return false;
}
/**
* Get the value of the first element (the smallest element) in the Skip List.
* @returns The value of the first element, or undefined if the Skip List is empty.
*/
getFirst(): V | undefined {
const firstNode = this.head.forward[0];
return firstNode ? firstNode.value : undefined;
}
/**
* Get the value of the last element (the largest element) in the Skip List.
* @returns The value of the last element, or undefined if the Skip List is empty.
*/
getLast(): V | undefined {
let current = this.head;
for (let i = this.level - 1; i >= 0; i--) {
while (current.forward[i]) {
current = current.forward[i];
}
}
return current.value;
}
/**
* Get the value of the first element in the Skip List that is greater than the given key.
* @param key - the given key.
* @returns The value of the first element greater than the given key, or undefined if there is no such element.
*/
higher(key: K): V | undefined {
let current = this.head;
for (let i = this.level - 1; i >= 0; i--) {
while (current.forward[i] && current.forward[i].key <= key) {
current = current.forward[i];
}
}
const nextNode = current.forward[0];
return nextNode ? nextNode.value : undefined;
}
/**
* Get the value of the last element in the Skip List that is less than the given key.
* @param key - the given key.
* @returns The value of the last element less than the given key, or undefined if there is no such element.
*/
lower(key: K): V | undefined {
let current = this.head;
let lastLess = null;
for (let i = this.level - 1; i >= 0; i--) {
while (current.forward[i] && current.forward[i].key < key) {
current = current.forward[i];
}
if (current.key < key) {
lastLess = current;
}
}
return lastLess ? lastLess.value : undefined;
}
/**
* The function "_randomLevel" generates a random level based on a given probability and maximum level.
* @returns the level, which is a number.
*/
protected _randomLevel(): number {
let level = 1;
while (Math.random() < this.probability && level < this.maxLevel) {
level++;
}
return level;
}
}

View file

@ -7,14 +7,14 @@
*/
// todo need to be improved
export class MatrixNTI2D<V = any> {
private readonly _matrix: Array<Array<V>>;
protected readonly _matrix: Array<Array<V>>;
/**
* The constructor creates a matrix with the specified number of rows and columns, and initializes all elements to a
* given initial value or 0 if not provided.
* @param options - An object containing the following properties:
*/
constructor(options: {row: number; col: number; initialVal?: V}) {
constructor(options: { row: number; col: number; initialVal?: V }) {
const {row, col, initialVal} = options;
this._matrix = new Array(row).fill(undefined).map(() => new Array(col).fill(initialVal || 0));
}

View file

@ -5,10 +5,10 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import Vector2D from './vector2d';
import {Vector2D} from './vector2d';
export class Matrix2D {
private readonly _matrix: number[][];
protected readonly _matrix: number[][];
/**
* The constructor function initializes a Matrix2D object with either a default identity matrix, or a provided matrix
@ -58,16 +58,6 @@ export class Matrix2D {
return this._matrix;
}
/**
* The function "toVector" returns a new Vector2D object with the values from the first and second elements of the
* _matrix array.
* @returns A new instance of the Vector2D class is being returned. The values of the returned vector are taken from
* the first column of the matrix.
*/
toVector(): Vector2D {
return new Vector2D(this._matrix[0][0], this._matrix[1][0]);
}
/**
* The function takes two 2D matrices as input and returns their sum as a new 2D matrix.
* @param {Matrix2D} matrix1 - Matrix2D - The first matrix to be added.
@ -208,6 +198,14 @@ export class Matrix2D {
[0, 0, vector.w]
]);
}
}
export default Matrix2D;
/**
* The function "toVector" returns a new Vector2D object with the values from the first and second elements of the
* _matrix array.
* @returns A new instance of the Vector2D class is being returned. The values of the returned vector are taken from
* the first column of the matrix.
*/
toVector(): Vector2D {
return new Vector2D(this._matrix[0][0], this._matrix[1][0]);
}
}

View file

@ -27,10 +27,10 @@ export class Character {
export class Navigator<T = number> {
onMove: (cur: [number, number]) => void;
private readonly _matrix: T[][];
private readonly _cur: [number, number];
private _character: Character;
private readonly _VISITED: T;
protected readonly _matrix: T[][];
protected readonly _cur: [number, number];
protected _character: Character;
protected readonly _VISITED: T;
/**
* The constructor initializes the Navigator object with the given parameters and sets the current position as visited

View file

@ -10,7 +10,8 @@ export class Vector2D {
public x: number = 0,
public y: number = 0,
public w: number = 1 // needed for matrix multiplication
) {}
) {
}
/**
* The function checks if the x and y values of a point are both zero.
@ -312,5 +313,3 @@ export class Vector2D {
this.y = 0;
}
}
export default Vector2D;

View file

@ -6,18 +6,20 @@
* @license MIT License
*/
import {PriorityQueue} from './priority-queue';
import type {HeapComparator} from '../../types';
import type {Comparator} from '../../types';
export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(
compare: HeapComparator<E> = (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return b - a;
options: { comparator: Comparator<E>; nodes?: E[] } = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return b - a;
}
}
}
) {
super(compare);
super(options);
}
}

View file

@ -6,18 +6,20 @@
* @license MIT License
*/
import {PriorityQueue} from './priority-queue';
import type {HeapComparator} from '../../types';
import type {Comparator} from '../../types';
export class MinPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(
compare: HeapComparator<E> = (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
options: { comparator: Comparator<E>; nodes?: E[] } = {
comparator: (a: E, b: E) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
}
}
}
) {
super(compare);
super(options);
}
}

View file

@ -7,10 +7,10 @@
*/
import {Heap} from '../heap';
import {HeapComparator} from '../../types';
import {Comparator} from '../../types';
export class PriorityQueue<E> extends Heap<E> {
constructor(comparator: HeapComparator<E>) {
super(comparator);
export class PriorityQueue<E = any> extends Heap<E> {
constructor(options: { comparator: Comparator<E>; nodes?: E[] }) {
super(options);
}
}

View file

@ -9,7 +9,8 @@ import {DoublyLinkedList} from '../linked-list';
// O(n) time complexity of obtaining the value
// O(1) time complexity of adding at the beginning and the end
export class Deque<E = any> extends DoublyLinkedList<E> {}
export class Deque<E = any> extends DoublyLinkedList<E> {
}
// O(1) time complexity of obtaining the value
// O(n) time complexity of adding at the beginning and the end
@ -19,43 +20,31 @@ export class ObjectDeque<E = number> {
if (capacity !== undefined) this._capacity = capacity;
}
private _nodes: {[key: number]: E} = {};
protected _nodes: { [key: number]: E } = {};
get nodes(): {[p: number]: E} {
get nodes(): { [p: number]: E } {
return this._nodes;
}
private _capacity = Number.MAX_SAFE_INTEGER;
protected _capacity = Number.MAX_SAFE_INTEGER;
get capacity(): number {
return this._capacity;
}
set capacity(value: number) {
this._capacity = value;
}
private _first = -1;
protected _first = -1;
get first(): number {
return this._first;
}
set first(value: number) {
this._first = value;
}
private _last = -1;
protected _last = -1;
get last(): number {
return this._last;
}
set last(value: number) {
this._last = value;
}
private _size = 0;
protected _size = 0;
get size(): number {
return this._size;
@ -67,14 +56,14 @@ export class ObjectDeque<E = number> {
* structure.
*/
addFirst(value: E) {
if (this._size === 0) {
const mid = Math.floor(this._capacity / 2);
if (this.size === 0) {
const mid = Math.floor(this.capacity / 2);
this._first = mid;
this._last = mid;
} else {
this._first--;
}
this._nodes[this._first] = value;
this.nodes[this.first] = value;
this._size++;
}
@ -83,46 +72,46 @@ export class ObjectDeque<E = number> {
* @param {E} value - The `value` parameter represents the value that you want to add to the end of the data structure.
*/
addLast(value: E) {
if (this._size === 0) {
const mid = Math.floor(this._capacity / 2);
if (this.size === 0) {
const mid = Math.floor(this.capacity / 2);
this._first = mid;
this._last = mid;
} else {
this._last++;
}
this._nodes[this._last] = value;
this.nodes[this.last] = value;
this._size++;
}
/**
* The function `pollFirst()` removes and returns the first element in a data structure.
* The function `popFirst()` removes and returns the first element in a data structure.
* @returns The value of the first element in the data structure.
*/
pollFirst() {
if (!this._size) return;
const value = this.peekFirst();
delete this._nodes[this._first];
popFirst() {
if (!this.size) return;
const value = this.getFirst();
delete this.nodes[this.first];
this._first++;
this._size--;
return value;
}
/**
* The `peekFirst` function returns the first element in an array-like data structure if it exists.
* The `getFirst` function returns the first element in an array-like data structure if it exists.
* @returns The element at the first position of the `_nodes` array.
*/
peekFirst() {
if (this._size) return this._nodes[this._first];
getFirst() {
if (this.size) return this.nodes[this.first];
}
/**
* The `pollLast()` function removes and returns the last element in a data structure.
* The `popLast()` function removes and returns the last element in a data structure.
* @returns The value that was removed from the data structure.
*/
pollLast() {
if (!this._size) return;
const value = this.peekLast();
delete this._nodes[this._last];
popLast() {
if (!this.size) return;
const value = this.getLast();
delete this.nodes[this.last];
this._last--;
this._size--;
@ -130,11 +119,11 @@ export class ObjectDeque<E = number> {
}
/**
* The `peekLast()` function returns the last element in an array-like data structure.
* The `getLast()` function returns the last element in an array-like data structure.
* @returns The last element in the array "_nodes" is being returned.
*/
peekLast() {
if (this._size) return this._nodes[this._last];
getLast() {
if (this.size) return this.nodes[this.last];
}
/**
@ -145,7 +134,7 @@ export class ObjectDeque<E = number> {
* index, `null` is returned.
*/
get(index: number) {
return this._nodes[this._first + index] || null;
return this.nodes[this.first + index] || null;
}
/**
@ -153,15 +142,7 @@ export class ObjectDeque<E = number> {
* @returns The method is returning a boolean value indicating whether the size of the object is less than or equal to 0.
*/
isEmpty() {
return this._size <= 0;
}
protected _seNodes(value: {[p: number]: E}) {
this._nodes = value;
}
protected _setSize(value: number) {
this._size = value;
return this.size <= 0;
}
}
@ -170,8 +151,12 @@ export class ObjectDeque<E = number> {
export class ArrayDeque<E> {
protected _nodes: E[] = [];
get nodes(): E[] {
return this._nodes;
}
get size() {
return this._nodes.length;
return this.nodes.length;
}
/**
@ -184,24 +169,24 @@ export class ArrayDeque<E> {
* @returns The return value is the new length of the array after the value has been added.
*/
addLast(value: E) {
return this._nodes.push(value);
return this.nodes.push(value);
}
/**
* The function "pollLast" returns and removes the last element from an array, or returns null if the array is empty.
* @returns The method `pollLast()` returns the last element of the `_nodes` array, or `null` if the array is empty.
* The function "popLast" returns and removes the last element from an array, or returns null if the array is empty.
* @returns The method `popLast()` returns the last element of the `_nodes` array, or `null` if the array is empty.
*/
pollLast(): E | null {
return this._nodes.pop() ?? null;
popLast(): E | null {
return this.nodes.pop() ?? null;
}
/**
* The `pollFirst` function removes and returns the first element from an array, or returns null if the array is empty.
* @returns The `pollFirst()` function returns the first element of the `_nodes` array, or `null` if the array is
* The `popFirst` function removes and returns the first element from an array, or returns null if the array is empty.
* @returns The `popFirst()` function returns the first element of the `_nodes` array, or `null` if the array is
* empty.
*/
pollFirst(): E | null {
return this._nodes.shift() ?? null;
popFirst(): E | null {
return this.nodes.shift() ?? null;
}
/**
@ -215,24 +200,24 @@ export class ArrayDeque<E> {
* `value` at the beginning.
*/
addFirst(value: E) {
return this._nodes.unshift(value);
return this.nodes.unshift(value);
}
/**
* The `peekFirst` function returns the first element of an array or null if the array is empty.
* @returns The function `peekFirst()` is returning the first element (`E`) of the `_nodes` array. If the array is
* The `getFirst` function returns the first element of an array or null if the array is empty.
* @returns The function `getFirst()` is returning the first element (`E`) of the `_nodes` array. If the array is
* empty, it will return `null`.
*/
peekFirst(): E | null {
return this._nodes[0] ?? null;
getFirst(): E | null {
return this.nodes[0] ?? null;
}
/**
* The `peekLast` function returns the last element of an array or null if the array is empty.
* @returns The method `peekLast()` returns the last element of the `_nodes` array, or `null` if the array is empty.
* The `getLast` function returns the last element of an array or null if the array is empty.
* @returns The method `getLast()` returns the last element of the `_nodes` array, or `null` if the array is empty.
*/
peekLast(): E | null {
return this._nodes[this._nodes.length - 1] ?? null;
getLast(): E | null {
return this.nodes[this.nodes.length - 1] ?? null;
}
/**
@ -247,7 +232,7 @@ export class ArrayDeque<E> {
* will be returned. If the element does not exist (i.e., the index is out of bounds), `null` will be returned.
*/
get(index: number): E | null {
return this._nodes[index] ?? null;
return this.nodes[index] ?? null;
}
/**
@ -259,7 +244,7 @@ export class ArrayDeque<E> {
* @returns The value that is being set at the specified index in the `_nodes` array.
*/
set(index: number, value: E) {
return (this._nodes[index] = value);
return (this.nodes[index] = value);
}
/**
@ -273,17 +258,17 @@ export class ArrayDeque<E> {
* are being removed, an empty array will be returned.
*/
insert(index: number, value: E) {
return this._nodes.splice(index, 0, value);
return this.nodes.splice(index, 0, value);
}
/**
* The remove function removes an element from an array at a specified index.
* The delete function removes an element from an array at a specified index.
* @param {number} index - The index parameter specifies the position of the element to be removed from the array. It
* is a number that represents the index of the element to be removed.
* @returns The method is returning an array containing the removed element.
*/
remove(index: number) {
return this._nodes.splice(index, 1);
delete(index: number) {
return this.nodes.splice(index, 1);
}
/**
@ -292,6 +277,6 @@ export class ArrayDeque<E> {
* is 0, indicating that the array is empty. Otherwise, it returns `false`.
*/
isEmpty() {
return this._nodes.length === 0;
return this.nodes.length === 0;
}
}

View file

@ -22,12 +22,20 @@ export class LinkedListQueue<E = any> extends SinglyLinkedList<E> {
return this.shift();
}
/**
* The `getFirst` function returns the value of the head node in a linked list, or `undefined` if the list is empty.
* @returns The `getFirst()` method is returning the value of the `head` node if it exists, otherwise it returns `undefined`.
*/
getFirst(): E | undefined {
return this.head?.value;
}
/**
* The `peek` function returns the value of the head node in a linked list, or `undefined` if the list is empty.
* @returns The `peek()` method is returning the value of the `head` node if it exists, otherwise it returns `undefined`.
*/
peek(): E | undefined {
return this.head?.val;
return this.getFirst();
}
}
@ -43,26 +51,18 @@ export class Queue<E = any> {
this._offset = 0;
}
private _nodes: E[];
protected _nodes: E[];
get nodes(): E[] {
return this._nodes;
}
set nodes(value: E[]) {
this._nodes = value;
}
private _offset: number;
protected _offset: number;
get offset(): number {
return this._offset;
}
set offset(value: number) {
this._offset = value;
}
/**
* The size function returns the number of elements in an array.
* @returns {number} The size of the array, which is the difference between the length of the array and the offset.
@ -101,25 +101,43 @@ export class Queue<E = any> {
shift(): E | undefined {
if (this.size === 0) return undefined;
const first = this.peek();
this.offset += 1;
const first = this.getFirst();
this._offset += 1;
if (this.offset * 2 < this.nodes.length) return first;
// only remove dequeued elements when reaching half size
// only delete dequeued elements when reaching half size
// to decrease latency of shifting elements.
this.nodes = this.nodes.slice(this.offset);
this.offset = 0;
this._nodes = this.nodes.slice(this.offset);
this._offset = 0;
return first;
}
/**
* The `getFirst` function returns the first element of the array `_nodes` if it exists, otherwise it returns `null`.
* @returns The `getFirst()` method returns the first element of the data structure, represented by the `_nodes` array at
* the `_offset` index. If the data structure is empty (size is 0), it returns `null`.
*/
getFirst(): E | undefined {
return this.size > 0 ? this.nodes[this.offset] : undefined;
}
/**
* The `peek` function returns the first element of the array `_nodes` if it exists, otherwise it returns `null`.
* @returns The `peek()` method returns the first element of the data structure, represented by the `_nodes` array at
* the `_offset` index. If the data structure is empty (size is 0), it returns `null`.
*/
peek(): E | undefined {
return this.size > 0 ? this.nodes[this.offset] : undefined;
return this.getFirst();
}
/**
* The `getLast` function returns the last element in an array-like data structure, or null if the structure is empty.
* @returns The method `getLast()` returns the last element of the `_nodes` array if the array is not empty. If the
* array is empty, it returns `null`.
*/
getLast(): E | undefined {
return this.size > 0 ? this.nodes[this.nodes.length - 1] : undefined;
}
/**
@ -128,7 +146,7 @@ export class Queue<E = any> {
* array is empty, it returns `null`.
*/
peekLast(): E | undefined {
return this.size > 0 ? this.nodes[this.nodes.length - 1] : undefined;
return this.getLast();
}
/**
@ -171,8 +189,8 @@ export class Queue<E = any> {
* The clear function resets the nodes array and offset to their initial values.
*/
clear(): void {
this.nodes = [];
this.offset = 0;
this._nodes = [];
this._offset = 0;
}
/**
@ -183,7 +201,7 @@ export class Queue<E = any> {
return new Queue(this.nodes.slice(this.offset));
}
*[Symbol.iterator]() {
* [Symbol.iterator]() {
for (const item of this.nodes) {
yield item;
}

View file

@ -4,8 +4,6 @@
* @class
*/
export class Stack<E = any> {
protected _elements: E[];
/**
* The constructor initializes an array of elements, which can be provided as an optional parameter.
* @param {E[]} [elements] - The `elements` parameter is an optional parameter of type `E[]`, which represents an array
@ -16,6 +14,12 @@ export class Stack<E = any> {
this._elements = Array.isArray(elements) ? elements : [];
}
protected _elements: E[];
get elements(): E[] {
return this._elements;
}
/**
* The function "fromArray" creates a new Stack object from an array of elements.
* @param {E[]} elements - The `elements` parameter is an array of elements of type `E`.
@ -31,7 +35,7 @@ export class Stack<E = any> {
* @returns A boolean value indicating whether the `_elements` array is empty or not.
*/
isEmpty(): boolean {
return this._elements.length === 0;
return this.elements.length === 0;
}
/**
@ -39,7 +43,7 @@ export class Stack<E = any> {
* @returns The size of the elements array.
*/
size(): number {
return this._elements.length;
return this.elements.length;
}
/**
@ -49,7 +53,7 @@ export class Stack<E = any> {
peek(): E | null {
if (this.isEmpty()) return null;
return this._elements[this._elements.length - 1];
return this.elements[this.elements.length - 1];
}
/**
@ -58,7 +62,7 @@ export class Stack<E = any> {
* @returns The `push` method is returning the updated `Stack<E>` object.
*/
push(element: E): Stack<E> {
this._elements.push(element);
this.elements.push(element);
return this;
}
@ -70,7 +74,7 @@ export class Stack<E = any> {
pop(): E | null {
if (this.isEmpty()) return null;
return this._elements.pop() || null;
return this.elements.pop() || null;
}
/**
@ -78,7 +82,7 @@ export class Stack<E = any> {
* @returns An array of type E.
*/
toArray(): E[] {
return this._elements.slice();
return this.elements.slice();
}
/**
@ -93,6 +97,6 @@ export class Stack<E = any> {
* @returns The `clone()` method is returning a new `Stack` object with a copy of the `_elements` array.
*/
clone(): Stack<E> {
return new Stack(this._elements.slice());
return new Stack(this.elements.slice());
}
}

View file

@ -1,38 +1,12 @@
export class TreeNode<V = any> {
key: string;
value?: V | undefined;
children?: TreeNode<V>[] | undefined;
constructor(key: string, value?: V, children?: TreeNode<V>[]) {
this._key = key;
this._value = value || undefined;
this._children = children || [];
}
private _key: string;
get key(): string {
return this._key;
}
set key(value: string) {
this._key = value;
}
private _value?: V | undefined;
get value(): V | undefined {
return this._value;
}
set value(value: V | undefined) {
this._value = value;
}
private _children?: TreeNode<V>[] | undefined;
get children(): TreeNode<V>[] | undefined {
return this._children;
}
set children(value: TreeNode<V>[] | undefined) {
this._children = value;
this.key = key;
this.value = value || undefined;
this.children = children || [];
}
addChildren(children: TreeNode<V> | TreeNode<V>[]) {

View file

@ -11,40 +11,14 @@
* and a flag indicating whether it's the end of a word.
*/
export class TrieNode {
key: string;
children: Map<string, TrieNode>;
isEnd: boolean;
constructor(key: string) {
this._key = key;
this._isEnd = false;
this._children = new Map<string, TrieNode>();
}
private _key;
get key(): string {
return this._key;
}
set key(v: string) {
this._key = v;
}
protected _children: Map<string, TrieNode>;
get children(): Map<string, TrieNode> {
return this._children;
}
set children(v: Map<string, TrieNode>) {
this._children = v;
}
protected _isEnd: boolean;
get isEnd(): boolean {
return this._isEnd;
}
set isEnd(v: boolean) {
this._isEnd = v;
this.key = key;
this.isEnd = false;
this.children = new Map<string, TrieNode>();
}
}
@ -62,18 +36,18 @@ export class Trie {
}
}
protected _caseSensitive: boolean;
get caseSensitive(): boolean {
return this._caseSensitive;
}
protected _root: TrieNode;
get root() {
return this._root;
}
set root(v: TrieNode) {
this._root = v;
}
private readonly _caseSensitive: boolean;
/**
* Add a word to the Trie structure.
* @param {string} word - The word to add.
@ -110,19 +84,12 @@ export class Trie {
return cur.isEnd;
}
private _caseProcess(str: string) {
if (!this._caseSensitive) {
str = str.toLowerCase(); // Convert str to lowercase if case-insensitive
}
return str;
}
/**
* Remove a word from the Trie structure.
* @param{string} word - The word to remove.
* @param{string} word - The word to delete.
* @returns {boolean} True if the word was successfully removed.
*/
remove(word: string) {
delete(word: string) {
word = this._caseProcess(word);
let isDeleted = false;
const dfs = (cur: TrieNode, i: number): boolean => {
@ -248,9 +215,10 @@ export class Trie {
* @param {string} prefix - The `prefix` parameter is a string that represents the prefix that we want to search for in the
* trie. It is an optional parameter, so if no prefix is provided, it will default to an empty string.
* @param {number} max - The max count of words will be found
* @param isAllWhenEmptyPrefix - If true, when the prefix provided as '', returns all the words in the trie.
* @returns {string[]} an array of strings.
*/
getWords(prefix = '', max = Number.MAX_SAFE_INTEGER): string[] {
getWords(prefix = '', max = Number.MAX_SAFE_INTEGER, isAllWhenEmptyPrefix = false): string[] {
prefix = this._caseProcess(prefix);
const words: string[] = [];
let found = 0;
@ -277,10 +245,18 @@ export class Trie {
if (nodeC) startNode = nodeC;
}
}
if (startNode !== this.root) dfs(startNode, prefix);
if (isAllWhenEmptyPrefix || startNode !== this.root) dfs(startNode, prefix);
return words;
}
protected _caseProcess(str: string) {
if (!this._caseSensitive) {
str = str.toLowerCase(); // Convert str to lowercase if case-insensitive
}
return str;
}
// --- end additional methods ---
}

View file

@ -1,10 +1,10 @@
import {BinaryTreeNode} from '../data-structures';
import {BinaryTreeDeletedResult, BinaryTreeNodeKey} from '../types';
import {BinaryTreeDeletedResult, BinaryTreeNodeNested, BTNCallback, BTNKey} from '../types';
export interface IBinaryTree<N extends BinaryTreeNode<N['val'], N>> {
createNode(key: BinaryTreeNodeKey, val?: N['val']): N;
export interface IBinaryTree<V = any, N extends BinaryTreeNode<V, N> = BinaryTreeNodeNested<V>> {
createNode(key: BTNKey, value?: N['value']): N;
add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined;
add(keyOrNode: BTNKey | N | null, value?: N['value']): N | null | undefined;
remove(nodeOrKey: N | BinaryTreeNodeKey): BinaryTreeDeletedResult<N>[];
delete<C extends BTNCallback<N>>(identifier: ReturnType<C> | null, callback: C): BinaryTreeDeletedResult<N>[];
}

View file

@ -1,7 +1,7 @@
import {VertexKey} from '../types';
export interface IGraph<V, E> {
createVertex(key: VertexKey, val?: V): V;
export interface IGraph<V, E, VO, EO> {
createVertex(key: VertexKey, value?: V): VO;
createEdge(srcOrV1: VertexKey | string, destOrV2: VertexKey | string, weight?: number, val?: E): E;
createEdge(srcOrV1: VertexKey, destOrV2: VertexKey, weight?: number, value?: E): EO;
}

View file

@ -1,4 +1,4 @@
import {AVLTreeNode} from '../../data-structures';
import {AVLTreeNode} from '../../../data-structures';
import {BSTOptions} from './bst';
export type AVLTreeNodeNested<T> = AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, AVLTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View file

@ -1,4 +1,4 @@
import {BinaryTreeNode} from '../../data-structures/binary-tree';
import {BinaryTreeNode} from '../../../data-structures';
/**
* Enum representing different loop types.
@ -7,7 +7,7 @@ import {BinaryTreeNode} from '../../data-structures/binary-tree';
* - `recursive`: Indicates the recursive loop type (with loops that call themselves).
*/
export enum LoopType {
export enum IterationType {
ITERATIVE = 'ITERATIVE',
RECURSIVE = 'RECURSIVE'
}
@ -22,8 +22,9 @@ export enum FamilyPosition {
MAL_NODE = 'MAL_NODE'
}
export type BinaryTreeNodePropertyName = 'key' | 'val';
export type BTNKey = number;
<<<<<<< HEAD:src/types/data-structures/binary-tree.ts
export type NodeOrPropertyName = 'node' | BinaryTreeNodePropertyName;
export type DFSOrderPattern = 'in' | 'pre' | 'post';
@ -36,10 +37,10 @@ export type BinaryTreeNodeProperty<N extends BinaryTreeNode<N['val'], N>> =
| number
| BinaryTreeNodeKey;
export type BinaryTreeDeletedResult<N> = { deleted: N | null | undefined; needBalanced: N | null | undefined };
export type BinaryTreeNodeProperties<N extends BinaryTreeNode<N['val'], N>> =
BinaryTreeNodeProperty<N>[];
=======
export type BinaryTreeDeletedResult<N> = { deleted: N | null | undefined; needBalanced: N | null };
>>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d:src/types/data-structures/binary-tree/binary-tree.ts
export type BinaryTreeNodeNested<T> = BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, BinaryTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeOptions = { loopType?: LoopType }
export type BinaryTreeOptions = { iterationType?: IterationType }

View file

@ -1,7 +1,7 @@
import {BSTNode} from '../../data-structures/binary-tree';
import type {BinaryTreeNodeKey, BinaryTreeOptions} from './binary-tree';
import {BSTNode} from '../../../data-structures';
import type {BinaryTreeOptions, BTNKey} from './binary-tree';
export type BSTComparator = (a: BinaryTreeNodeKey, b: BinaryTreeNodeKey) => number;
export type BSTComparator = (a: BTNKey, b: BTNKey) => number;
// prettier-ignore
export type BSTNodeNested<T> = BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@ -9,5 +9,3 @@ export type BSTNodeNested<T> = BSTNode<T, BSTNode<T, BSTNode<T, BSTNode<T, BSTNo
export type BSTOptions = BinaryTreeOptions & {
comparator?: BSTComparator,
}
export enum CP {lt = 'lt', eq = 'eq', gt = 'gt'}

View file

@ -0,0 +1,6 @@
export * from './binary-tree';
export * from './bst';
export * from './avl-tree';
export * from './segment-tree';
export * from './tree-multiset';
export * from './rb-tree';

View file

@ -1,8 +1,8 @@
import {BinaryTreeOptions} from './binary-tree';
import {RBTreeNode} from '../../data-structures/binary-tree';
import {RBTreeNode} from '../../../data-structures';
export enum RBColor { RED = 'RED', BLACK = 'BLACK'}
export type RBTreeNodeNested<T> = RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, RBTreeNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type RBTreeOptions = BinaryTreeOptions & {}
export type RBTreeOptions = BinaryTreeOptions & {}

View file

@ -1,4 +1,4 @@
import {TreeMultisetNode} from '../../data-structures/binary-tree';
import {TreeMultisetNode} from '../../../data-structures';
import {AVLTreeOptions} from './avl-tree';
export type TreeMultisetNodeNested<T> = TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, TreeMultisetNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View file

@ -0,0 +1,3 @@
export * from './abstract-graph';
export * from './map-graph';
export * from './directed-graph';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -1,3 +0,0 @@
export type HeapComparator<T> = (a: T, b: T) => number;
export type HeapDFSOrderPattern = 'pre' | 'in' | 'post';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export * from './heap';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -1,14 +1,11 @@
export * from './binary-tree';
export * from './bst';
export * from './avl-tree';
export * from './segment-tree';
export * from './tree-multiset';
export * from './abstract-graph';
export * from './map-graph';
export * from './rb-tree';
export * from './directed-graph';
export * from './graph';
export * from './linked-list';
export * from './heap';
export * from './singly-linked-list';
export * from './doubly-linked-list';
export * from './navigator';
export * from './matrix';
export * from './hash';
export * from './priority-queue';
export * from './queue';
export * from './stack';
export * from './tree';
export * from './trie';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1,2 @@
export * from './singly-linked-list';
export * from './doubly-linked-list';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export * from './navigator';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -1,6 +1,6 @@
export type Direction = 'up' | 'right' | 'down' | 'left';
export type Turning = {[key in Direction]: Direction};
export type Turning = { [key in Direction]: Direction };
export type NavigatorParams<T = any> = {
matrix: T[][];

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1,3 @@
export * from './priority-queue';
export * from './min-priority-queue';
export * from './max-priority-queue';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1,2 @@
export * from './queue';
export * from './deque';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export * from './stack';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export * from './tree';

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1 @@
export * from './trie';

View file

@ -0,0 +1 @@
export {};

Some files were not shown because too many files have changed in this diff Show more