Successfully implemented recursive type inference for the BinaryTreeNode type by passing the node constructor through the constructor, effectively addressing the type inconsistency caused by invoking parent class methods after inheritance.

This commit is contained in:
Revone 2023-08-22 22:50:16 +08:00
parent d3f5dd4a9e
commit 60e08d3ac5
23 changed files with 940 additions and 623 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ __tests__/
notes/
docs/
backup/

View file

@ -8,6 +8,7 @@
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/notes" />
<excludeFolder url="file://$MODULE_DIR$/docs" />
<excludeFolder url="file://$MODULE_DIR$/backup" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

259
README.md
View file

@ -8,10 +8,200 @@ wide range of data structures
## Data Structures
Binary Tree, Binary Search Tree (BST), AVL Tree, Tree Multiset, Segment Tree, Binary Indexed Tree, Graph, Directed
Graph, Undirected Graph, Linked List, Singly Linked List, Doubly Linked List, Queue, Object Deque, Array Deque, Stack,
Hash, Coordinate Set, Coordinate Map, Heap, Priority Queue, Max Priority Queue, Min Priority Queue, Trie
<table>
<thead>
<tr>
<th>Data Structure</th>
<th>Unit Test</th>
<th>Performance Test</th>
<th>API Documentation</th>
<th>Implemented</th>
</tr>
</thead>
<tbody>
<tr>
<td>Binary Tree</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt="">
</img></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt="">
</img></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/BinaryTree.html"><span>Binary Tree</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Binary Search Tree (BST)</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/BST.html"><span>BST</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>AVL Tree</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/AVLTree.html"><span>AVLTree</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Tree Multiset</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/TreeMultiSet.html"><span>TreeMultiSet</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Segment Tree</td>
<td></td>
<td></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/SegmentTree.html"><span>SegmentTree</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Binary Indexed Tree</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/BinaryIndexedTree.html"><span>BinaryIndexedTree</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Graph</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/AbstractGraph.html"><span>AbstractGraph</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Directed Graph</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/DirectedGraph.html"><span>DirectedGraph</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Undirected Graph</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/UndirectedGraph.html"><span>UndirectedGraph</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Linked List</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/SinglyLinkedList.html"><span>SinglyLinkedList</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Singly Linked List</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/SinglyLinkedList.html"><span>SinglyLinkedList</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Doubly Linked List</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/DoublyLinkedList.html"><span>DoublyLinkedList</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Queue</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/Queue.html"><span>Queue</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Object Deque</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/ObjectDeque.html"><span>ObjectDeque</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Array Deque</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/ArrayDeque.html"><span>ArrayDeque</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Stack</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/Stack.html"><span>Stack</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
[//]: # (<tr>)
[//]: # (<td>Hash</td>)
[//]: # (<td></td>)
[//]: # (<td></td>)
[//]: # (<td><a href="https://data-structure-typed-docs.vercel.app/classes/HashTable.html"><span>HashTable</span></a></td>)
[//]: # (<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>)
[//]: # (</tr>)
<tr>
<td>Coordinate Set</td>
<td></td>
<td></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/CoordinateSet.html"><span>CoordinateSet</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Coordinate Map</td>
<td></td>
<td></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/CoordinateMap.html"><span>CoordinateMap</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Heap</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/Heap.html"><span>Heap</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Priority Queue</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/PriorityQueue.html"><span>PriorityQueue</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Max Priority Queue</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/MaxPriorityQueue.html"><span>MaxPriorityQueue</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Min Priority Queue</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/MinPriorityQueue.html"><span>MinPriorityQueue</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
<tr>
<td>Trie</td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
<td></td>
<td><a href="https://data-structure-typed-docs.vercel.app/classes/Trie.html"><span>Trie</span></a></td>
<td><img src="https://raw.githubusercontent.com/zrwusa/assets/master/images/data-structure-typed/assets/tick.svg" alt=""></td>
</tr>
</tbody>
</table>
## Algorithms list only a few out, you can discover more in API docs
DFS, DFSIterative, BFS, morris, Bellman-Ford Algorithm, Dijkstra's Algorithm, Floyd-Warshall Algorithm, Tarjan's Algorithm
# How
@ -409,69 +599,6 @@ describe('DirectedGraph Test3', () => {
## API docs
[//]: # ([api docs]&#40;https://data-structure-typed-docs.vercel.app/&#41;)
<nav class="tsd-navigation"><a href="https://data-structure-typed-docs.vercel.app/modules.html" class="current"><span>data-<wbr/>structure-<wbr/>typed</span></a>
<ul class="tsd-small-nested-navigation">
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/enums/CP.html"><span>CP</span></a></li>)
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/enums/FamilyPosition.html"><span>Family<wbr/>Position</span></a></li>)
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/enums/LoopType.html"><span>Loop<wbr/>Type</span></a></li>)
<li><a href="https://data-structure-typed-docs.vercel.app/classes/AVLTree.html"><span>AVLTree</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/AVLTreeNode.html"><span>AVLTree<wbr/>Node</span></a></li>
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/classes/AaTree.html"><span>Aa<wbr/>Tree</span></a></li>)
<li><a href="https://data-structure-typed-docs.vercel.app/classes/AbstractEdge.html"><span>Abstract<wbr/>Edge</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/AbstractGraph.html"><span>Abstract<wbr/>Graph</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/AbstractVertex.html"><span>Abstract<wbr/>Vertex</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/ArrayDeque.html"><span>Array<wbr/>Deque</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/BST.html"><span>BST</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/BSTNode.html"><span>BSTNode</span></a></li>
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/classes/BTree.html"><span>BTree</span></a></li>)
<li><a href="https://data-structure-typed-docs.vercel.app/classes/BinaryIndexedTree.html"><span>Binary<wbr/>Indexed<wbr/>Tree</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/BinaryTree.html"><span>Binary<wbr/>Tree</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/BinaryTreeNode.html"><span>Binary<wbr/>Tree<wbr/>Node</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Character.html"><span>Character</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/CoordinateMap.html"><span>Coordinate<wbr/>Map</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/CoordinateSet.html"><span>Coordinate<wbr/>Set</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Deque.html"><span>Deque</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/DirectedEdge.html"><span>Directed<wbr/>Edge</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/DirectedGraph.html"><span>Directed<wbr/>Graph</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/DirectedVertex.html"><span>Directed<wbr/>Vertex</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/DoublyLinkedList.html"><span>Doubly<wbr/>Linked<wbr/>List</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/DoublyLinkedListNode.html"><span>Doubly<wbr/>Linked<wbr/>List<wbr/>Node</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Heap.html"><span>Heap</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Matrix2D.html"><span>Matrix2D</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/MatrixNTI2D.html"><span>MatrixNTI2D</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/MaxHeap.html"><span>Max<wbr/>Heap</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/MaxPriorityQueue.html"><span>Max<wbr/>Priority<wbr/>Queue</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/MinHeap.html"><span>Min<wbr/>Heap</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/MinPriorityQueue.html"><span>Min<wbr/>Priority<wbr/>Queue</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Navigator.html"><span>Navigator</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/ObjectDeque.html"><span>Object<wbr/>Deque</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/PriorityQueue.html"><span>Priority<wbr/>Queue</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Queue.html"><span>Queue</span></a></li>
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/classes/RBTree.html"><span>RBTree</span></a></li>)
<li><a href="https://data-structure-typed-docs.vercel.app/classes/SegmentTree.html"><span>Segment<wbr/>Tree</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/SegmentTreeNode.html"><span>Segment<wbr/>Tree<wbr/>Node</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/SinglyLinkedList.html"><span>Singly<wbr/>Linked<wbr/>List</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/SinglyLinkedListNode.html"><span>Singly<wbr/>Linked<wbr/>List<wbr/>Node</span></a></li>
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/classes/SplayTree.html"><span>Splay<wbr/>Tree</span></a></li>)
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Stack.html"><span>Stack</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/TreeMultiSet.html"><span>Tree<wbr/>Multi<wbr/>Set</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Trie.html"><span>Trie</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/TrieNode.html"><span>Trie<wbr/>Node</span></a></li>
[//]: # (<li><a href="https://data-structure-typed-docs.vercel.app/classes/TwoThreeTree.html"><span>Two<wbr/>Three<wbr/>Tree</span></a></li>)
<li><a href="https://data-structure-typed-docs.vercel.app/classes/UndirectedEdge.html"><span>Undirected<wbr/>Edge</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/UndirectedGraph.html"><span>Undirected<wbr/>Graph</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/UndirectedVertex.html"><span>Undirected<wbr/>Vertex</span></a></li>
<li><a href="https://data-structure-typed-docs.vercel.app/classes/Vector2D.html"><span>Vector2D</span></a></li></ul></nav>
[//]: # ([Examples Repository]&#40;https://github.com/zrwusa/data-structure-typed-examples&#41;)

View file

@ -1,6 +1,6 @@
{
"name": "data-structure-typed",
"version": "1.18.0",
"version": "1.18.5",
"description": "Explore our comprehensive Javascript Data Structure / TypeScript Data Structure Library, meticulously crafted to empower developers with a versatile set of essential data structures. Our library includes a wide range of data structures, such as Binary Tree, AVL Tree, Binary Search Tree (BST), Tree Multiset, Segment Tree, Binary Indexed Tree, Graph, Directed Graph, Undirected Graph, Singly Linked List, Hash, CoordinateSet, CoordinateMap, Heap, Doubly Linked List, Priority Queue, Max Priority Queue, Min Priority Queue, Queue, ObjectDeque, ArrayDeque, Stack, and Trie. Each data structure is thoughtfully designed and implemented using TypeScript to provide efficient, reliable, and easy-to-use solutions for your programming needs. Whether you're optimizing algorithms, managing data, or enhancing performance, our TypeScript Data Structure Library is your go-to resource. Elevate your coding experience with these fundamental building blocks for software development.",
"main": "dist/index.js",
"scripts": {
@ -38,7 +38,15 @@
"Priority Queue",
"Max Priority Queue",
"Min Priority Queue",
"Trie"
"Trie",
"DFS",
"DFSIterative",
"BFS",
"morris",
"Bellman-Ford ",
"Dijkstra's Algorithm",
"Floyd-Warshall Algorithm",
"Tarjan's Algorithm"
],
"author": "Tyler Zeng zrwusa@gmail.com",
"license": "MIT",

View file

@ -6,19 +6,19 @@
* @license MIT License
*/
import {BST, BSTNode} from './bst';
import type {AVLTreeDeleted, BinaryTreeNodeId} from '../types';
import type {AVLTreeDeleted, BinaryTreeNodeId, RecursiveAVLTreeNode} from '../types';
import {IBinaryTreeNode} from '../interfaces';
export class AVLTreeNode<T> extends BSTNode<T> implements IBinaryTreeNode<T> {
override _createNode(id: BinaryTreeNodeId, val: T | null, count?: number): AVLTreeNode<T> | null {
return val !== null ? new AVLTreeNode<T>(id, val, count) : null;
}
export class AVLTreeNode<T, FAMILY extends AVLTreeNode<T, FAMILY> = RecursiveAVLTreeNode<T>> extends BSTNode<T, FAMILY> implements IBinaryTreeNode<T, FAMILY> {
}
export class AVLTree<T> extends BST<T> {
export class AVLTree<N extends AVLTreeNode<N['val'], N> = AVLTreeNode<number>> extends BST<N> {
override _createNode(id: BinaryTreeNodeId, val: T, count?: number): AVLTreeNode<T> {
return new AVLTreeNode<T>(id, val, count);
override _createNode(id: BinaryTreeNodeId, val: N['val'], count?: number): N {
const node = new AVLTreeNode<N['val'], N>(id, val, count);
return node as N;
}
/**
@ -26,14 +26,14 @@ export class AVLTree<T> extends BST<T> {
* balances the tree.
* @param {BinaryTreeNodeId} id - The `id` parameter is the identifier of the binary tree node that we want to add or
* update in the AVL tree.
* @param {T | null} val - The `val` parameter represents the value that you want to assign to the node with the given
* `id`. It can be of type `T` (the generic type) or `null`.
* @param {N | null} val - The `val` parameter represents the value that you want to assign to the node with the given
* `id`. It can be of type `N` (the generic type) or `null`.
* @param {number} [count] - The `count` parameter is an optional parameter of type `number`. It represents the number
* of times the value `val` should be inserted into the AVL tree. If the `count` parameter is not provided, it defaults
* to `1`, indicating that the value should be inserted once.
* @returns The method is returning either an AVLTreeNode<T> object or null.
* @returns The method is returning either an N object or null.
*/
override add(id: BinaryTreeNodeId, val: T | null, count?: number): AVLTreeNode<T> | null {
override add(id: BinaryTreeNodeId, val: N['val'] | null, count?: number): N | null {
const inserted = super.add(id, val, count);
if (inserted) this.balancePath(inserted);
return inserted;
@ -47,9 +47,9 @@ export class AVLTree<T> extends BST<T> {
* @param {boolean} [isUpdateAllLeftSum] - The `isUpdateAllLeftSum` parameter is an optional boolean parameter that
* determines whether the left sum of all nodes in the AVL tree should be updated after removing a node. If
* `isUpdateAllLeftSum` is set to `true`, the left sum of all nodes will be recalculated.
* @returns The method is returning an array of `AVLTreeDeleted<T>` objects.
* @returns The method is returning an array of `AVLTreeDeleted<N>` objects.
*/
override remove(id: BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): AVLTreeDeleted<T>[] {
override remove(id: BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): AVLTreeDeleted<N>[] {
const deletedResults = super.remove(id, isUpdateAllLeftSum);
for (const {needBalanced} of deletedResults) {
if (needBalanced) {
@ -62,10 +62,10 @@ export class AVLTree<T> extends BST<T> {
/**
* 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 AVLTreeNode<T>, which represents a node in an AVL tree.
* @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.
*/
balanceFactor(node: AVLTreeNode<T>): number {
balanceFactor(node: N): number {
if (!node.right) // node has no right subtree
return -node.height;
else if (!node.left) // node has no left subtree
@ -78,7 +78,7 @@ export class AVLTree<T> extends BST<T> {
* 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.
*/
updateHeight(node: AVLTreeNode<T>): void {
updateHeight(node: N): void {
if (!node.left && !node.right) // node is a leaf
node.height = 0;
else if (!node.left) {
@ -96,7 +96,7 @@ export class AVLTree<T> extends BST<T> {
* 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.
*/
balancePath(node: AVLTreeNode<T>): void {
balancePath(node: N): void {
const path = this.getPathToRoot(node);
for (let i = path.length - 1; i >= 0; i--) {
const A = path[i];
@ -127,7 +127,7 @@ export class AVLTree<T> extends BST<T> {
* The `balanceLL` function performs a left-left rotation on an AVL tree to balance it.
* @param A - The parameter A is an AVLTreeNode object.
*/
balanceLL(A: AVLTreeNode<T>): void {
balanceLL(A: N): void {
const parentOfA = A.parent;
const B = A.left; // A is left-heavy and B is left-heavy
A.parent = B;
@ -157,7 +157,7 @@ export class AVLTree<T> extends BST<T> {
* The `balanceLR` function performs a left-right rotation to balance an AVL tree.
* @param A - A is an AVLTreeNode object.
*/
balanceLR(A: AVLTreeNode<T>): void {
balanceLR(A: N): void {
const parentOfA = A.parent;
const B = A.left; // A is left-heavy
let C = null;
@ -205,7 +205,7 @@ export class AVLTree<T> extends BST<T> {
* The `balanceRR` function performs a right-right rotation on an AVL tree to balance it.
* @param A - The parameter A is an AVLTreeNode object.
*/
balanceRR(A: AVLTreeNode<T>): void {
balanceRR(A: N): void {
const parentOfA = A.parent;
const B = A.right; // A is right-heavy and B is right-heavy
A.parent = B;
@ -240,7 +240,7 @@ export class AVLTree<T> extends BST<T> {
* The `balanceRL` function performs a right-left rotation to balance an AVL tree.
* @param A - A is an AVLTreeNode object.
*/
balanceRL(A: AVLTreeNode<T>): void {
balanceRL(A: N): void {
const parentOfA = A.parent;
const B = A.right; // A is right-heavy
let C = null;

View file

@ -13,6 +13,7 @@ import type {
BinaryTreeNodePropertyName,
DFSOrderPattern,
NodeOrPropertyName,
RecursiveBinaryTreeNode,
ResultByProperty,
ResultsByProperty
} from '../types';
@ -29,7 +30,7 @@ export enum FamilyPosition {root, left, right}
*/
export enum LoopType { iterative = 1, recursive = 2}
export class BinaryTreeNode<T> implements IBinaryTreeNode<T> {
export class BinaryTreeNode<T, FAMILY extends BinaryTreeNode<T, FAMILY> = RecursiveBinaryTreeNode<T>> implements IBinaryTreeNode<T, FAMILY> {
constructor(id: BinaryTreeNodeId, val: T, count?: number) {
this._id = id;
@ -57,41 +58,41 @@ export class BinaryTreeNode<T> implements IBinaryTreeNode<T> {
this._val = v;
}
private _left?: BinaryTreeNode<T> | null;
private _left?: FAMILY | null;
get left(): BinaryTreeNode<T> | null | undefined {
get left(): FAMILY | null | undefined {
return this._left;
}
set left(v: BinaryTreeNode<T> | null | undefined) {
set left(v: FAMILY | null | undefined) {
if (v) {
v.parent = this;
v.parent = this as unknown as FAMILY;
v.familyPosition = FamilyPosition.left;
}
this._left = v;
}
private _right?: BinaryTreeNode<T> | null;
private _right?: FAMILY | null;
get right(): BinaryTreeNode<T> | null | undefined {
get right(): FAMILY | null | undefined {
return this._right;
}
set right(v: BinaryTreeNode<T> | null | undefined) {
set right(v: FAMILY | null | undefined) {
if (v) {
v.parent = this;
v.parent = this as unknown as FAMILY;
v.familyPosition = FamilyPosition.right;
}
this._right = v;
}
private _parent: BinaryTreeNode<T> | null | undefined;
private _parent: FAMILY | null | undefined;
get parent(): BinaryTreeNode<T> | null | undefined {
get parent(): FAMILY | null | undefined {
return this._parent;
}
set parent(v: BinaryTreeNode<T> | null | undefined) {
set parent(v: FAMILY | null | undefined) {
this._parent = v;
}
@ -125,11 +126,11 @@ export class BinaryTreeNode<T> implements IBinaryTreeNode<T> {
this._height = v;
}
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): BinaryTreeNode<T> | null {
return val !== null ? new BinaryTreeNode<T>(id, val, count) : null;
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): FAMILY | null {
return val !== null ? new BinaryTreeNode<T, FAMILY>(id, val, count) as FAMILY : null;
}
swapLocation(swapNode: BinaryTreeNode<T>): BinaryTreeNode<T> {
swapLocation(swapNode: FAMILY): FAMILY {
const {val, count, height} = swapNode;
const tempNode = this._createNode(swapNode.id, val);
if (tempNode instanceof BinaryTreeNode) {
@ -150,12 +151,12 @@ export class BinaryTreeNode<T> implements IBinaryTreeNode<T> {
return swapNode;
}
clone(): BinaryTreeNode<T> | null {
clone(): FAMILY | null {
return this._createNode(this.id, this.val, this.count);
}
}
export class BinaryTree<T> implements IBinaryTree<T> {
export class BinaryTree<N extends BinaryTreeNode<N['val'], N> = BinaryTreeNode<number>> implements IBinaryTree<N> {
/**
* The constructor function accepts an optional options object and sets the values of loopType, autoIncrementId, and
@ -190,15 +191,15 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return this._visitedId;
}
private _visitedVal: Array<T> = [];
private _visitedVal: Array<N['val']> = [];
get visitedVal(): Array<T> {
get visitedVal(): Array<N['val']> {
return this._visitedVal;
}
private _visitedNode: BinaryTreeNode<T>[] = [];
private _visitedNode: N[] = [];
get visitedNode(): BinaryTreeNode<T>[] {
get visitedNode(): N[] {
return this._visitedNode;
}
@ -232,9 +233,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return this._isDuplicatedVal;
}
private _root: BinaryTreeNode<T> | null = null;
private _root: N | null = null;
get root(): BinaryTreeNode<T> | null {
get root(): N | null {
return this._root;
}
@ -255,14 +256,15 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* it returns null.
* @param {BinaryTreeNodeId} id - The `id` parameter is the identifier for the binary tree node. It is of type
* `BinaryTreeNodeId`.
* @param {T | null} val - The `val` parameter represents the value of the node. It can be of type `T` (generic type)
* @param {N | null} val - The `val` parameter represents the value of the node. It can be of type `N` (generic type)
* or `null`.
* @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 tree node. If not provided, the default value is `undefined`.
* @returns a BinaryTreeNode object if the value is not null, otherwise it returns null.
*/
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): BinaryTreeNode<T> | null {
return val !== null ? new BinaryTreeNode<T>(id, val, count) : null;
_createNode(id: BinaryTreeNodeId, val: N['val'] | null, count?: number): N | null {
const node = new BinaryTreeNode<N['val'], N>(id, val, count);
return node as N | null;
}
/**
@ -288,17 +290,17 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* already exists.
* @param {BinaryTreeNodeId} id - The id parameter is the identifier of the binary tree node. It is used to uniquely
* identify each node in the binary tree.
* @param {T} val - The value to be inserted into the binary tree.
* @param {N} val - The value to be inserted into the binary tree.
* @param {number} [count] - The `count` parameter is an optional parameter that specifies the number of times the
* value should be inserted into the binary tree. If not provided, it defaults to 1.
* @returns The function `add` returns a `BinaryTreeNode<T>` object if a new node is inserted, or `null` if no new node
* @returns The function `add` returns a `N` object if a new node is inserted, or `null` if no new node
* is inserted, or `undefined` if the insertion fails.
*/
add(id: BinaryTreeNodeId, val: T, count?: number): BinaryTreeNode<T> | null | undefined {
add(id: BinaryTreeNodeId, val: N['val'], count?: number): N | null | undefined {
count = count ?? 1;
const _bfs = (root: BinaryTreeNode<T>, newNode: BinaryTreeNode<T> | null): BinaryTreeNode<T> | undefined | null => {
const queue: Array<BinaryTreeNode<T> | null> = [root];
const _bfs = (root: N, newNode: N | null): N | undefined | null => {
const queue: Array<N | null> = [root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
@ -311,7 +313,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return;
};
let inserted: BinaryTreeNode<T> | null | undefined;
let inserted: N | null | undefined;
const needInsert = val !== null ? this._createNode(id, val, count) : null;
const existNode = val !== null ? this.get(id, 'id') : null;
if (this.root) {
@ -338,13 +340,13 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The function inserts a new node into a binary tree as the left or right child of a given parent node.
* @param {BinaryTreeNode<T> | null} newNode - The `newNode` parameter is an instance of the `BinaryTreeNode` class or
* @param {N | null} newNode - The `newNode` parameter is an instance of the `BinaryTreeNode` class or
* `null`. It represents the node that needs to be inserted into the binary tree.
* @param parent - The `parent` parameter is a BinaryTreeNode object representing the parent node to which the new node
* will be inserted as a child.
* @returns The method returns the newly inserted node, either as the left child or the right child of the parent node.
*/
addTo(newNode: BinaryTreeNode<T> | null, parent: BinaryTreeNode<T>) {
addTo(newNode: N | null, parent: N) {
if (parent) {
if (parent.left === undefined) {
if (newNode) {
@ -354,7 +356,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
parent.left = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._setCount(this.count + newNode?.count ?? 0)
this._setCount(this.count + newNode.count ?? 0)
}
return parent.left;
@ -366,7 +368,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
parent.right = newNode;
if (newNode !== null) {
this._setSize(this.size + 1);
this._setCount(this.count + newNode?.count ?? 0);
this._setCount(this.count + newNode.count ?? 0);
}
return parent.right;
} else {
@ -380,13 +382,13 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The `addMany` function inserts multiple items into a binary tree and returns an array of the inserted nodes or
* null/undefined values.
* @param {T[] | BinaryTreeNode<T>[]} data - The `data` parameter can be either an array of elements of type `T` or an
* array of `BinaryTreeNode<T>` objects.
* @returns The function `addMany` returns an array of `BinaryTreeNode<T>`, `null`, or `undefined` values.
* @param {N[] | N[]} data - The `data` parameter can be either an array of elements of type `N` or an
* array of `N` objects.
* @returns The function `addMany` returns an array of `N`, `null`, or `undefined` values.
*/
addMany(data: T[] | BinaryTreeNode<T>[]): (BinaryTreeNode<T> | null | undefined)[] {
const inserted: (BinaryTreeNode<T> | null | undefined)[] = [];
const map: Map<T | BinaryTreeNode<T>, number> = new Map();
addMany(data: N[] | Array<N['val']>): (N | null | undefined)[] {
const inserted: (N | null | undefined)[] = [];
const map: Map<N | N['val'], number> = new Map();
if (!this._isDuplicatedVal) {
for (const i of data) map.set(i, (map.get(i) ?? 0) + 1);
@ -427,11 +429,11 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The `fill` function clears the current data and inserts new data, returning a boolean indicating if the insertion
* was successful.
* @param {T[] | BinaryTreeNode<T>[]} data - The `data` parameter can be either an array of elements of type `T` or an
* array of `BinaryTreeNode<T>` objects.
* @param {N[] | N[]} data - The `data` parameter can be either an array of elements of type `N` or an
* array of `N` objects.
* @returns The method is returning a boolean value.
*/
fill(data: T[] | BinaryTreeNode<T>[]): boolean {
fill(data: N[] | Array<N['val']>): boolean {
this.clear();
return data.length === this.addMany(data).length;
}
@ -447,9 +449,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* "needBalanced". The "deleted" property contains the deleted node or undefined if no node was deleted. The
* "needBalanced" property is always null.
*/
remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeleted<T>[] {
remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeleted<N>[] {
const nodes = this.getNodes(id, 'id', true);
let node: BinaryTreeNode<T> | null | undefined = nodes[0];
let node: N | null | undefined = nodes[0];
if (!node) node = undefined;
else if (node.count > 1 && !ignoreCount) {
@ -485,11 +487,11 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The function calculates the depth of a binary tree node by traversing its parent nodes.
* @param node - BinaryTreeNode<T> - This is the node for which we want to calculate the depth. It is a generic type,
* @param node - N - This is the node for which we want to calculate the depth. It is a generic type,
* meaning it can represent any type of data that we want to store in the node.
* @returns The depth of the given binary tree node.
*/
getDepth(node: BinaryTreeNode<T>): number {
getDepth(node: N): number {
let depth = 0;
while (node.parent) {
depth++;
@ -501,17 +503,17 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The `getHeight` function calculates the maximum height of a binary tree using either a recursive or iterative
* approach.
* @param {BinaryTreeNode<T> | null} [beginRoot] - The `beginRoot` parameter is an optional parameter of type
* `BinaryTreeNode<T> | null`. It represents the starting node from which to calculate the height of the binary tree.
* @param {N | null} [beginRoot] - The `beginRoot` parameter is an optional parameter of type
* `N | null`. It represents the starting node from which to calculate the height of the binary tree.
* If no value is provided for `beginRoot`, the function will use the `root` property of the class instance as
* @returns the height of the binary tree.
*/
getHeight(beginRoot?: BinaryTreeNode<T> | null): number {
getHeight(beginRoot?: N | null): number {
beginRoot = beginRoot ?? this.root;
if (!beginRoot) return -1;
if (this._loopType === LoopType.recursive) {
const _getMaxHeight = (cur: BinaryTreeNode<T> | null | undefined): number => {
const _getMaxHeight = (cur: N | null | undefined): number => {
if (!cur) return -1;
const leftHeight = _getMaxHeight(cur.left);
const rightHeight = _getMaxHeight(cur.right);
@ -520,9 +522,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return _getMaxHeight(beginRoot);
} else {
const stack: BinaryTreeNode<T>[] = [];
let node: BinaryTreeNode<T> | null | undefined = beginRoot, last: BinaryTreeNode<T> | null = null;
const depths: Map<BinaryTreeNode<T>, number> = new Map();
const stack: N[] = [];
let node: N | null | undefined = beginRoot, last: N | null = null;
const depths: Map<N, number> = new Map();
while (stack.length > 0 || node) {
if (node) {
@ -550,17 +552,17 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The `getMinHeight` function calculates the minimum height of a binary tree using either a recursive or iterative
* approach.
* @param {BinaryTreeNode<T> | null} [beginRoot] - The `beginRoot` parameter is an optional parameter of type
* `BinaryTreeNode<T> | null`. It represents the starting node from which to calculate the minimum height of the binary
* @param {N | null} [beginRoot] - The `beginRoot` parameter is an optional parameter of type
* `N | null`. It represents the starting node from which to calculate the minimum height of the binary
* tree. If no value is provided for `beginRoot`, the function will use the root node of the binary tree.
* @returns The function `getMinHeight` returns the minimum height of the binary tree.
*/
getMinHeight(beginRoot?: BinaryTreeNode<T> | null): number {
getMinHeight(beginRoot?: N | null): number {
beginRoot = beginRoot || this.root;
if (!beginRoot) return -1;
if (this._loopType === LoopType.recursive) {
const _getMinHeight = (cur: BinaryTreeNode<T> | null | undefined): number => {
const _getMinHeight = (cur: N | null | undefined): number => {
if (!cur) return 0;
if (!cur.left && !cur.right) return 0;
const leftMinHeight = _getMinHeight(cur.left);
@ -570,9 +572,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return _getMinHeight(beginRoot);
} else {
const stack: BinaryTreeNode<T>[] = [];
let node: BinaryTreeNode<T> | null | undefined = beginRoot, last: BinaryTreeNode<T> | null = null;
const depths: Map<BinaryTreeNode<T>, number> = new Map();
const stack: N[] = [];
let node: N | null | undefined = beginRoot, last: N | null = null;
const depths: Map<N, number> = new Map();
while (stack.length > 0 || node) {
if (node) {
@ -599,34 +601,34 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The function checks if a binary tree is balanced by comparing the minimum height and the maximum height of the tree.
* @param {BinaryTreeNode<T> | null} [beginRoot] - The `beginRoot` parameter is the root node of a binary tree. It is
* of type `BinaryTreeNode<T> | null`, which means it can either be a `BinaryTreeNode` object or `null`.
* @param {N | null} [beginRoot] - The `beginRoot` parameter is the root node of a binary tree. It is
* of type `N | null`, which means it can either be a `BinaryTreeNode` object or `null`.
* @returns The method is returning a boolean value.
*/
isBalanced(beginRoot?: BinaryTreeNode<T> | null): boolean {
isBalanced(beginRoot?: N | null): boolean {
return (this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot));
}
/**
* The function `getNodes` returns an array of binary tree nodes that match a given property value, with options for
* searching recursively or iteratively.
* @param {BinaryTreeNodeId | T} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `T`. It represents the property of the binary tree node that you want to search for.
* @param {BinaryTreeNodeId | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` 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 when searching for nodes. If not provided, it defaults to 'id'.
* @param {boolean} [onlyOne] - The `onlyOne` parameter is an optional boolean parameter that determines whether to
* return only one node that matches the `nodeProperty` or `propertyName` criteria. If `onlyOne` is set to `true`, the
* function will stop traversing the tree and return the first matching node. If `
* @returns The function `getNodes` returns an array of `BinaryTreeNode<T> | null | undefined` objects.
* @returns The function `getNodes` returns an array of `N | null | undefined` objects.
*/
getNodes(nodeProperty: BinaryTreeNodeId | T, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean) {
getNodes(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean) {
if (!this.root) return [] as null[];
propertyName = propertyName ?? 'id';
const result: (BinaryTreeNode<T> | null | undefined)[] = [];
const result: (N | null | undefined)[] = [];
if (this._loopType === LoopType.recursive) {
const _traverse = (cur: BinaryTreeNode<T>) => {
const _traverse = (cur: N) => {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return;
if (!cur.left && !cur.right) return;
cur.left && _traverse(cur.left);
@ -635,7 +637,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
_traverse(this.root);
} else {
const queue: BinaryTreeNode<T>[] = [this.root];
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
@ -652,26 +654,26 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The function checks if a binary tree node has a specific property or if any node in the tree has a specific
* property.
* @param {BinaryTreeNodeId | T} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `T`. It represents the property of a binary tree node that you want to check.
* @param {BinaryTreeNodeId | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `N`. It represents the property of a binary tree node that you want to check.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the name of the property to check for in the nodes.
* @returns a boolean value.
*/
has(nodeProperty: BinaryTreeNodeId | T, propertyName ?: BinaryTreeNodePropertyName): boolean {
has(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName): boolean {
return this.getNodes(nodeProperty, propertyName).length > 0;
}
/**
* The function returns the first binary tree node that matches the given property name and value, or null if no match
* is found.
* @param {BinaryTreeNodeId | T} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `T`. It represents the property of the binary tree node that you want to search for.
* @param {BinaryTreeNodeId | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` 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 of the binary tree node to search for. If not provided, it defaults to `'id'`.
* @returns a BinaryTreeNode object or null.
*/
get(nodeProperty: BinaryTreeNodeId | T, propertyName ?: BinaryTreeNodePropertyName): BinaryTreeNode<T> | null {
get(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName): N | null {
propertyName = propertyName ?? 'id';
return this.getNodes(nodeProperty, propertyName, true)[0] ?? null;
}
@ -680,11 +682,11 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* The function getPathToRoot returns an array of BinaryTreeNode objects representing the path from a given node to the
* root of a binary tree.
* @param node - The `node` parameter is a BinaryTreeNode object.
* @returns The function `getPathToRoot` returns an array of `BinaryTreeNode<T>` objects, representing the path from
* @returns The function `getPathToRoot` returns an array of `N` objects, representing the path from
* the given `node` to the root of the binary tree.
*/
getPathToRoot(node: BinaryTreeNode<T>): BinaryTreeNode<T>[] {
const result: BinaryTreeNode<T>[] = [];
getPathToRoot(node: N): N[] {
const result: N[] = [];
while (node.parent) {
result.unshift(node);
node = node.parent;
@ -693,25 +695,25 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return result;
}
getLeftMost(): BinaryTreeNode<T> | null;
getLeftMost(): N | null;
getLeftMost(node: BinaryTreeNode<T>): BinaryTreeNode<T>;
getLeftMost(node: N): N;
/**
* The `getLeftMost` function returns the leftmost node in a binary tree, either recursively or iteratively using tail
* recursion optimization.
* @param {BinaryTreeNode<T> | null} [node] - The `node` parameter is an optional parameter of type `BinaryTreeNode<T>
* @param {N | null} [node] - The `node` parameter is an optional parameter of type `N
* | null`. It represents the starting node from which to find the leftmost node in a binary tree. If no node is
* provided, the function will use the root node of the binary tree.
* @returns The `getLeftMost` function returns the leftmost node in a binary tree.
*/
getLeftMost(node?: BinaryTreeNode<T> | null): BinaryTreeNode<T> | null {
getLeftMost(node?: N | null): N | null {
node = node ?? this.root;
if (!node) return node;
if (this._loopType === LoopType.recursive) {
const _traverse = (cur: BinaryTreeNode<T>): BinaryTreeNode<T> => {
const _traverse = (cur: N): N => {
if (!cur.left) return cur;
return _traverse(cur.left);
}
@ -719,7 +721,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return _traverse(node);
} else {
// Indirect implementation of iteration using tail recursion optimization
const _traverse = trampoline((cur: BinaryTreeNode<T>) => {
const _traverse = trampoline((cur: N) => {
if (!cur.left) return cur;
return _traverse.cont(cur.left);
});
@ -728,24 +730,24 @@ export class BinaryTree<T> implements IBinaryTree<T> {
}
}
getRightMost(): BinaryTreeNode<T> | null;
getRightMost(): N | null;
getRightMost(node: BinaryTreeNode<T>): BinaryTreeNode<T>;
getRightMost(node: N): N;
/**
* The `getRightMost` function returns the rightmost node in a binary tree, either recursively or iteratively using
* tail recursion optimization.
* @param {BinaryTreeNode<T> | null} [node] - The `node` parameter is an optional parameter of type `BinaryTreeNode<T>
* @param {N | null} [node] - The `node` parameter is an optional parameter of type `N
* | null`. It represents the starting node from which to find the rightmost node in a binary tree. If no node is
* provided, the function will use the root node of the binary tree.
* @returns The `getRightMost` function returns the rightmost node in a binary tree.
*/
getRightMost(node?: BinaryTreeNode<T> | null): BinaryTreeNode<T> | null {
getRightMost(node?: N | null): N | null {
node = node ?? this.root;
if (!node) return node;
if (this._loopType === LoopType.recursive) {
const _traverse = (cur: BinaryTreeNode<T>): BinaryTreeNode<T> => {
const _traverse = (cur: N): N => {
if (!cur.right) return cur;
return _traverse(cur.right);
}
@ -753,7 +755,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return _traverse(node);
} else {
// Indirect implementation of iteration using tail recursion optimization
const _traverse = trampoline((cur: BinaryTreeNode<T>) => {
const _traverse = trampoline((cur: N) => {
if (!cur.right) return cur;
return _traverse.cont(cur.right);
});
@ -764,18 +766,18 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The `isBST` function checks if a binary tree is a binary search tree.
* @param {BinaryTreeNode<T> | null} [node] - The `node` parameter is an optional parameter of type `BinaryTreeNode<T>
* @param {N | null} [node] - The `node` parameter is an optional parameter of type `N
* | null`. It represents the root node of the binary search tree (BST) that we want to check for validity. If no node
* is provided, the function will default to using the root node of the BST instance that
* @returns The `isBST` function returns a boolean value. It returns `true` if the binary tree is a valid binary search
* tree, and `false` otherwise.
*/
isBST(node?: BinaryTreeNode<T> | null): boolean {
isBST(node?: N | null): boolean {
node = node ?? this.root;
if (!node) return true;
if (this._loopType === LoopType.recursive) {
const dfs = (cur: BinaryTreeNode<T> | null | undefined, min: BinaryTreeNodeId, max: BinaryTreeNodeId): boolean => {
const dfs = (cur: N | null | undefined, min: BinaryTreeNodeId, max: BinaryTreeNodeId): boolean => {
if (!cur) return true;
if (cur.id <= min || cur.id >= max) return false;
return dfs(cur.left, min, cur.id) && dfs(cur.right, cur.id, max);
@ -784,7 +786,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return dfs(node, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
} else {
const stack = [];
let prev = Number.MIN_SAFE_INTEGER, curr: BinaryTreeNode<T> | null | undefined = node;
let prev = Number.MIN_SAFE_INTEGER, curr: N | null | undefined = node;
while (curr || stack.length > 0) {
while (curr) {
stack.push(curr);
@ -802,17 +804,17 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The function calculates the size and count of a subtree in a binary tree using either recursive or iterative
* traversal.
* @param {BinaryTreeNode<T> | null | undefined} subTreeRoot - The `subTreeRoot` parameter is the root node of a binary
* @param {N | null | undefined} subTreeRoot - The `subTreeRoot` parameter is the root node of a binary
* tree.
* @returns The function `getSubTreeSizeAndCount` returns an array `[number, number]`. The first element of the array
* represents the size of the subtree, and the second element represents the count of the nodes in the subtree.
*/
getSubTreeSizeAndCount(subTreeRoot: BinaryTreeNode<T> | null | undefined) {
getSubTreeSizeAndCount(subTreeRoot: N | null | undefined) {
const res: [number, number] = [0, 0];
if (!subTreeRoot) return res;
if (this._loopType === LoopType.recursive) {
const _traverse = (cur: BinaryTreeNode<T>) => {
const _traverse = (cur: N) => {
res[0]++;
res[1] += cur.count;
cur.left && _traverse(cur.left);
@ -822,7 +824,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
_traverse(subTreeRoot);
return res;
} else {
const stack: BinaryTreeNode<T>[] = [subTreeRoot];
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
@ -848,13 +850,13 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* provided, it defaults to `'val'`.
* @returns a number, which is the sum of the values of the nodes in the subtree rooted at `subTreeRoot`.
*/
subTreeSum(subTreeRoot: BinaryTreeNode<T>, propertyName ?: BinaryTreeNodePropertyName): number {
subTreeSum(subTreeRoot: N, propertyName ?: BinaryTreeNodePropertyName): number {
propertyName = propertyName ?? 'val';
if (!subTreeRoot) return 0;
let sum = 0;
const _sumByProperty = (cur: BinaryTreeNode<T>) => {
const _sumByProperty = (cur: N) => {
let needSum: number;
switch (propertyName) {
case 'id':
@ -874,7 +876,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
}
if (this._loopType === LoopType.recursive) {
const _traverse = (cur: BinaryTreeNode<T>): void => {
const _traverse = (cur: N): void => {
sum += _sumByProperty(cur);
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
@ -882,7 +884,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
_traverse(subTreeRoot);
} else {
const stack: BinaryTreeNode<T>[] = [subTreeRoot];
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
@ -904,11 +906,11 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* specifies the property of the `BinaryTreeNode` that should be modified. It defaults to `'id'` if not provided.
* @returns a boolean value, which is `true`.
*/
subTreeAdd(subTreeRoot: BinaryTreeNode<T>, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
subTreeAdd(subTreeRoot: N, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
propertyName = propertyName ?? 'id';
if (!subTreeRoot) return false;
const _addByProperty = (cur: BinaryTreeNode<T>) => {
const _addByProperty = (cur: N) => {
switch (propertyName) {
case 'id':
cur.id += delta;
@ -924,7 +926,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
}
if (this._loopType === LoopType.recursive) {
const _traverse = (cur: BinaryTreeNode<T>) => {
const _traverse = (cur: N) => {
_addByProperty(cur);
cur.left && _traverse(cur.left);
cur.right && _traverse(cur.right);
@ -932,7 +934,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
_traverse(subTreeRoot);
} else {
const stack: BinaryTreeNode<T>[] = [subTreeRoot];
const stack: N[] = [subTreeRoot];
while (stack.length > 0) {
const cur = stack.pop()!;
@ -949,9 +951,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
BFS(nodeOrPropertyName: 'id'): BinaryTreeNodeId[];
BFS(nodeOrPropertyName: 'val'): T[];
BFS(nodeOrPropertyName: 'val'): N['val'][];
BFS(nodeOrPropertyName: 'node'): BinaryTreeNode<T>[];
BFS(nodeOrPropertyName: 'node'): N[];
BFS(nodeOrPropertyName: 'count'): number[];
@ -962,12 +964,12 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* represents either a node or a property name. If a node is provided, the breadth-first search algorithm will be
* performed starting from that node. If a property name is provided, the breadth-first search algorithm will be
* performed starting from the root node
* @returns an object of type `ResultsByProperty<T>`.
* @returns an object of type `ResultsByProperty<N>`.
*/
BFS(nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<T> {
BFS(nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<N> {
nodeOrPropertyName = nodeOrPropertyName ?? 'id';
this._resetResults();
const queue: Array<BinaryTreeNode<T> | null | undefined> = [this.root];
const queue: Array<N | null | undefined> = [this.root];
while (queue.length !== 0) {
const cur = queue.shift();
@ -985,9 +987,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[];
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'val'): T[];
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'val'): N[];
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): BinaryTreeNode<T>[];
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): N[];
DFS(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[];
@ -1001,13 +1003,13 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* either the name of a property in the `BinaryTreeNode` object or the value of the `id` property in the
* `BinaryTreeNode` object. This parameter is used to accumulate the results based on the specified property name. If
* no value
* @returns an object of type `ResultsByProperty<T>`.
* @returns an object of type `ResultsByProperty<N>`.
*/
DFS(pattern ?: 'in' | 'pre' | 'post', nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<T> {
DFS(pattern ?: 'in' | 'pre' | 'post', nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<N> {
pattern = pattern ?? 'in';
nodeOrPropertyName = nodeOrPropertyName ?? 'id';
this._resetResults();
const _traverse = (node: BinaryTreeNode<T>) => {
const _traverse = (node: N) => {
switch (pattern) {
case 'in':
if (node.left) _traverse(node.left);
@ -1035,9 +1037,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[];
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'val'): T[];
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'val'): N[];
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): BinaryTreeNode<T>[];
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): N[];
DFSIterative(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[];
@ -1048,13 +1050,13 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* @param nodeOrPropertyName
* @constructor
*/
DFSIterative(pattern ?: 'in' | 'pre' | 'post', nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<T> {
DFSIterative(pattern ?: 'in' | 'pre' | 'post', nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<N> {
pattern = pattern || 'in';
nodeOrPropertyName = nodeOrPropertyName || 'id';
this._resetResults();
if (!this.root) return this._getResultByPropertyName(nodeOrPropertyName);
// 0: visit, 1: print
const stack: { opt: 0 | 1, node: BinaryTreeNode<T> | null | undefined }[] = [{opt: 0, node: this.root}];
const stack: { opt: 0 | 1, node: N | null | undefined }[] = [{opt: 0, node: this.root}];
while (stack.length > 0) {
const cur = stack.pop();
@ -1090,35 +1092,35 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return this._getResultByPropertyName(nodeOrPropertyName);
}
levelIterative(node: BinaryTreeNode<T> | null): BinaryTreeNodeId[];
levelIterative(node: N | null): BinaryTreeNodeId[];
levelIterative(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[];
levelIterative(node: N | null, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[];
levelIterative(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'val'): T[];
levelIterative(node: N | null, nodeOrPropertyName?: 'val'): N['val'][];
levelIterative(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'node'): BinaryTreeNode<T>[];
levelIterative(node: N | null, nodeOrPropertyName?: 'node'): N[];
levelIterative(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'count'): number[];
levelIterative(node: N | null, nodeOrPropertyName?: 'count'): number[];
/**
* The `levelIterative` function performs a level-order traversal on a binary tree and returns the values of the nodes
* in an array, based on a specified property name.
* @param {BinaryTreeNode<T> | null} node - The `node` parameter is a BinaryTreeNode object representing the starting
* @param {N | null} node - The `node` parameter is a BinaryTreeNode object representing the starting
* node for the level order traversal. It can be null if no specific node is provided, in which case the root node of
* the tree is used as the starting node.
* @param {NodeOrPropertyName} [nodeOrPropertyName] - The `nodeOrPropertyName` parameter is an optional parameter that
* can be either a `BinaryTreeNode` property name or the string `'id'`. If a property name is provided, the function
* will accumulate results based on that property. If no property name is provided, the function will default to
* accumulating results
* @returns The function `levelIterative` returns an object of type `ResultsByProperty<T>`.
* @returns The function `levelIterative` returns an object of type `ResultsByProperty<N>`.
*/
levelIterative(node: BinaryTreeNode<T> | null, nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<T> {
levelIterative(node: N | null, nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<N> {
nodeOrPropertyName = nodeOrPropertyName || 'id';
node = node || this.root;
if (!node) return [];
this._resetResults();
const queue: BinaryTreeNode<T>[] = [node];
const queue: N[] = [node];
while (queue.length > 0) {
const cur = queue.shift();
@ -1136,33 +1138,33 @@ export class BinaryTree<T> implements IBinaryTree<T> {
return this._getResultByPropertyName(nodeOrPropertyName);
}
listLevels(node: BinaryTreeNode<T> | null): BinaryTreeNodeId[][];
listLevels(node: N | null): BinaryTreeNodeId[][];
listLevels(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[][];
listLevels(node: N | null, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[][];
listLevels(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'val'): T[][];
listLevels(node: N | null, nodeOrPropertyName?: 'val'): N['val'][][];
listLevels(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'node'): BinaryTreeNode<T>[][];
listLevels(node: N | null, nodeOrPropertyName?: 'node'): N[][];
listLevels(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: 'count'): number[][];
listLevels(node: N | null, nodeOrPropertyName?: 'count'): number[][];
/**
* The `listLevels` function collects nodes from a binary tree by a specified property and organizes them into levels.
* @param {BinaryTreeNode<T> | null} node - The `node` parameter is a BinaryTreeNode object or null. It represents the
* @param {N | null} node - The `node` parameter is a BinaryTreeNode object or null. It represents the
* root node of a binary tree. If it is null, the function will use the root node of the current binary tree instance.
* @param {NodeOrPropertyName} [nodeOrPropertyName] - The `nodeOrPropertyName` parameter is an optional parameter that
* specifies the property of the `BinaryTreeNode` object to collect at each level. It can be one of the following
* values:
* @returns The function `listLevels` returns a 2D array of `ResultByProperty<T>` objects.
* @returns The function `listLevels` returns a 2D array of `ResultByProperty<N>` objects.
*/
listLevels(node: BinaryTreeNode<T> | null, nodeOrPropertyName?: NodeOrPropertyName): ResultByProperty<T>[][] {
listLevels(node: N | null, nodeOrPropertyName?: NodeOrPropertyName): ResultByProperty<N>[][] {
nodeOrPropertyName = nodeOrPropertyName || 'id';
node = node || this.root;
if (!node) return [];
const levelsNodes: ResultByProperty<T>[][] = [];
const levelsNodes: ResultByProperty<N>[][] = [];
const collectByProperty = (node: BinaryTreeNode<T>, level: number) => {
const collectByProperty = (node: N, level: number) => {
switch (nodeOrPropertyName) {
case 'id':
levelsNodes[level].push(node.id);
@ -1183,7 +1185,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
}
if (this._loopType === LoopType.recursive) {
const _recursive = (node: BinaryTreeNode<T>, level: number) => {
const _recursive = (node: N, level: number) => {
if (!levelsNodes[level]) levelsNodes[level] = [];
collectByProperty(node, level);
if (node.left) _recursive(node.left, level + 1);
@ -1192,7 +1194,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
_recursive(node, 0);
} else {
const stack: [BinaryTreeNode<T>, number][] = [[node, 0]];
const stack: [N, number][] = [[node, 0]];
while (stack.length > 0) {
const head = stack.pop()!;
@ -1213,9 +1215,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* @param node - The parameter `node` is a BinaryTreeNode object, representing a node in a binary tree.
* @returns the predecessor of the given node in a binary tree.
*/
getPredecessor(node: BinaryTreeNode<T>): BinaryTreeNode<T> {
getPredecessor(node: N): N {
if (node.left) {
let predecessor: BinaryTreeNode<T> | null | undefined = node.left;
let predecessor: N | null | undefined = node.left;
while (!(predecessor) || predecessor.right && predecessor.right !== node) {
if (predecessor) {
predecessor = predecessor.right;
@ -1231,9 +1233,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'id'): BinaryTreeNodeId[];
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'val'): T[];
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'val'): N[];
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): BinaryTreeNode<T>[];
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'node'): N[];
morris(pattern?: DFSOrderPattern, nodeOrPropertyName?: 'count'): number[];
@ -1247,9 +1249,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* @param {NodeOrPropertyName} [nodeOrPropertyName] - The `nodeOrPropertyName` parameter is used to specify the
* property of the nodes that you want to retrieve in the results. It can be either the node itself or the name of the
* property. If not provided, it defaults to `'id'`.
* @returns The function `morris` returns an object of type `ResultsByProperty<T>`.
* @returns The function `morris` returns an object of type `ResultsByProperty<N>`.
*/
morris(pattern?: 'in' | 'pre' | 'post', nodeOrPropertyName?: NodeOrPropertyName): ResultsByProperty<T> {
morris(pattern?: 'in' | 'pre' | 'post', nodeOrPropertyName?: NodeOrPropertyName): ResultsByProperty<N> {
if (this.root === null) return [];
pattern = pattern || 'in';
@ -1257,10 +1259,10 @@ export class BinaryTree<T> implements IBinaryTree<T> {
this._resetResults();
let cur: BinaryTreeNode<T> | null | undefined = this.root;
const _reverseEdge = (node: BinaryTreeNode<T> | null | undefined) => {
let pre: BinaryTreeNode<T> | null | undefined = null;
let next: BinaryTreeNode<T> | null | undefined = null;
let cur: N | null | undefined = this.root;
const _reverseEdge = (node: N | null | undefined) => {
let pre: N | null | undefined = null;
let next: N | null | undefined = null;
while (node) {
next = node.right;
node.right = pre;
@ -1269,9 +1271,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
}
return pre;
};
const _printEdge = (node: BinaryTreeNode<T> | null) => {
const tail: BinaryTreeNode<T> | null | undefined = _reverseEdge(node);
let cur: BinaryTreeNode<T> | null | undefined = tail;
const _printEdge = (node: N | null) => {
const tail: N | null | undefined = _reverseEdge(node);
let cur: N | null | undefined = tail;
while (cur) {
this._accumulatedByPropertyName(cur, nodeOrPropertyName);
cur = cur.right;
@ -1343,11 +1345,11 @@ export class BinaryTree<T> implements IBinaryTree<T> {
this._visitedId = value;
}
protected _setVisitedVal(value: Array<T>) {
protected _setVisitedVal(value: Array<N>) {
this._visitedVal = value;
}
protected _setVisitedNode(value: BinaryTreeNode<T>[]) {
protected _setVisitedNode(value: N[]) {
this._visitedNode = value;
}
@ -1371,7 +1373,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
this._isDuplicatedVal = value;
}
protected _setRoot(v: BinaryTreeNode<T> | null) {
protected _setRoot(v: N | null) {
if (v) {
v.parent = null;
v.familyPosition = FamilyPosition.root;
@ -1402,9 +1404,9 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* The function checks if a given property of a binary tree node matches a specified value, and if so, adds the node to
* a result array.
* @param cur - The current binary tree node that is being checked.
* @param {(BinaryTreeNode<T> | null | undefined)[]} result - An array that stores the matching nodes found during the
* @param {(N | null | undefined)[]} result - An array that stores the matching nodes found during the
* traversal.
* @param {BinaryTreeNodeId | T} nodeProperty - The `nodeProperty` parameter is the value that we are searching for in
* @param {BinaryTreeNodeId | N} nodeProperty - The `nodeProperty` parameter is the value that we are searching for in
* the binary tree nodes. It can be either the `id`, `count`, or `val` property of the node.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property of the `BinaryTreeNode` object that you want to compare with the `nodeProperty` value. It can
@ -1414,7 +1416,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* `true`, the function will stop after finding the first matching node and return `true`. If `onlyOne
* @returns a boolean value indicating whether or not a node was pushed into the result array.
*/
protected _pushByPropertyNameStopOrNot(cur: BinaryTreeNode<T>, result: (BinaryTreeNode<T> | null | undefined)[], nodeProperty: BinaryTreeNodeId | T, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean) {
protected _pushByPropertyNameStopOrNot(cur: N, result: (N | null | undefined)[], nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean) {
switch (propertyName) {
case 'id':
if (cur.id === nodeProperty) {
@ -1446,12 +1448,12 @@ export class BinaryTree<T> implements IBinaryTree<T> {
/**
* The function `_accumulatedByPropertyName` pushes a property value of a binary tree node into an array based on the
* provided property name or a default property name.
* @param node - The `node` parameter is of type `BinaryTreeNode<T>`, which represents a node in a binary tree.
* @param node - The `node` parameter is of type `N`, which represents a node in a binary tree.
* @param {NodeOrPropertyName} [nodeOrPropertyName] - The parameter `nodeOrPropertyName` is an optional parameter that
* can be either a string representing a property name or a reference to a node object. If it is a string, it specifies
* the property name of the node that should be accumulated. If it is a node object, it specifies the node itself
*/
protected _accumulatedByPropertyName(node: BinaryTreeNode<T>, nodeOrPropertyName ?: NodeOrPropertyName) {
protected _accumulatedByPropertyName(node: N, nodeOrPropertyName ?: NodeOrPropertyName) {
nodeOrPropertyName = nodeOrPropertyName ?? 'id';
switch (nodeOrPropertyName) {
@ -1480,7 +1482,7 @@ export class BinaryTree<T> implements IBinaryTree<T> {
* can accept a value of type `NodeOrPropertyName`.
* @returns The method returns an object of type `ResultsByProperty<T>`.
*/
protected _getResultByPropertyName(nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<T> {
protected _getResultByPropertyName(nodeOrPropertyName ?: NodeOrPropertyName): ResultsByProperty<N> {
nodeOrPropertyName = nodeOrPropertyName ?? 'id';
switch (nodeOrPropertyName) {

View file

@ -5,19 +5,23 @@
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import type {BinaryTreeNodeId, BinaryTreeNodePropertyName, BSTComparator, BSTDeletedResult} from '../types';
import type {
BinaryTreeNodeId,
BinaryTreeNodePropertyName,
BSTComparator,
BSTDeletedResult,
RecursiveBSTNode
} from '../types';
import {BinaryTree, BinaryTreeNode, FamilyPosition, LoopType,} from './binary-tree';
import {IBinaryTree, IBinaryTreeNode} from '../interfaces';
export enum CP {lt = -1, eq = 0, gt = 1}
export class BSTNode<T> extends BinaryTreeNode<T> implements IBinaryTreeNode<T> {
override _createNode(id: BinaryTreeNodeId, val: T | null, count?: number): BSTNode<T> | null {
return val !== null ? new BSTNode<T>(id, val, count) : null;
}
export class BSTNode<T, FAMILY extends BSTNode<T, FAMILY> = RecursiveBSTNode<T>> extends BinaryTreeNode<T, FAMILY> implements IBinaryTreeNode<T, FAMILY> {
}
export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
export class BST<N extends BSTNode<N['val'], N> = BSTNode<number>> extends BinaryTree<N> implements IBinaryTree<N> {
/**
* The constructor function accepts an optional options object and sets the comparator property if provided.
* @param [options] - An optional object that can contain the following properties:
@ -35,8 +39,9 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
}
}
override _createNode(id: BinaryTreeNodeId, val: T | null, count?: number): BSTNode<T> | null {
return val !== null ? new BSTNode<T>(id, val, count) : null;
override _createNode(id: BinaryTreeNodeId, val: N['val'] | null, count?: number): N | null {
const node = val !== null ? new BSTNode<N['val'], N>(id, val, count) : null;
return node as N;
}
/**
@ -44,15 +49,15 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
* the ID matches, and returns the inserted node.
* @param {BinaryTreeNodeId} id - The `id` parameter represents the identifier of the binary tree node. It is used to
* determine the position of the node in the binary search tree.
* @param {T | null} val - The `val` parameter represents the value to be stored in the binary search tree node. It can
* be of type `T` (the generic type) or `null`.
* @param {N | null} val - The `val` parameter represents the value to be stored in the binary search tree node. It can
* be of type `N` (the generic type) or `null`.
* @param {number} [count=1] - The `count` parameter represents the number of times the value should be inserted into
* the binary search tree. By default, it is set to 1, meaning that if no count is specified, the value will be
* inserted once.
* @returns The method `add` returns a `BSTNode<T>` object or `null`.
* @returns The method `add` returns a `N` object or `null`.
*/
override add(id: BinaryTreeNodeId, val: T | null, count: number = 1): BSTNode<T> | null {
let inserted: BSTNode<T> | null = null;
override add(id: BinaryTreeNodeId, val: N['val'] | null, count: number = 1): N | null {
let inserted: N | null = null;
const newNode = this._createNode(id, val, count);
if (this.root === null) {
this._setRoot(newNode);
@ -118,14 +123,14 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
/**
* The `get` function returns the first node in a binary search tree that matches the given property value or name.
* @param {BinaryTreeNodeId | T} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `T`. It represents the value of the property that you want to search for in the binary search tree.
* @param {BinaryTreeNodeId | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `N`. It represents the value of the property that you want to search for in the binary search tree.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property name to use for searching the binary search tree nodes. If not provided, it defaults to
* `'id'`.
* @returns The method is returning a BSTNode<T> object or null.
* @returns The method is returning a N object or null.
*/
override get(nodeProperty: BinaryTreeNodeId | T, propertyName ?: BinaryTreeNodePropertyName): BSTNode<T> | null {
override get(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName): N | null {
propertyName = propertyName ?? 'id';
return this.getNodes(nodeProperty, propertyName, true)[0] ?? null;
}
@ -152,17 +157,17 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
* @param {boolean} [ignoreCount] - A boolean flag indicating whether to ignore the count of the node being removed. If
* set to true, the count of the node will not be considered and the node will be removed regardless of its count. If
* set to false or not provided, the count of the node will be taken into account and the
* @returns an array of `BSTDeletedResult<T>` objects.
* @returns an array of `BSTDeletedResult<N>` objects.
*/
override remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BSTDeletedResult<T>[] {
const bstDeletedResult: BSTDeletedResult<T>[] = [];
override remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BSTDeletedResult<N>[] {
const bstDeletedResult: BSTDeletedResult<N>[] = [];
if (!this.root) return bstDeletedResult;
const curr: BSTNode<T> | null = this.get(id);
const curr: N | null = this.get(id);
if (!curr) return bstDeletedResult;
const parent: BSTNode<T> | null = curr?.parent ? curr.parent : null;
let needBalanced: BSTNode<T> | null = null, orgCurrent = curr;
const parent: N | null = curr?.parent ? curr.parent : null;
let needBalanced: N | null = null, orgCurrent = curr;
if (curr.count > 1 && !ignoreCount) {
curr.count--;
@ -205,23 +210,23 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
/**
* The function `getNodes` returns an array of binary search tree nodes that match a given property value, with the
* option to specify the property name and whether to return only one node.
* @param {BinaryTreeNodeId | T} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `T`. It represents the property value that you want to search for in the binary search tree.
* @param {BinaryTreeNodeId | N} nodeProperty - The `nodeProperty` parameter can be either a `BinaryTreeNodeId` or a
* generic type `N`. It represents the property value that you want to search for in the binary search tree.
* @param {BinaryTreeNodePropertyName} [propertyName] - The `propertyName` parameter is an optional parameter that
* specifies the property of the nodes to compare with the `nodeProperty` parameter. If not provided, it defaults to
* `'id'`.
* @param {boolean} [onlyOne] - A boolean value indicating whether to return only one node that matches the given
* nodeProperty. If set to true, the function will stop traversing the tree and return the first matching node. If set
* to false or not provided, the function will return all nodes that match the given nodeProperty.
* @returns an array of BSTNode<T> objects.
* @returns an array of N objects.
*/
override getNodes(nodeProperty: BinaryTreeNodeId | T, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean): BSTNode<T>[] {
override getNodes(nodeProperty: BinaryTreeNodeId | N, propertyName ?: BinaryTreeNodePropertyName, onlyOne ?: boolean): N[] {
propertyName = propertyName ?? 'id';
if (!this.root) return [];
const result: BSTNode<T>[] = [];
const result: N[] = [];
if (this.loopType === LoopType.recursive) {
const _traverse = (cur: BSTNode<T>) => {
const _traverse = (cur: N) => {
if (this._pushByPropertyNameStopOrNot(cur, result, nodeProperty, propertyName, onlyOne)) return;
if (!cur.left && !cur.right) return;
@ -236,7 +241,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
_traverse(this.root);
} else {
const queue: BSTNode<T>[] = [this.root];
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
@ -270,7 +275,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
propertyName = propertyName ?? 'id';
if (!this.root) return 0;
const getSumByPropertyName = (cur: BSTNode<T>) => {
const getSumByPropertyName = (cur: N) => {
let needSum: number;
switch (propertyName) {
case 'id':
@ -289,7 +294,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
let sum = 0;
if (this.loopType === LoopType.recursive) {
const _traverse = (cur: BSTNode<T>): void => {
const _traverse = (cur: N): void => {
const compared = this._compare(cur.id, id);
if (compared === CP.eq) {
if (cur.right) sum += this.subTreeSum(cur.right, propertyName);
@ -307,7 +312,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
_traverse(this.root);
} else {
const queue: BSTNode<T>[] = [this.root];
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
@ -334,7 +339,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
/**
* The function `allGreaterNodesAdd` updates the value of a specified property for all nodes in a binary search tree
* that have a greater value than a given node.
* @param node - The `node` parameter is of type `BSTNode<T>`, which represents a node in a binary search tree. It
* @param node - The `node` parameter is of type `N`, which represents a node in a binary search tree. It
* contains properties such as `id` and `count`.
* @param {number} delta - The `delta` parameter is a number that represents the amount by which the property value of
* each node should be increased.
@ -343,11 +348,11 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
* defaults to 'id'.
* @returns a boolean value.
*/
allGreaterNodesAdd(node: BSTNode<T>, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
allGreaterNodesAdd(node: N, delta: number, propertyName ?: BinaryTreeNodePropertyName): boolean {
propertyName = propertyName ?? 'id';
if (!this.root) return false;
const _sumByPropertyName = (cur: BSTNode<T>) => {
const _sumByPropertyName = (cur: N) => {
switch (propertyName) {
case 'id':
cur.id += delta;
@ -362,7 +367,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
}
if (this.loopType === LoopType.recursive) {
const _traverse = (cur: BSTNode<T>) => {
const _traverse = (cur: N) => {
const compared = this._compare(cur.id, node.id);
_sumByPropertyName(cur);
@ -374,7 +379,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
_traverse(this.root);
return true;
} else {
const queue: BSTNode<T>[] = [this.root];
const queue: N[] = [this.root];
while (queue.length > 0) {
const cur = queue.shift();
if (cur) {
@ -441,7 +446,7 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
let balanced = true;
if (this.loopType === LoopType.recursive) {
const _height = (cur: BSTNode<T> | null | undefined): number => {
const _height = (cur: N | null | undefined): number => {
if (!cur) return 0;
const leftHeight = _height(cur.left), rightHeight = _height(cur.right);
if (Math.abs(leftHeight - rightHeight) > 1) balanced = false;
@ -449,9 +454,9 @@ export class BST<T> extends BinaryTree<T> implements IBinaryTree<T> {
};
_height(this.root);
} else {
const stack: BSTNode<T>[] = [];
let node: BSTNode<T> | null | undefined = this.root, last: BSTNode<T> | null = null;
const depths: Map<BSTNode<T>, number> = new Map();
const stack: N[] = [];
let node: N | null | undefined = this.root, last: N | null = null;
const depths: Map<N, number> = new Map();
while (stack.length > 0 || node) {
if (node) {

View file

@ -3,7 +3,7 @@ import {IBinaryTree, IBinaryTreeNode} from '../interfaces';
enum RBColor { Red, Black }
class RBNode<T> extends BinaryTreeNode<T> implements IBinaryTreeNode<T> {
class RBNode<T, FAMILY extends RBNode<T, FAMILY>> extends BinaryTreeNode<T, FAMILY> implements IBinaryTreeNode<T, FAMILY> {
// override createNode(id: BinaryTreeNodeId, val: T | null, count?: number): RBNode<T> | null {
// return val !== null ? new RBNode<T>(id, val, count) : null;
// }
@ -58,7 +58,7 @@ class RBNode<T> extends BinaryTreeNode<T> implements IBinaryTreeNode<T> {
// }
}
class RBTree<T> extends BinaryTree<T> implements IBinaryTree<T> {
class RBTree<N extends RBNode<N['val'], N>> extends BinaryTree<N> implements IBinaryTree<N> {
constructor(options?: {
loopType?: LoopType,
autoIncrementId?: boolean,
@ -67,43 +67,43 @@ class RBTree<T> extends BinaryTree<T> implements IBinaryTree<T> {
super(options);
}
// override _createNode(id: BinaryTreeNodeId, val: T | null, count?: number): RBNode<T> | null {
// return val !== null ? new RBNode<T>(id, val, count) : null;
// override _createNode(id: BinaryTreeNodeId, val: N | null, count?: number): RBNode<N> | null {
// return val !== null ? new RBNode<N>(id, val, count) : null;
// }
// private override _root: BinaryTreeNode<T> | null = null;
// private override _root: BinaryTreeNode<N> | null = null;
//
// override get root(): BinaryTreeNode<T> | null {
// override get root(): BinaryTreeNode<N> | null {
// return this._root;
// }
insert(id: number, val: T | null) {
insert(id: number, val: N | null) {
}
private leftRotate(node: RBNode<T>) {
private leftRotate(node: N) {
}
private rightRotate(node: RBNode<T>) {
private rightRotate(node: N) {
}
private insertFixup(node: RBNode<T>) {
private insertFixup(node: N) {
}
private deleteFixup(node: RBNode<T>) {
private deleteFixup(node: N) {
}
private transplant(u: RBNode<T>, v: RBNode<T>) {
private transplant(u: N, v: N) {
}
// override remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeleted<T>[] {
// override remove(id: BinaryTreeNodeId, ignoreCount?: boolean): BinaryTreeDeleted<N>[] {
//
// return [{deleted: new RBNode<T>(0, 0), needBalanced: null}];
// return [{deleted: new N(0, 0), needBalanced: null}];
// }

View file

@ -9,30 +9,31 @@ import {BST, BSTNode} from './bst';
import type {BinaryTreeNodeId, TreeMultiSetDeletedResult} from '../types';
import {IBinaryTree} from '../interfaces';
export class TreeMultiSet<T> extends BST<T> implements IBinaryTree<T> {
export class TreeMultiSet<N extends BSTNode<N['val'], N> = BSTNode<number>> extends BST<N> implements IBinaryTree<N> {
/**
* The function creates a new BSTNode with the given id, value, and count.
* @param {BinaryTreeNodeId} id - The id parameter is the unique identifier for the binary tree node. It is used to
* distinguish one node from another in the tree.
* @param {T} val - The `val` parameter represents the value that will be stored in the binary search tree node.
* @param {N} val - The `val` 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 id, value, and count (if provided).
*/
override _createNode(id: BinaryTreeNodeId, val: T, count?: number): BSTNode<T> {
return new BSTNode<T>(id, val, count);
override _createNode(id: BinaryTreeNodeId, val: N['val'], count?: number): N {
const node = new BSTNode<N['val'], N>(id, val, count);
return node as N;
}
/**
* The function overrides the add method of the BinarySearchTree class in TypeScript.
* @param {BinaryTreeNodeId} id - The `id` parameter is the identifier of the binary tree node that you want to add.
* @param {T | null} val - The `val` parameter represents the value that you want to add to the binary search tree. It
* can be of type `T` (the generic type) or `null`.
* @param {N | null} val - The `val` parameter represents the value that you want to add to the binary search tree. It
* can be of type `N` (the generic type) or `null`.
* @param {number} [count] - The `count` parameter is an optional parameter of type `number`. It represents the number
* of times the value should be added to the binary search tree. If not provided, the default value is `undefined`.
* @returns The `add` method is returning a `BSTNode<T>` object or `null`.
* @returns The `add` method is returning a `BSTNode<N>` object or `null`.
*/
override add(id: BinaryTreeNodeId, val: T | null, count?: number): BSTNode<T> | null {
override add(id: BinaryTreeNodeId, val: N | null, count?: number): N | null {
return super.add(id, val, count);
}
@ -46,7 +47,7 @@ export class TreeMultiSet<T> extends BST<T> implements IBinaryTree<T> {
* set to `true`, the left sum of all nodes will be recalculated. If it
* @returns The method is returning an array of TreeMultiSetDeletedResult objects.
*/
override remove(id: BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): TreeMultiSetDeletedResult<T>[] {
override remove(id: BinaryTreeNodeId, isUpdateAllLeftSum?: boolean): TreeMultiSetDeletedResult<N>[] {
return super.remove(id, isUpdateAllLeftSum);
}
}

View file

@ -10,9 +10,9 @@ import {PriorityQueue} from '../priority-queue';
import type {DijkstraResult, VertexId} from '../types';
import {IGraph} from '../interfaces';
export abstract class AbstractVertex<V = number> {
export abstract class AbstractVertex<T = number> {
protected constructor(id: VertexId, val?: V) {
protected constructor(id: VertexId, val?: T) {
this._id = id;
this._val = val;
}
@ -27,13 +27,13 @@ export abstract class AbstractVertex<V = number> {
this._id = v;
}
private _val: V | undefined;
private _val: T | undefined;
get val(): V | undefined {
get val(): T | undefined {
return this._val;
}
set val(value: V | undefined) {
set val(value: T | undefined) {
this._val = value;
}
@ -43,24 +43,24 @@ export abstract class AbstractVertex<V = number> {
// * @param id
// * @param val
// */
// abstract _createVertex(id: VertexId, val?: V): AbstractVertex<V>;
// abstract _createVertex(id: VertexId, val?: T): AbstractVertex<T>;
}
export abstract class AbstractEdge<E> {
export abstract class AbstractEdge<T = number> {
protected constructor(weight?: number, val?: E) {
protected constructor(weight?: number, val?: T) {
this._weight = weight !== undefined ? weight : 1;
this._val = val;
this._hashCode = uuidV4();
}
private _val: E | undefined;
private _val: T | undefined;
get val(): E | undefined {
get val(): T | undefined {
return this._val;
}
set val(value: E | undefined) {
set val(value: T | undefined) {
this._val = value;
}
@ -88,7 +88,7 @@ export abstract class AbstractEdge<E> {
// * @param weight
// * @param val
// */
// abstract _createEdge(srcOrV1: VertexId | string, destOrV2: VertexId | string, weight?: number, val?: E): AbstractEdge<E>;
// abstract _createEdge(srcOrV1: VertexId | string, destOrV2: VertexId | string, weight?: number, val?: E): E;
protected _setHashCode(v: string) {
this._hashCode = v;
@ -96,10 +96,10 @@ export abstract class AbstractEdge<E> {
}
// Connected Component === Largest Connected Sub-Graph
export abstract class AbstractGraph<V = number, E = number> implements IGraph<V, E> {
private _vertices: Map<VertexId, AbstractVertex<V>> = new Map<VertexId, AbstractVertex<V>>();
export abstract class AbstractGraph<V extends AbstractVertex<any>, E extends AbstractEdge<any>> implements IGraph<V, E> {
private _vertices: Map<VertexId, V> = new Map<VertexId, V>();
get vertices(): Map<VertexId, AbstractVertex<V>> {
get vertices(): Map<VertexId, V> {
return this._vertices;
}
@ -109,7 +109,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @param id
* @param val
*/
abstract _createVertex(id: VertexId, val?: V): AbstractVertex<V>;
abstract _createVertex(id: VertexId, val?: V): V;
/**
* 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.
@ -119,22 +119,22 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @param weight
* @param val
*/
abstract _createEdge(srcOrV1: VertexId | string, destOrV2: VertexId | string, weight?: number, val?: E): AbstractEdge<E>;
abstract _createEdge(srcOrV1: VertexId | string, destOrV2: VertexId | string, weight?: number, val?: E): E;
abstract removeEdgeBetween(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
abstract removeEdgeBetween(srcOrId: V | VertexId, destOrId: V | VertexId): E | null;
abstract removeEdge(edge: AbstractEdge<E>): AbstractEdge<E> | null;
abstract removeEdge(edge: E): E | null;
_getVertex(vertexOrId: VertexId | AbstractVertex<V>): AbstractVertex<V> | null {
_getVertex(vertexOrId: VertexId | V): V | null {
const vertexId = this._getVertexId(vertexOrId);
return this._vertices.get(vertexId) || null;
}
getVertex(vertexId: VertexId): AbstractVertex<V> | null {
getVertex(vertexId: VertexId): V | null {
return this._vertices.get(vertexId) || null;
}
_getVertexId(vertexOrId: AbstractVertex<V> | VertexId): VertexId {
_getVertexId(vertexOrId: V | VertexId): VertexId {
return vertexOrId instanceof AbstractVertex ? vertexOrId.id : vertexOrId;
}
@ -144,20 +144,21 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* (`VertexId`).
* @returns The method `hasVertex` returns a boolean value.
*/
hasVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean {
hasVertex(vertexOrId: V | VertexId): boolean {
return this._vertices.has(this._getVertexId(vertexOrId));
}
abstract getEdge(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
abstract getEdge(srcOrId: V | VertexId, destOrId: V | VertexId): E | null;
createAddVertex(id: VertexId, val?: V): boolean {
createAddVertex(id: VertexId, val?: V['val']): boolean {
const newVertex = this._createVertex(id, val);
return this.addVertex(newVertex);
}
addVertex(newVertex: AbstractVertex<V>): boolean {
addVertex(newVertex: V): boolean {
if (this.hasVertex(newVertex)) {
throw (new Error('Duplicated vertex id is not allowed'));
return false;
// throw (new Error('Duplicated vertex id is not allowed'));
}
this._vertices.set(newVertex.id, newVertex);
return true;
@ -169,7 +170,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* (`VertexId`).
* @returns The method `removeVertex` returns a boolean value.
*/
removeVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean {
removeVertex(vertexOrId: V | VertexId): boolean {
const vertexId = this._getVertexId(vertexOrId);
return this._vertices.delete(vertexId);
}
@ -181,7 +182,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @returns a boolean value. It returns true if at least one vertex was successfully removed, and false if no vertices
* were removed.
*/
removeAllVertices(vertices: AbstractVertex<V>[] | VertexId[]): boolean {
removeAllVertices(vertices: V[] | VertexId[]): boolean {
const removed: boolean[] = [];
for (const v of vertices) {
removed.push(this.removeVertex(v));
@ -189,11 +190,11 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
return removed.length > 0;
}
abstract degreeOf(vertexOrId: AbstractVertex<V> | VertexId): number;
abstract degreeOf(vertexOrId: V | VertexId): number;
abstract edgeSet(): AbstractEdge<E>[];
abstract edgeSet(): E[];
abstract edgesOf(vertexOrId: AbstractVertex<V> | VertexId): AbstractEdge<E>[];
abstract edgesOf(vertexOrId: V | VertexId): E[];
/**
* The function checks if there is an edge between two vertices in a graph.
@ -204,19 +205,19 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @returns The function `hasEdge` returns a boolean value. It returns `true` if there is an edge between the
* vertices `v1` and `v2`, and `false` otherwise.
*/
hasEdge(v1: VertexId | AbstractVertex<V>, v2: VertexId | AbstractVertex<V>): boolean {
hasEdge(v1: VertexId | V, v2: VertexId | V): boolean {
const edge = this.getEdge(v1, v2);
return !!edge;
}
createAddEdge(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId, weight: number, val: E): boolean {
createAddEdge(src: V | VertexId, dest: V | VertexId, weight: number, val: E['val']): boolean {
if (src instanceof AbstractVertex) src = src.id;
if (dest instanceof AbstractVertex) dest = dest.id;
const newEdge = this._createEdge(src, dest, weight, val);
return this.addEdge(newEdge);
}
abstract addEdge(edge: AbstractEdge<E>): boolean;
abstract addEdge(edge: E): boolean;
/**
* The function sets the weight of an edge between two vertices in a graph.
@ -229,7 +230,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @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(srcOrId: VertexId | AbstractVertex<V>, destOrId: VertexId | AbstractVertex<V>, weight: number): boolean {
setEdgeWeight(srcOrId: VertexId | V, destOrId: VertexId | V, weight: number): boolean {
const edge = this.getEdge(srcOrId, destOrId);
if (edge) {
edge.weight = weight;
@ -239,7 +240,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
}
}
abstract getNeighbors(vertexOrId: AbstractVertex<V> | VertexId): AbstractVertex<V>[];
abstract getNeighbors(vertexOrId: V | VertexId): V[];
/**
* The function `getAllPathsBetween` finds all paths between two vertices in a graph using depth-first search.
@ -250,15 +251,15 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @returns an array of arrays of vertices (V[][]). Each inner array represents a path between the given vertices (v1
* and v2).
*/
getAllPathsBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId): AbstractVertex<V>[][] {
const paths: AbstractVertex<V>[][] = [];
getAllPathsBetween(v1: V | VertexId, v2: V | VertexId): V[][] {
const paths: V[][] = [];
const vertex1 = this._getVertex(v1);
const vertex2 = this._getVertex(v2);
if (!(vertex1 && vertex2)) {
return [];
}
const dfs = (cur: AbstractVertex<V>, dest: AbstractVertex<V>, visiting: Map<AbstractVertex<V>, boolean>, path: AbstractVertex<V>[]) => {
const dfs = (cur: V, dest: V, visiting: Map<V, boolean>, path: V[]) => {
visiting.set(cur, true);
if (cur === dest) {
@ -270,14 +271,14 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
if (!visiting.get(neighbor)) {
path.push(neighbor);
dfs(neighbor, dest, visiting, path);
arrayRemove(path, (vertex: AbstractVertex<V>) => vertex === neighbor);
arrayRemove(path, (vertex: V) => vertex === neighbor);
}
}
visiting.set(cur, false);
};
dfs(vertex1, vertex2, new Map<AbstractVertex<V>, boolean>(), []);
dfs(vertex1, vertex2, new Map<V, boolean>(), []);
return paths;
}
@ -286,7 +287,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @param {V[]} path - An array of vertices (V) 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: AbstractVertex<V>[]): number {
getPathSumWeight(path: V[]): number {
let sum = 0;
for (let i = 0; i < path.length; i++) {
sum += this.getEdge(path[i], path[i + 1])?.weight || 0;
@ -308,7 +309,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* If `isWeight` is `false` or not provided, it calculates the minimum number of edges between the vertices. If the
* vertices are not
*/
getMinCostBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId, isWeight?: boolean): number | null {
getMinCostBetween(v1: V | VertexId, v2: V | VertexId, isWeight?: boolean): number | null {
if (isWeight === undefined) isWeight = false;
if (isWeight) {
@ -326,8 +327,8 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
return null;
}
const visited: Map<AbstractVertex<V>, boolean> = new Map();
const queue: AbstractVertex<V>[] = [vertex1];
const visited: Map<V, boolean> = new Map();
const queue: V[] = [vertex1];
visited.set(vertex1, true);
let cost = 0;
while (queue.length > 0) {
@ -365,7 +366,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* @returns The function `getMinPathBetween` returns an array of vertices (`V[]`) representing the minimum path between
* two vertices (`v1` and `v2`). If no path is found, it returns `null`.
*/
getMinPathBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId, isWeight?: boolean): AbstractVertex<V>[] | null {
getMinPathBetween(v1: V | VertexId, v2: V | VertexId, isWeight?: boolean): V[] | null {
if (isWeight === undefined) isWeight = false;
if (isWeight) {
@ -384,14 +385,14 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
return allPaths[minIndex] || null;
} else {
// BFS
let minPath: AbstractVertex<V>[] = [];
let minPath: V[] = [];
const vertex1 = this._getVertex(v1);
const vertex2 = this._getVertex(v2);
if (!(vertex1 && vertex2)) {
return [];
}
const dfs = (cur: AbstractVertex<V>, dest: AbstractVertex<V>, visiting: Map<AbstractVertex<V>, boolean>, path: AbstractVertex<V>[]) => {
const dfs = (cur: V, dest: V, visiting: Map<V, boolean>, path: V[]) => {
visiting.set(cur, true);
if (cur === dest) {
@ -404,14 +405,14 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
if (!visiting.get(neighbor)) {
path.push(neighbor);
dfs(neighbor, dest, visiting, path);
arrayRemove(path, (vertex: AbstractVertex<V>) => vertex === neighbor);
arrayRemove(path, (vertex: V) => vertex === neighbor);
}
}
visiting.set(cur, false);
};
dfs(vertex1, vertex2, new Map<AbstractVertex<V>, boolean>(), []);
dfs(vertex1, vertex2, new Map<V, boolean>(), []);
return minPath;
}
}
@ -433,20 +434,20 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* 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>`.
*/
dijkstraWithoutHeap(src: AbstractVertex<V> | VertexId, dest?: AbstractVertex<V> | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<AbstractVertex<V>> {
dijkstraWithoutHeap(src: V | VertexId, dest?: V | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<V> {
if (getMinDist === undefined) getMinDist = false;
if (genPaths === undefined) genPaths = false;
if (dest === undefined) dest = null;
let minDist = Infinity;
let minDest: AbstractVertex<V> | null = null;
let minPath: AbstractVertex<V>[] = [];
const paths: AbstractVertex<V>[][] = [];
let minDest: V | null = null;
let minPath: V[] = [];
const paths: V[][] = [];
const vertices = this._vertices;
const distMap: Map<AbstractVertex<V>, number> = new Map();
const seen: Set<AbstractVertex<V>> = new Set();
const preMap: Map<AbstractVertex<V>, AbstractVertex<V> | null> = new Map(); // predecessor
const distMap: Map<V, number> = new Map();
const seen: Set<V> = new Set();
const preMap: Map<V, V | null> = new Map(); // predecessor
const srcVertex = this._getVertex(src);
const destVertex = dest ? this._getVertex(dest) : null;
@ -464,7 +465,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
const getMinOfNoSeen = () => {
let min = Infinity;
let minV: AbstractVertex<V> | null = null;
let minV: V | null = null;
for (const [key, val] of distMap) {
if (!seen.has(key)) {
if (val < min) {
@ -476,12 +477,12 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
return minV;
};
const getPaths = (minV: AbstractVertex<V> | null) => {
const getPaths = (minV: V | null) => {
for (const vertex of vertices) {
const vertexOrId = vertex[1];
if (vertexOrId instanceof AbstractVertex) {
const path: AbstractVertex<V>[] = [vertexOrId];
const path: V[] = [vertexOrId];
let parent = preMap.get(vertexOrId);
while (parent) {
path.push(parent);
@ -560,19 +561,19 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* 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>`.
*/
dijkstra(src: AbstractVertex<V> | VertexId, dest?: AbstractVertex<V> | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<AbstractVertex<V>> {
dijkstra(src: V | VertexId, dest?: V | VertexId | null, getMinDist?: boolean, genPaths?: boolean): DijkstraResult<V> {
if (getMinDist === undefined) getMinDist = false;
if (genPaths === undefined) genPaths = false;
if (dest === undefined) dest = null;
let minDist = Infinity;
let minDest: AbstractVertex<V> | null = null;
let minPath: AbstractVertex<V>[] = [];
const paths: AbstractVertex<V>[][] = [];
let minDest: V | null = null;
let minPath: V[] = [];
const paths: V[][] = [];
const vertices = this._vertices;
const distMap: Map<AbstractVertex<V>, number> = new Map();
const seen: Set<AbstractVertex<V>> = new Set();
const preMap: Map<AbstractVertex<V>, AbstractVertex<V> | null> = new Map(); // predecessor
const distMap: Map<V, number> = new Map();
const seen: Set<V> = new Set();
const preMap: Map<V, V | null> = new Map(); // predecessor
const srcVertex = this._getVertex(src);
const destVertex = dest ? this._getVertex(dest) : null;
@ -586,17 +587,17 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
if (vertexOrId instanceof AbstractVertex) distMap.set(vertexOrId, Infinity);
}
const heap = new PriorityQueue<{ id: number, val: AbstractVertex<V> }>({comparator: (a, b) => a.id - b.id});
const heap = new PriorityQueue<{ id: number, val: V }>({comparator: (a, b) => a.id - b.id});
heap.add({id: 0, val: srcVertex});
distMap.set(srcVertex, 0);
preMap.set(srcVertex, null);
const getPaths = (minV: AbstractVertex<V> | null) => {
const getPaths = (minV: V | null) => {
for (const vertex of vertices) {
const vertexOrId = vertex[1];
if (vertexOrId instanceof AbstractVertex) {
const path: AbstractVertex<V>[] = [vertexOrId];
const path: V[] = [vertexOrId];
let parent = preMap.get(vertexOrId);
while (parent) {
path.push(parent);
@ -678,7 +679,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* 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.
*/
abstract getEndsOfEdge(edge: AbstractEdge<E>): [AbstractVertex<V>, AbstractVertex<V>] | null;
abstract getEndsOfEdge(edge: E): [V, V] | null;
/**
* BellmanFord time:O(VE) space:O(V)
@ -696,16 +697,16 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* vertex.
* @returns The function `bellmanFord` returns an object with the following properties:
*/
bellmanFord(src: AbstractVertex<V> | VertexId, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
bellmanFord(src: V | VertexId, scanNegativeCycle?: boolean, getMin?: boolean, genPath?: boolean) {
if (getMin === undefined) getMin = false;
if (genPath === undefined) genPath = false;
const srcVertex = this._getVertex(src);
const paths: AbstractVertex<V>[][] = [];
const distMap: Map<AbstractVertex<V>, number> = new Map();
const preMap: Map<AbstractVertex<V>, AbstractVertex<V>> = new Map(); // predecessor
const paths: V[][] = [];
const distMap: Map<V, number> = new Map();
const preMap: Map<V, V> = new Map(); // predecessor
let min = Infinity;
let minPath: AbstractVertex<V>[] = [];
let minPath: V[] = [];
// TODO
let hasNegativeCycle: boolean | undefined;
if (scanNegativeCycle) hasNegativeCycle = false;
@ -740,7 +741,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
}
}
let minDest: AbstractVertex<V> | null = null;
let minDest: V | null = null;
if (getMin) {
distMap.forEach((d, v) => {
if (v !== srcVertex) {
@ -756,7 +757,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
for (const vertex of vertices) {
const vertexOrId = vertex[1];
if (vertexOrId instanceof AbstractVertex) {
const path: AbstractVertex<V>[] = [vertexOrId];
const path: V[] = [vertexOrId];
let parent = preMap.get(vertexOrId);
while (parent !== undefined) {
path.push(parent);
@ -802,12 +803,12 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
* `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: (AbstractVertex<V> | null)[][] } {
floyd(): { costs: number[][], predecessor: (V | null)[][] } {
const idAndVertices = [...this._vertices];
const n = idAndVertices.length;
const costs: number[][] = [];
const predecessor: (AbstractVertex<V> | null)[][] = [];
const predecessor: (V | null)[][] = [];
// successors
for (let i = 0; i < n; i++) {
@ -876,8 +877,8 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
if (needSCCs === undefined) needSCCs = defaultConfig;
if (needCycles === undefined) needCycles = defaultConfig;
const dfnMap: Map<AbstractVertex<V>, number> = new Map();
const lowMap: Map<AbstractVertex<V>, number> = new Map();
const dfnMap: Map<V, number> = new Map();
const lowMap: Map<V, number> = new Map();
const vertices = this._vertices;
vertices.forEach(v => {
dfnMap.set(v, -1);
@ -886,10 +887,10 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
const [root] = vertices.values();
const articulationPoints: AbstractVertex<V>[] = [];
const bridges: AbstractEdge<E>[] = [];
const articulationPoints: V[] = [];
const bridges: E[] = [];
let dfn = 0;
const dfs = (cur: AbstractVertex<V>, parent: AbstractVertex<V> | null) => {
const dfs = (cur: V, parent: V | null) => {
dfn++;
dfnMap.set(cur, dfn);
lowMap.set(cur, dfn);
@ -933,10 +934,10 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
dfs(root, null);
let SCCs: Map<number, AbstractVertex<V>[]> = new Map();
let SCCs: Map<number, V[]> = new Map();
const getSCCs = () => {
const SCCs: Map<number, AbstractVertex<V>[]> = new Map();
const SCCs: Map<number, V[]> = new Map();
lowMap.forEach((low, vertex) => {
if (!SCCs.has(low)) {
SCCs.set(low, [vertex]);
@ -951,9 +952,9 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
SCCs = getSCCs();
}
const cycles: Map<number, AbstractVertex<V>[]> = new Map();
const cycles: Map<number, V[]> = new Map();
if (needCycles) {
let SCCs: Map<number, AbstractVertex<V>[]> = new Map();
let SCCs: Map<number, V[]> = new Map();
if (SCCs.size < 1) {
SCCs = getSCCs();
}
@ -971,7 +972,7 @@ export abstract class AbstractGraph<V = number, E = number> implements IGraph<V,
/**--- start find cycles --- */
protected _setVertices(value: Map<VertexId, AbstractVertex<V>>) {
protected _setVertices(value: Map<VertexId, V>) {
this._vertices = value;
}

View file

@ -10,24 +10,24 @@ import {AbstractEdge, AbstractGraph, AbstractVertex} from './abstract-graph';
import type {TopologicalStatus, VertexId} from '../types';
import {IDirectedGraph} from '../interfaces';
export class DirectedVertex<V = number> extends AbstractVertex<V> {
export class DirectedVertex<T = number> extends AbstractVertex<T> {
/**
* The constructor function initializes a vertex with an optional value.
* @param {VertexId} id - The `id` parameter is the identifier for the vertex. It is of type `VertexId`, which is
* typically a unique identifier for each vertex in a graph.
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to specify the value
* @param {T} [val] - The "val" parameter is an optional parameter of type T. It is used to specify the value
* associated with the vertex.
*/
constructor(id: VertexId, val?: V) {
constructor(id: VertexId, val?: T) {
super(id, val);
}
// _createVertex(id: VertexId, val?: V): DirectedVertex<V> {
// return new DirectedVertex<V>(id, val);
// _createVertex(id: VertexId, val?: T): DirectedVertex<T> {
// return new DirectedVertex<T>(id, val);
// }
}
export class DirectedEdge<E = number> extends AbstractEdge<E> {
export class DirectedEdge<T = number> extends AbstractEdge<T> {
/**
* The constructor function initializes the source and destination vertices of an edge, along with an optional weight
@ -38,10 +38,10 @@ export class DirectedEdge<E = number> extends AbstractEdge<E> {
* @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 will default to `undefined`.
* @param {E} [val] - The "val" parameter is an optional parameter of type E. It represents the value associated with
* @param {T} [val] - The "val" parameter is an optional parameter of type T. It represents the value associated with
* the edge.
*/
constructor(src: VertexId, dest: VertexId, weight?: number, val?: E) {
constructor(src: VertexId, dest: VertexId, weight?: number, val?: T) {
super(weight, val);
this._src = src;
this._dest = dest;
@ -67,28 +67,32 @@ export class DirectedEdge<E = number> extends AbstractEdge<E> {
this._dest = v;
}
// _createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): DirectedEdge<E> {
// _createEdge(src: VertexId, dest: VertexId, weight?: number, val?: T): DirectedEdge<T> {
// if (weight === undefined || weight === null) weight = 1;
// return new DirectedEdge(src, dest, weight, val);
// }
}
// Strongly connected, One direction connected, Weakly connected
export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> implements IDirectedGraph<V, E> {
export class DirectedGraph<V extends DirectedVertex<any>, E extends DirectedEdge<any>> extends AbstractGraph<V, E> implements IDirectedGraph<V, E> {
private readonly _vertexConstructor: new (id: VertexId, val?: V['val']) => V;
private readonly _edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E;
constructor() {
constructor(vertexConstructor: new (id: VertexId, val?: V['val']) => V, edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E) {
super();
this._vertexConstructor = vertexConstructor;
this._edgeConstructor = edgeConstructor;
}
private _outEdgeMap: Map<DirectedVertex<V>, DirectedEdge<E>[]> = new Map<DirectedVertex<V>, DirectedEdge<E>[]>();
private _outEdgeMap: Map<V, E[]> = new Map<V, E[]>();
get outEdgeMap(): Map<DirectedVertex<V>, DirectedEdge<E>[]> {
get outEdgeMap(): Map<V, E[]> {
return this._outEdgeMap;
}
private _inEdgeMap: Map<DirectedVertex<V>, DirectedEdge<E>[]> = new Map<DirectedVertex<V>, DirectedEdge<E>[]>();
private _inEdgeMap: Map<V, E[]> = new Map<V, E[]>();
get inEdgeMap(): Map<DirectedVertex<V>, DirectedEdge<E>[]> {
get inEdgeMap(): Map<V, E[]> {
return this._inEdgeMap;
}
@ -98,8 +102,8 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
* @param id
* @param val
*/
_createVertex(id: VertexId, val?: V): DirectedVertex<V> {
return new DirectedVertex<V>(id, val);
_createVertex(id: VertexId, val?: V['val']): V {
return new this._vertexConstructor(id, val);
}
/**
@ -110,25 +114,25 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
* @param weight
* @param val
*/
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): DirectedEdge<E> {
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E['val']): E {
if (weight === undefined || weight === null) weight = 1;
return new DirectedEdge(src, dest, weight, val);
return new this._edgeConstructor(src, dest, weight, val);
}
/**
* The function `getEdge` returns the directed edge between two vertices, given their source and destination.
* @param {DirectedVertex<V> | null | VertexId} srcOrId - The source vertex or its ID. It can be either a
* @param {V | null | VertexId} srcOrId - The source vertex or its ID. It can be either a
* DirectedVertex object or a VertexId.
* @param {DirectedVertex<V> | null | VertexId} destOrId - The `destOrId` parameter is the destination vertex or its
* @param {V | null | VertexId} destOrId - The `destOrId` parameter is the destination vertex or its
* ID. It can be either a `DirectedVertex` object or a `VertexId` value.
* @returns a DirectedEdge<E> object or null.
* @returns a E object or null.
*/
getEdge(srcOrId: DirectedVertex<V> | null | VertexId, destOrId: DirectedVertex<V> | null | VertexId): DirectedEdge<E> | null {
let edges: DirectedEdge<E>[] = [];
getEdge(srcOrId: V | null | VertexId, destOrId: V | null | VertexId): E | null {
let edges: E[] = [];
if (srcOrId !== null && destOrId !== null) {
const src: DirectedVertex<V> | null = this._getVertex(srcOrId);
const dest: DirectedVertex<V> | null = this._getVertex(destOrId);
const src: V | null = this._getVertex(srcOrId);
const dest: V | null = this._getVertex(destOrId);
if (src && dest) {
const srcOutEdges = this._outEdgeMap.get(src);
@ -143,12 +147,12 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The `addEdge` function adds a directed edge to a graph if the source and destination vertices exist.
* @param edge - The parameter `edge` is of type `DirectedEdge<E>`, which represents a directed edge in a graph. It
* @param edge - The parameter `edge` is of type `E`, which represents a directed edge in a graph. It
* contains two properties:
* @returns The method `addEdge` 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 of the edge is not present in the graph.
*/
addEdge(edge: DirectedEdge<E>): boolean {
addEdge(edge: E): boolean {
if (!(this.hasVertex(edge.src) && this.hasVertex(edge.dest))) {
return false;
}
@ -180,18 +184,18 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The `removeEdgeBetween` function removes an edge between two vertices in a directed graph and returns the removed
* edge, or null if the edge was not found.
* @param {DirectedVertex<V> | VertexId} srcOrId - The `srcOrId` parameter represents either a `DirectedVertex<V>`
* @param {V | VertexId} srcOrId - The `srcOrId` parameter represents either a `V`
* object or a `VertexId` value. It is used to specify the source vertex of the edge that you want to remove.
* @param {DirectedVertex<V> | VertexId} destOrId - The `destOrId` parameter represents the destination vertex of the
* edge that you want to remove. It can be either a `DirectedVertex<V>` object or a `VertexId` value.
* @returns The function `removeEdgeBetween` returns the removed edge (`DirectedEdge<E>`) if it exists, or `null` if
* @param {V | VertexId} destOrId - The `destOrId` parameter represents the destination vertex of the
* edge that you want to remove. It can be either a `V` object or a `VertexId` value.
* @returns The function `removeEdgeBetween` returns the removed edge (`E`) if it exists, or `null` if
* the edge does not exist.
*/
removeEdgeBetween(srcOrId: DirectedVertex<V> | VertexId, destOrId: DirectedVertex<V> | VertexId): DirectedEdge<E> | null {
removeEdgeBetween(srcOrId: V | VertexId, destOrId: V | VertexId): E | null {
const src: DirectedVertex<V> | null = this._getVertex(srcOrId);
const dest: DirectedVertex<V> | null = this._getVertex(destOrId);
let removed: DirectedEdge<E> | null = null;
const src: V | null = this._getVertex(srcOrId);
const dest: V | null = this._getVertex(destOrId);
let removed: E | null = null;
if (!src || !dest) {
return null;
}
@ -201,17 +205,17 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The removeEdge function removes an edge from a graph and returns the removed edge, or null if the edge was not
* found.
* @param {DirectedEdge<E>} edge - The `edge` parameter represents the edge that you want to remove from the graph. It should be an
* @param {E} edge - The `edge` parameter represents the edge that you want to remove from the graph. It should be an
* object that has `src` and `dest` properties, which represent the source and destination vertices of the edge,
* respectively.
* @returns The method `removeEdge` returns the removed edge (`DirectedEdge<E>`) if it exists, or `null` if the edge does not exist.
* @returns The method `removeEdge` returns the removed edge (`E`) if it exists, or `null` if the edge does not exist.
*/
arrayRemove<DirectedEdge<E>>(srcOutEdges, (edge: DirectedEdge<E>) => edge.dest === dest.id);
arrayRemove<E>(srcOutEdges, (edge: E) => edge.dest === dest.id);
}
const destInEdges = this._inEdgeMap.get(dest);
if (destInEdges) {
removed = arrayRemove<DirectedEdge<E>>(destInEdges, (edge: DirectedEdge<E>) => edge.src === src.id)[0] || null;
removed = arrayRemove<E>(destInEdges, (edge: E) => edge.src === src.id)[0] || null;
}
return removed;
}
@ -219,24 +223,24 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The `removeEdge` function removes a directed edge from a graph and returns the removed edge, or null if the edge was
* not found.
* @param edge - The `edge` parameter is an object of type `DirectedEdge<E>`, which represents a directed edge in a
* @param edge - The `edge` parameter is an object of type `E`, which represents a directed edge in a
* graph. It has two properties:
* @returns The function `removeEdge` returns a `DirectedEdge<E>` object if an edge is successfully removed, or `null`
* @returns The function `removeEdge` returns a `E` object if an edge is successfully removed, or `null`
* if no edge is removed.
*/
removeEdge(edge: DirectedEdge<E>): DirectedEdge<E> | null {
let removed: DirectedEdge<E> | null = null;
removeEdge(edge: E): E | null {
let removed: E | 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: DirectedEdge<E>) => edge.src === src.id);
arrayRemove(srcOutEdges, (edge: E) => edge.src === src.id);
}
const destInEdges = this._inEdgeMap.get(dest);
if (destInEdges && destInEdges.length > 0) {
removed = arrayRemove(destInEdges, (edge: DirectedEdge<E>) => edge.dest === dest.id)[0];
removed = arrayRemove(destInEdges, (edge: E) => edge.dest === dest.id)[0];
}
}
@ -246,22 +250,22 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The function removeAllEdges removes all edges between two vertices.
* @param {VertexId | DirectedVertex<V>} src - The `src` parameter can be either a `VertexId` or a `DirectedVertex<V>`.
* @param {VertexId | DirectedVertex<V>} dest - The `dest` parameter represents the destination vertex of an edge. It
* can be either a `VertexId` or a `DirectedVertex<V>`.
* @param {VertexId | V} src - The `src` parameter can be either a `VertexId` or a `V`.
* @param {VertexId | V} dest - The `dest` parameter represents the destination vertex of an edge. It
* can be either a `VertexId` or a `V`.
* @returns An empty array of DirectedEdge objects is being returned.
*/
removeAllEdges(src: VertexId | DirectedVertex<V>, dest: VertexId | DirectedVertex<V>): DirectedEdge<E>[] {
removeAllEdges(src: VertexId | V, dest: VertexId | V): E[] {
return [];
}
/**
* The function returns an array of incoming edges of a given vertex or vertex ID.
* @param {DirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `DirectedVertex<V>`
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `V`
* object or a `VertexId`.
* @returns The method `incomingEdgesOf` returns an array of `DirectedEdge<E>` objects.
* @returns The method `incomingEdgesOf` returns an array of `E` objects.
*/
incomingEdgesOf(vertexOrId: DirectedVertex<V> | VertexId): DirectedEdge<E>[] {
incomingEdgesOf(vertexOrId: V | VertexId): E[] {
const target = this._getVertex(vertexOrId);
if (target) {
return this.inEdgeMap.get(target) || []
@ -271,11 +275,11 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The function `outgoingEdgesOf` returns an array of outgoing directed edges from a given vertex or vertex ID.
* @param {DirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `DirectedVertex<V>`
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `V`
* object or a `VertexId`.
* @returns The method `outgoingEdgesOf` returns an array of `DirectedEdge<E>` objects.
* @returns The method `outgoingEdgesOf` returns an array of `E` objects.
*/
outgoingEdgesOf(vertexOrId: DirectedVertex<V> | VertexId): DirectedEdge<E>[] {
outgoingEdgesOf(vertexOrId: V | VertexId): E[] {
const target = this._getVertex(vertexOrId);
if (target) {
return this._outEdgeMap.get(target) || [];
@ -286,41 +290,41 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The function "degreeOf" returns the total degree of a vertex in a directed graph, which is the sum of its out-degree
* and in-degree.
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `DirectedVertex<V>`.
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `V`.
* @returns The sum of the out-degree and in-degree of the given vertex or vertex ID.
*/
degreeOf(vertexOrId: VertexId | DirectedVertex<V>): number {
degreeOf(vertexOrId: VertexId | V): number {
return this.outDegreeOf(vertexOrId) + this.inDegreeOf(vertexOrId);
}
/**
* The function "inDegreeOf" returns the number of incoming edges for a given vertex or vertex ID in a directed graph.
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `DirectedVertex<V>`.
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `V`.
* @returns The number of incoming edges of the specified vertex or vertex ID.
*/
inDegreeOf(vertexOrId: VertexId | DirectedVertex<V>): number {
inDegreeOf(vertexOrId: VertexId | V): number {
return this.incomingEdgesOf(vertexOrId).length;
}
/**
* The function "outDegreeOf" returns the number of outgoing edges from a given vertex.
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `DirectedVertex<V>`.
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `V`.
* @returns The number of outgoing edges from the specified vertex or vertex ID.
*/
outDegreeOf(vertexOrId: VertexId | DirectedVertex<V>): number {
outDegreeOf(vertexOrId: VertexId | V): number {
return this.outgoingEdgesOf(vertexOrId).length;
}
/**
* The function "edgesOf" returns an array of both outgoing and incoming directed edges of a given vertex or vertex ID.
* @param {VertexId | DirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `DirectedVertex<V>`.
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or a
* `V`.
* @returns an array of directed edges.
*/
edgesOf(vertexOrId: VertexId | DirectedVertex<V>): DirectedEdge<E>[] {
edgesOf(vertexOrId: VertexId | V): E[] {
return [...this.outgoingEdgesOf(vertexOrId), ...this.incomingEdgesOf(vertexOrId)];
}
@ -329,31 +333,31 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
* @param e - A directed edge object of type E.
* @returns either a DirectedVertex object or null.
*/
getEdgeSrc(e: DirectedEdge<E>): DirectedVertex<V> | null {
getEdgeSrc(e: E): V | null {
return this._getVertex(e.src);
}
/**
* The function "getEdgeDest" returns the destination vertex of a directed edge.
* @param e - DirectedEdge<E> - This is an object representing a directed edge in a graph. It contains information
* @param e - E - This is an object representing a directed edge in a graph. It contains information
* about the source vertex, destination vertex, and any associated data.
* @returns either a DirectedVertex object or null.
*/
getEdgeDest(e: DirectedEdge<E>): DirectedVertex<V> | null {
getEdgeDest(e: E): V | null {
return this._getVertex(e.dest);
}
/**
* The function `getDestinations` returns an array of directed vertices that are the destinations of outgoing edges
* from a given vertex.
* @param {DirectedVertex<V> | VertexId | null} vertex - The `vertex` parameter can be one of the following:
* @param {V | VertexId | null} vertex - The `vertex` parameter can be one of the following:
* @returns an array of DirectedVertex objects.
*/
getDestinations(vertex: DirectedVertex<V> | VertexId | null): DirectedVertex<V>[] {
getDestinations(vertex: V | VertexId | null): V[] {
if (vertex === null) {
return [];
}
const destinations: DirectedVertex<V>[] = [];
const destinations: V[] = [];
const outgoingEdges = this.outgoingEdgesOf(vertex);
for (const outEdge of outgoingEdges) {
const child = this.getEdgeDest(outEdge);
@ -367,20 +371,20 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The `topologicalSort` function performs a topological sort on a directed graph and returns the sorted vertices in
* reverse order, or null if the graph contains a cycle.
* @returns The function `topologicalSort()` returns an array of `DirectedVertex<V>` or `VertexId` objects in
* @returns The function `topologicalSort()` returns an array of `V` or `VertexId` objects in
* topological order, or `null` if there is a cycle in the graph.
*/
topologicalSort(): Array<DirectedVertex<V> | VertexId> | null {
topologicalSort(): Array<V | VertexId> | null {
// 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<DirectedVertex<V> | VertexId, TopologicalStatus> = new Map<DirectedVertex<V> | VertexId, TopologicalStatus>();
const statusMap: Map<V | VertexId, TopologicalStatus> = new Map<V | VertexId, TopologicalStatus>();
for (const entry of this.vertices) {
statusMap.set(entry[1], 0);
}
const sorted: (DirectedVertex<V> | VertexId)[] = [];
const sorted: (V | VertexId)[] = [];
let hasCycle = false;
const dfs = (cur: DirectedVertex<V> | VertexId) => {
const dfs = (cur: V | VertexId) => {
statusMap.set(cur, 1);
const children = this.getDestinations(cur);
for (const child of children) {
@ -408,10 +412,10 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The `edgeSet` function returns an array of all directed edges in the graph.
* @returns The `edgeSet()` method returns an array of `DirectedEdge<E>` objects.
* @returns The `edgeSet()` method returns an array of `E` objects.
*/
edgeSet(): DirectedEdge<E>[] {
let edges: DirectedEdge<E>[] = [];
edgeSet(): E[] {
let edges: E[] = [];
this._outEdgeMap.forEach(outEdges => {
edges = [...edges, ...outEdges];
});
@ -422,12 +426,12 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
/**
* The function `getNeighbors` returns an array of neighboring vertices of a given vertex in a directed graph.
* @param {DirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `DirectedVertex<V>`
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either a `V`
* object or a `VertexId`.
* @returns an array of DirectedVertex objects.
*/
getNeighbors(vertexOrId: DirectedVertex<V> | VertexId): DirectedVertex<V>[] {
const neighbors: DirectedVertex<V>[] = [];
getNeighbors(vertexOrId: V | VertexId): V[] {
const neighbors: V[] = [];
const vertex = this._getVertex(vertexOrId);
if (vertex) {
const outEdges = this.outgoingEdgesOf(vertex);
@ -451,7 +455,7 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
* @returns an array containing the starting and ending vertices of the given directed edge, or null if the edge does
* not exist in the graph.
*/
getEndsOfEdge(edge: DirectedEdge<E>): [DirectedVertex<V>, DirectedVertex<V>] | null {
getEndsOfEdge(edge: E): [V, V] | null {
if (!this.hasEdge(edge.src, edge.dest)) {
return null;
}
@ -464,11 +468,11 @@ export class DirectedGraph<V = number, E = number> extends AbstractGraph<V, E> i
}
}
protected _setOutEdgeMap(value: Map<DirectedVertex<V>, DirectedEdge<E>[]>) {
protected _setOutEdgeMap(value: Map<V, E[]>) {
this._outEdgeMap = value;
}
protected _setInEdgeMap(value: Map<DirectedVertex<V>, DirectedEdge<E>[]>) {
protected _setInEdgeMap(value: Map<V, E[]>) {
this._inEdgeMap = value;
}
}

View file

@ -9,24 +9,24 @@ import {arrayRemove} from '../../utils';
import {AbstractEdge, AbstractGraph, AbstractVertex} from './abstract-graph';
import type {VertexId} from '../types';
export class UndirectedVertex<V = number> extends AbstractVertex<V> {
export class UndirectedVertex<T = number> extends AbstractVertex<T> {
/**
* The constructor function initializes a vertex with an optional value.
* @param {VertexId} id - The `id` parameter is the identifier for the vertex. It is of type `VertexId`, which is
* typically a unique identifier for each vertex in a graph.
* @param {V} [val] - The "val" parameter is an optional parameter of type V. It is used to initialize the value of the
* @param {T} [val] - The "val" parameter is an optional parameter of type T. 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(id: VertexId, val?: V) {
constructor(id: VertexId, val?: T) {
super(id, val);
}
// _createVertex(id: VertexId, val?: V): UndirectedVertex<V> {
// return new UndirectedVertex<V>(id, val);
// _createVertex(id: VertexId, val?: T): T {
// return new T(id, val);
// }
}
export class UndirectedEdge<E = number> extends AbstractEdge<E> {
export class UndirectedEdge<T = number> extends AbstractEdge<T> {
/**
* The constructor function initializes an instance of a class with two vertex IDs, an optional weight, and an optional
* value.
@ -34,10 +34,10 @@ export class UndirectedEdge<E = number> extends AbstractEdge<E> {
* @param {VertexId} v2 - The parameter `v2` is a `VertexId`, 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 {E} [val] - The "val" parameter is an optional parameter of type E. It represents the value associated with
* @param {T} [val] - The "val" parameter is an optional parameter of type T. It represents the value associated with
* the edge.
*/
constructor(v1: VertexId, v2: VertexId, weight?: number, val?: E) {
constructor(v1: VertexId, v2: VertexId, weight?: number, val?: T) {
super(weight, val);
this._vertices = [v1, v2];
}
@ -52,24 +52,27 @@ export class UndirectedEdge<E = number> extends AbstractEdge<E> {
this._vertices = v;
}
// _createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): UndirectedEdge<E> {
// _createEdge(src: VertexId, dest: VertexId, weight?: number, val?: T): T {
// if (weight === undefined || weight === null) weight = 1;
// return new UndirectedEdge(src, dest, weight, val);
// }
}
export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdge> extends AbstractGraph<V, E> {
/**
* The constructor initializes a new instance of the class with an empty map of edges.
*/
constructor() {
export class UndirectedGraph<V extends UndirectedVertex<any>, E extends UndirectedEdge<any>> extends AbstractGraph<V, E> {
private readonly _vertexConstructor: new (id: VertexId, val?: V['val']) => V;
private readonly _edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E;
constructor(vertexConstructor: new (id: VertexId, val?: V['val']) => V, edgeConstructor: new (src: VertexId, dest: VertexId, weight?: number, val?: E['val']) => E) {
super();
this._edges = new Map<UndirectedVertex<V>, UndirectedEdge<E>[]>();
this._vertexConstructor = vertexConstructor;
this._edgeConstructor = edgeConstructor;
this._edges = new Map<V, E[]>();
}
protected _edges: Map<UndirectedVertex<V>, UndirectedEdge<E>[]>;
protected _edges: Map<V, E[]>;
get edges(): Map<UndirectedVertex<V>, UndirectedEdge<E>[]> {
get edges(): Map<V, E[]> {
return this._edges;
}
@ -79,8 +82,8 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
* @param id
* @param val
*/
_createVertex(id: VertexId, val?: V): UndirectedVertex<V> {
return new UndirectedVertex<V>(id, val);
_createVertex(id: VertexId, val?: V['val']): V {
return new this._vertexConstructor(id, val);
}
/**
@ -91,26 +94,26 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
* @param weight
* @param val
*/
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): UndirectedEdge<E> {
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E['val']): E {
if (weight === undefined || weight === null) weight = 1;
return new UndirectedEdge(src, dest, weight, val);
return new this._edgeConstructor(src, dest, weight, val);
}
/**
* The function `getEdge` returns the first undirected edge that connects two given vertices, or null if no such edge
* exists.
* @param {UndirectedVertex<V> | null | VertexId} v1 - The parameter `v1` represents either an `UndirectedVertex<V>`
* @param {V | null | VertexId} v1 - The parameter `v1` represents either an `V`
* object, `null`, or a `VertexId`. It is used to specify one of the vertices of the edge.
* @param {UndirectedVertex<V> | null | VertexId} v2 - The parameter `v2` represents either an `UndirectedVertex`
* @param {V | null | VertexId} v2 - The parameter `v2` represents either an `UndirectedVertex`
* object or a `VertexId` (identifier) of an undirected vertex.
* @returns an instance of `UndirectedEdge<E>` or `null`.
* @returns an instance of `E` or `null`.
*/
getEdge(v1: UndirectedVertex<V> | null | VertexId, v2: UndirectedVertex<V> | null | VertexId): UndirectedEdge<E> | null {
let edges: UndirectedEdge<E>[] | undefined = [];
getEdge(v1: V | null | VertexId, v2: V | null | VertexId): E | null {
let edges: E[] | undefined = [];
if (v1 !== null && v2 !== null) {
const vertex1: UndirectedVertex<V> | null = this._getVertex(v1);
const vertex2: UndirectedVertex<V> | null = this._getVertex(v2);
const vertex1: V | null = this._getVertex(v1);
const vertex2: V | null = this._getVertex(v2);
if (vertex1 && vertex2) {
edges = this._edges.get(vertex1)?.filter(e => e.vertices.includes(vertex2.id));
@ -126,7 +129,7 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
* array of two vertices connected by the edge.
* @returns a boolean value.
*/
addEdge(edge: UndirectedEdge<E>): boolean {
addEdge(edge: E): boolean {
for (const end of edge.vertices) {
const endVertex = this._getVertex(end);
if (endVertex === null) return false;
@ -144,32 +147,32 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
/**
* The function removes an edge between two vertices in an undirected graph.
* @param {UndirectedVertex<V> | VertexId} v1 - The parameter `v1` represents either an `UndirectedVertex<V>` object or
* @param {V | VertexId} v1 - The parameter `v1` represents either an `V` object or
* a `VertexId`. It is used to specify one of the vertices of the edge that needs to be removed.
* @param {UndirectedVertex<V> | VertexId} v2 - The parameter `v2` represents either an instance of the
* @param {V | VertexId} v2 - The parameter `v2` represents either an instance of the
* `UndirectedVertex` class or a `VertexId`. It is used to identify the second vertex of the edge that needs to be
* removed.
* @returns The function `removeEdgeBetween` returns an `UndirectedEdge<E>` object if an edge is successfully removed
* @returns The function `removeEdgeBetween` returns an `E` object if an edge is successfully removed
* between the two vertices `v1` and `v2`. If either `v1` or `v2` is not found in the graph, or if there is no edge
* between them, the function returns `null`.
*/
removeEdgeBetween(v1: UndirectedVertex<V> | VertexId, v2: UndirectedVertex<V> | VertexId): UndirectedEdge<E> | null {
removeEdgeBetween(v1: V | VertexId, v2: V | VertexId): E | null {
const vertex1: UndirectedVertex<V> | null = this._getVertex(v1);
const vertex2: UndirectedVertex<V> | null = this._getVertex(v2);
const vertex1: V | null = this._getVertex(v1);
const vertex2: V | null = this._getVertex(v2);
if (!vertex1 || !vertex2) {
return null;
}
const v1Edges = this._edges.get(vertex1);
let removed: UndirectedEdge<E> | null = null;
let removed: E | null = null;
if (v1Edges) {
removed = arrayRemove<UndirectedEdge<E>>(v1Edges, (e: UndirectedEdge<E>) => e.vertices.includes(vertex2.id))[0] || null;
removed = arrayRemove<E>(v1Edges, (e: E) => e.vertices.includes(vertex2.id))[0] || null;
}
const v2Edges = this._edges.get(vertex2);
if (v2Edges) {
arrayRemove<UndirectedEdge<E>>(v2Edges, (e: UndirectedEdge<E>) => e.vertices.includes(vertex1.id));
arrayRemove<E>(v2Edges, (e: E) => e.vertices.includes(vertex1.id));
}
return removed;
}
@ -180,17 +183,17 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
* containing the two vertices connected by the edge.
* @returns The method is returning an UndirectedEdge object or null.
*/
removeEdge(edge: UndirectedEdge<E>): UndirectedEdge<E> | null {
removeEdge(edge: E): E | null {
return this.removeEdgeBetween(edge.vertices[0], edge.vertices[1]);
}
/**
* The function "degreeOf" returns the degree of a given vertex in an undirected graph.
* @param {VertexId | UndirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or an
* `UndirectedVertex<V>`.
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or an
* `V`.
* @returns the degree of the vertex.
*/
degreeOf(vertexOrId: VertexId | UndirectedVertex<V>): number {
degreeOf(vertexOrId: VertexId | V): number {
const vertex = this._getVertex(vertexOrId);
if (vertex) {
return this._edges.get(vertex)?.length || 0;
@ -201,11 +204,11 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
/**
* The function "edgesOf" returns an array of undirected edges connected to a given vertex or vertex ID.
* @param {VertexId | UndirectedVertex<V>} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or an
* `UndirectedVertex<V>`.
* @param {VertexId | V} vertexOrId - The parameter `vertexOrId` can be either a `VertexId` or an
* `V`.
* @returns an array of UndirectedEdge objects.
*/
edgesOf(vertexOrId: VertexId | UndirectedVertex<V>): UndirectedEdge<E>[] {
edgesOf(vertexOrId: VertexId | V): E[] {
const vertex = this._getVertex(vertexOrId);
if (vertex) {
return this._edges.get(vertex) || [];
@ -216,10 +219,10 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
/**
* The function "edgeSet" returns an array of unique undirected edges from a set of edges.
* @returns The method `edgeSet()` returns an array of `UndirectedEdge<E>` objects.
* @returns The method `edgeSet()` returns an array of `E` objects.
*/
edgeSet(): UndirectedEdge<E>[] {
const edgeSet: Set<UndirectedEdge<E>> = new Set();
edgeSet(): E[] {
const edgeSet: Set<E> = new Set();
this._edges.forEach(edges => {
edges.forEach(edge => {
edgeSet.add(edge);
@ -230,11 +233,11 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
/**
* The function "getEdgesOf" returns an array of undirected edges connected to a given vertex or vertex ID.
* @param {UndirectedVertex<V> | VertexId} vertexOrId - The parameter `vertexOrId` can be either an
* `UndirectedVertex<V>` object or a `VertexId`.
* @returns The function `getEdgesOf` returns an array of `UndirectedEdge<E>` objects.
* @param {V | VertexId} vertexOrId - The parameter `vertexOrId` can be either an
* `V` object or a `VertexId`.
* @returns The function `getEdgesOf` returns an array of `E` objects.
*/
getEdgesOf(vertexOrId: UndirectedVertex<V> | VertexId): UndirectedEdge<E>[] {
getEdgesOf(vertexOrId: V | VertexId): E[] {
const vertex = this._getVertex(vertexOrId);
if (!vertex) {
return [];
@ -244,12 +247,12 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
/**
* The function `getNeighbors` returns an array of neighboring vertices of a given vertex in an undirected graph.
* @param {UndirectedVertex<V> | VertexId} vertexOrId - The `vertexOrId` parameter can be either an
* `UndirectedVertex<V>` object or a `VertexId`. It represents the vertex for which we want to find the neighbors.
* @param {V | VertexId} vertexOrId - The `vertexOrId` parameter can be either an
* `V` object or a `VertexId`. It represents the vertex for which we want to find the neighbors.
* @returns an array of UndirectedVertex objects.
*/
getNeighbors(vertexOrId: UndirectedVertex<V> | VertexId): UndirectedVertex<V>[] {
const neighbors: UndirectedVertex<V>[] = [];
getNeighbors(vertexOrId: V | VertexId): V[] {
const neighbors: V[] = [];
const vertex = this._getVertex(vertexOrId);
if (vertex) {
const neighborEdges = this.getEdgesOf(vertex);
@ -271,7 +274,7 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
* @returns The function `getEndsOfEdge` returns an array containing the two ends of the given `edge` if the edge
* exists in the graph. If the edge does not exist, it returns `null`.
*/
getEndsOfEdge(edge: UndirectedEdge<E>): [UndirectedVertex<V>, UndirectedVertex<V>] | null {
getEndsOfEdge(edge: E): [V, V] | null {
if (!this.hasEdge(edge.vertices[0], edge.vertices[1])) {
return null;
}
@ -284,7 +287,7 @@ export class UndirectedGraph<V extends UndirectedVertex, E extends UndirectedEdg
}
}
protected _setEdges(v: Map<UndirectedVertex<V>, UndirectedEdge<E>[]>) {
protected _setEdges(v: Map<V, E[]>) {
this._edges = v;
}
}

View file

@ -1,43 +1,42 @@
import {VertexId} from '../types';
import {AbstractEdge, AbstractVertex} from '../graph';
export interface IGraph<V, E> {
hasVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean;
hasVertex(vertexOrId: V | VertexId): boolean;
_getVertex(vertexOrId: VertexId | AbstractVertex<V>): AbstractVertex<V> | null;
_getVertex(vertexOrId: VertexId | V): V | null;
_getVertexId(vertexOrId: AbstractVertex<V> | VertexId): VertexId;
_getVertexId(vertexOrId: V | VertexId): VertexId;
createAddVertex(id: VertexId, val?: V): boolean;
addVertex(newVertex: AbstractVertex<V>): boolean;
addVertex(newVertex: V): boolean;
removeVertex(vertexOrId: AbstractVertex<V> | VertexId): boolean;
removeVertex(vertexOrId: V | VertexId): boolean;
removeAllVertices(vertices: AbstractVertex<V>[] | VertexId[]): boolean;
removeAllVertices(vertices: V[] | VertexId[]): boolean;
degreeOf(vertexOrId: AbstractVertex<V> | VertexId): number;
degreeOf(vertexOrId: V | VertexId): number;
edgesOf(vertexOrId: AbstractVertex<V> | VertexId): AbstractEdge<E>[];
edgesOf(vertexOrId: V | VertexId): E[];
hasEdge(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId): boolean;
hasEdge(src: V | VertexId, dest: V | VertexId): boolean;
getEdge(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
getEdge(srcOrId: V | VertexId, destOrId: V | VertexId): E | null;
edgeSet(): AbstractEdge<E>[];
edgeSet(): E[];
createAddEdge(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId, weight: number, val: E): boolean;
createAddEdge(src: V | VertexId, dest: V | VertexId, weight: number, val: E): boolean;
addEdge(edge: AbstractEdge<E>): boolean;
addEdge(edge: E): boolean;
removeEdgeBetween(src: AbstractVertex<V> | VertexId, dest: AbstractVertex<V> | VertexId): AbstractEdge<E> | null;
removeEdgeBetween(src: V | VertexId, dest: V | VertexId): E | null;
removeEdge(edge: AbstractEdge<E>): AbstractEdge<E> | null;
removeEdge(edge: E): E | null;
setEdgeWeight(srcOrId: AbstractVertex<V> | VertexId, destOrId: AbstractVertex<V> | VertexId, weight: number): boolean;
setEdgeWeight(srcOrId: V | VertexId, destOrId: V | VertexId, weight: number): boolean;
getMinPathBetween(v1: AbstractVertex<V> | VertexId, v2: AbstractVertex<V> | VertexId, isWeight?: boolean): AbstractVertex<V>[] | null;
getMinPathBetween(v1: V | VertexId, v2: V | VertexId, isWeight?: boolean): V[] | null;
getNeighbors(vertexOrId: AbstractVertex<V> | VertexId): AbstractVertex<V>[];
getNeighbors(vertexOrId: V | VertexId): V[];
}

View file

@ -1,10 +1,56 @@
import {BinaryTreeNodeId} from '../types';
import {BinaryTreeNode} from '../binary-tree';
import {FamilyPosition} from '../binary-tree';
export interface IBinaryTreeNode<T> {
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): BinaryTreeNode<T> | null
export interface IBinaryTreeNode<T, FAMILY extends IBinaryTreeNode<T, FAMILY>> {
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): FAMILY | null;
get id(): BinaryTreeNodeId
set id(v: BinaryTreeNodeId)
get val(): T
set val(v: T)
get left(): FAMILY | null | undefined
set left(v: FAMILY | null | undefined)
get right(): FAMILY | null | undefined
set right(v: FAMILY | null | undefined)
get parent(): FAMILY | null | undefined
set parent(v: FAMILY | null | undefined)
get familyPosition(): FamilyPosition
set familyPosition(v: FamilyPosition)
get count(): number
set count(v: number)
get height(): number
set height(v: number)
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): FAMILY | null
swapLocation(swapNode: FAMILY): FAMILY
clone(): FAMILY | null;
}
export interface IBinaryTree<T> {
_createNode(id: BinaryTreeNodeId, val: T | null, count?: number): BinaryTreeNode<T> | null
export interface IBinaryTree<N extends IBinaryTreeNode<N['val'], N>> {
_createNode(id: BinaryTreeNodeId, val: N['val'] | null, count?: number): N | null
}

View file

@ -1,16 +1,15 @@
import {VertexId} from '../types';
import {DirectedEdge, DirectedVertex} from '../graph';
export interface IDirectedGraph<V, E> {
incomingEdgesOf(vertex: DirectedVertex<V>): DirectedEdge<E>[];
incomingEdgesOf(vertex: V): E[];
outgoingEdgesOf(vertex: DirectedVertex<V>): DirectedEdge<E>[];
outgoingEdgesOf(vertex: V): E[];
inDegreeOf(vertexOrId: DirectedVertex<V> | VertexId): number;
inDegreeOf(vertexOrId: V | VertexId): number;
outDegreeOf(vertexOrId: DirectedVertex<V> | VertexId): number;
outDegreeOf(vertexOrId: V | VertexId): number;
getEdgeSrc(e: DirectedEdge<E>): DirectedVertex<V> | null;
getEdgeSrc(e: E): V | null;
getEdgeDest(e: DirectedEdge<E>): DirectedVertex<V> | null;
getEdgeDest(e: E): V | null;
}

View file

@ -1,6 +1,8 @@
import {AVLTreeNode} from '../binary-tree';
export type AVLTreeDeleted<T> = {
deleted: AVLTreeNode<T> | null;
needBalanced: AVLTreeNode<T> | null;
}
export type AVLTreeDeleted<N> = {
deleted: N | null;
needBalanced: N | null;
}
export type RecursiveAVLTreeNode<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

@ -4,6 +4,7 @@ export type BinaryTreeNodePropertyName = 'id' | 'val' | 'count';
export type NodeOrPropertyName = 'node' | BinaryTreeNodePropertyName;
export type DFSOrderPattern = 'in' | 'pre' | 'post';
export type BinaryTreeNodeId = number;
export type BinaryTreeDeleted<T> = { deleted: BinaryTreeNode<T> | null | undefined, needBalanced: BinaryTreeNode<T> | null };
export type ResultByProperty<T> = T | BinaryTreeNode<T> | number | BinaryTreeNodeId;
export type ResultsByProperty<T> = ResultByProperty<T>[];
export type BinaryTreeDeleted<N> = { deleted: N | null | undefined, needBalanced: N | null };
export type ResultByProperty<N extends BinaryTreeNode<N['val'], N>> = N['val'] | N | number | BinaryTreeNodeId;
export type ResultsByProperty<N extends BinaryTreeNode<N['val'], N>> = ResultByProperty<N>[];
export type RecursiveBinaryTreeNode<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>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View file

@ -2,4 +2,5 @@ import {BSTNode} from '../binary-tree';
import type {BinaryTreeNodeId} from './binary-tree';
export type BSTComparator = (a: BinaryTreeNodeId, b: BinaryTreeNodeId) => number;
export type BSTDeletedResult<T> = { deleted: BSTNode<T> | null, needBalanced: BSTNode<T> | null };
export type BSTDeletedResult<N extends BSTNode<N['val'], N>> = { deleted: N | null, needBalanced: N | null };
export type RecursiveBSTNode<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, BSTNode<T, BSTNode<T, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View file

@ -5,4 +5,4 @@ export enum TopologicalProperty {
VAL = 'VAL',
NODE = 'NODE',
ID = 'ID',
}
}

View file

@ -1,3 +1 @@
import {BSTNode} from '../binary-tree';
export type TreeMultiSetDeletedResult<T> = { deleted: BSTNode<T> | null, needBalanced: BSTNode<T> | null };
export type TreeMultiSetDeletedResult<N> = { deleted: N | null, needBalanced: N | null };

View file

@ -0,0 +1,122 @@
import {AVLTree} from '../../../../src';
describe('AVL Tree Test', () => {
it('should perform various operations on a AVL Tree', () => {
const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5];
const tree = new AVLTree();
for (const i of arr) tree.add(i, i);
const node6 = tree.get(6);
expect(node6 && tree.getHeight(node6)).toBe(3);
expect(node6 && tree.getDepth(node6)).toBe(1);
const getNodeById = tree.get(10, 'id');
expect(getNodeById?.id).toBe(10);
const getNodesByCount = tree.getNodes(1, 'count');
expect(getNodesByCount.length).toBe(16);
const getMinNodeByRoot = tree.getLeftMost();
expect(getMinNodeByRoot?.id).toBe(1);
const node15 = tree.get(15);
const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node15);
expect(getMinNodeBySpecificNode?.id).toBe(12);
const subTreeSum = node15 && tree.subTreeSum(node15);
expect(subTreeSum).toBe(70);
const lesserSum = tree.lesserSum(10);
expect(lesserSum).toBe(45);
if (node15) {
const subTreeAdd = tree.subTreeAdd(node15, 1, 'count');
expect(subTreeAdd).toBe(true);
}
// node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
expect(node15?.val).toBe(15);
const node11 = tree.get(11);
if (node11) {
const allGreaterNodesAdd = tree.allGreaterNodesAdd(node11, 2, 'count');
expect(allGreaterNodesAdd).toBe(true);
}
const dfs = tree.DFS('in', 'node');
expect(dfs[0].id).toBe(1);
expect(dfs[dfs.length - 1].id).toBe(16);
tree.balance();
const bfs = tree.BFS('node');
expect(tree.isBalanced()).toBe(true);
expect(bfs[0].id).toBe(8);
expect(bfs[bfs.length - 1].id).toBe(16);
expect(tree.remove(11, true)[0].deleted?.id).toBe(11);
expect(tree.isAVLBalanced()).toBe(true);
expect(node15 && tree.getHeight(node15)).toBe(2);
expect(tree.remove(1, true)[0].deleted?.id).toBe(1);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(4);
expect(tree.remove(4, true)[0].deleted?.id).toBe(4);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(4);
expect(tree.remove(10, true)[0].deleted?.id).toBe(10);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(3);
expect(tree.remove(15, true)[0].deleted?.id).toBe(15);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(3);
expect(tree.remove(5, true)[0].deleted?.id).toBe(5);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(3);
expect(tree.remove(13, true)[0].deleted?.id).toBe(13);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(3);
expect(tree.remove(3, true)[0].deleted?.id).toBe(3);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(3);
expect(tree.remove(8, true)[0].deleted?.id).toBe(8);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(3);
expect(tree.remove(6, true)[0].deleted?.id).toBe(6);
expect(tree.remove(6, true).length).toBe(0);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(2);
expect(tree.remove(7, true)[0].deleted?.id).toBe(7);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(2);
expect(tree.remove(9, true)[0].deleted?.id).toBe(9);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(2);
expect(tree.remove(14, true)[0].deleted?.id).toBe(14);
expect(tree.isAVLBalanced()).toBe(true);
expect(tree.getHeight()).toBe(1);
expect(tree.isAVLBalanced()).toBe(true);
const lastBFSIds = tree.BFS();
expect(lastBFSIds[0]).toBe(12);
expect(lastBFSIds[1]).toBe(2);
expect(lastBFSIds[2]).toBe(16);
const lastBFSNodes = tree.BFS('node');
expect(lastBFSNodes[0].id).toBe(12);
expect(lastBFSNodes[1].id).toBe(2);
expect(lastBFSNodes[2].id).toBe(16);
});
});

View file

@ -2,7 +2,6 @@ import {BST, BSTNode} from '../../../../src';
describe('BST Case6', () => {
it('should perform various operations on a Binary Search Tree', () => {
const tree = new BST();
expect(tree).toBeInstanceOf(BST);

View file

@ -1,10 +1,10 @@
import {DirectedEdge, DirectedGraph, DirectedVertex, VertexId} from '../../../../src';
describe('DirectedGraph Operation Test', () => {
let graph: DirectedGraph<number, number>;
let graph: DirectedGraph<DirectedVertex, DirectedEdge>;
beforeEach(() => {
graph = new DirectedGraph();
graph = new DirectedGraph(DirectedVertex, DirectedEdge);
});
@ -59,13 +59,12 @@ describe('DirectedGraph Operation Test', () => {
graph.addEdge(edgeBC);
const topologicalOrder = graph.topologicalSort();
if (topologicalOrder) expect(topologicalOrder.map(v => v instanceof DirectedVertex ? v.id: '')).toEqual(['A', 'B', 'C']);
if (topologicalOrder) expect(topologicalOrder.map(v => v instanceof DirectedVertex ? v.id : '')).toEqual(['A', 'B', 'C']);
});
});
class MyVertex<V extends string> extends DirectedVertex<V> {
constructor(id: VertexId, val?: V) {
super(id, val);
this._data = val;
@ -84,7 +83,7 @@ class MyVertex<V extends string> extends DirectedVertex<V> {
class MyEdge<E extends string> extends DirectedEdge<E> {
constructor(v1: VertexId, v2: VertexId, weight: number, val?: E) {
constructor(v1: VertexId, v2: VertexId, weight?: number, val?: E) {
super(v1, v2, weight, val);
this._data = val;
}
@ -100,21 +99,14 @@ class MyEdge<E extends string> extends DirectedEdge<E> {
}
}
class MyDirectedGraph<V extends string, E extends string> extends DirectedGraph<V, E> {
_createVertex(id: VertexId, val?: V): MyVertex<V> {
return new MyVertex<V>(id, val);
}
class MyDirectedGraph<V extends MyVertex<string>, E extends MyEdge<string>> extends DirectedGraph<V, E> {
_createEdge(src: VertexId, dest: VertexId, weight?: number, val?: E): MyEdge<E> {
if (weight === undefined || weight === null) weight = 1;
return new MyEdge<E>(src, dest, weight, val);
}
}
describe('Inherit from DirectedGraph and perform operations', () => {
let myGraph = new MyDirectedGraph();
let myGraph = new MyDirectedGraph<MyVertex<string>, MyEdge<string>>(MyVertex, MyEdge);
beforeEach(() => {
myGraph = new MyDirectedGraph();
myGraph = new MyDirectedGraph(MyVertex, MyEdge);
});
it('Add vertices', () => {
@ -150,11 +142,16 @@ describe('Inherit from DirectedGraph and perform operations', () => {
const edge2 = myGraph.getEdge(myGraph.getVertex(1), myGraph.getVertex(2));
const edge3 = myGraph.getEdge(1, '100');
// edge1.data has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class.
expect(edge1?.val).toBe('val1');
expect(edge1).toBeInstanceOf(MyEdge);
edge1 && expect(edge1.src).toBe(1);
expect(edge1).toEqual(edge2);
expect(edge3).toBeNull();
if (edge1) {
expect(edge1.data).toBe('val1');
expect(edge1?.val).toBe('val1');
expect(edge1).toBeInstanceOf(MyEdge);
expect(edge1.src).toBe(1);
expect(edge1).toEqual(edge2);
expect(edge3).toBeNull();
}
});
it('Edge set and vertex set', () => {
@ -214,7 +211,7 @@ describe('Inherit from DirectedGraph and perform operations', () => {
});
describe('Inherit from DirectedGraph and perform operations test2.', () => {
const myGraph = new MyDirectedGraph<string, string>();
const myGraph = new MyDirectedGraph<MyVertex<string>, MyEdge<string>>(MyVertex, MyEdge);
it('should test graph operations', () => {
const vertex1 = new MyVertex(1, 'data1');