tree-set-typed 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +61 -0
- package/.prettierignore +6 -0
- package/.prettierrc.js +16 -0
- package/LICENSE +21 -0
- package/README.md +482 -0
- package/coverage/clover.xml +13 -0
- package/coverage/coverage-final.json +96 -0
- package/coverage/coverage-summary.json +60 -0
- package/coverage/lcov-report/base.css +403 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +119 -0
- package/coverage/lcov-report/index.ts.html +109 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +206 -0
- package/coverage/lcov.info +14 -0
- package/dist/cjs/index.cjs +12 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs-legacy/index.cjs +12 -0
- package/dist/cjs-legacy/index.cjs.map +1 -0
- package/dist/esm/index.mjs +3 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm-legacy/index.mjs +3 -0
- package/dist/esm-legacy/index.mjs.map +1 -0
- package/dist/types/common/index.d.ts +12 -0
- package/dist/types/constants/index.d.ts +4 -0
- package/dist/types/data-structures/base/index.d.ts +2 -0
- package/dist/types/data-structures/base/iterable-element-base.d.ts +219 -0
- package/dist/types/data-structures/base/iterable-entry-base.d.ts +150 -0
- package/dist/types/data-structures/base/linear-base.d.ts +335 -0
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +236 -0
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +197 -0
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +440 -0
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +174 -0
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +807 -0
- package/dist/types/data-structures/binary-tree/bst.d.ts +645 -0
- package/dist/types/data-structures/binary-tree/index.d.ts +10 -0
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +312 -0
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +160 -0
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +243 -0
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +333 -0
- package/dist/types/data-structures/graph/abstract-graph.d.ts +340 -0
- package/dist/types/data-structures/graph/directed-graph.d.ts +332 -0
- package/dist/types/data-structures/graph/index.d.ts +4 -0
- package/dist/types/data-structures/graph/map-graph.d.ts +78 -0
- package/dist/types/data-structures/graph/undirected-graph.d.ts +347 -0
- package/dist/types/data-structures/hash/hash-map.d.ts +428 -0
- package/dist/types/data-structures/hash/index.d.ts +1 -0
- package/dist/types/data-structures/heap/heap.d.ts +552 -0
- package/dist/types/data-structures/heap/index.d.ts +3 -0
- package/dist/types/data-structures/heap/max-heap.d.ts +32 -0
- package/dist/types/data-structures/heap/min-heap.d.ts +33 -0
- package/dist/types/data-structures/index.d.ts +12 -0
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +437 -0
- package/dist/types/data-structures/linked-list/index.d.ts +3 -0
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +567 -0
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +27 -0
- package/dist/types/data-structures/matrix/index.d.ts +2 -0
- package/dist/types/data-structures/matrix/matrix.d.ts +168 -0
- package/dist/types/data-structures/matrix/navigator.d.ts +55 -0
- package/dist/types/data-structures/priority-queue/index.d.ts +3 -0
- package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +27 -0
- package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +26 -0
- package/dist/types/data-structures/priority-queue/priority-queue.d.ts +15 -0
- package/dist/types/data-structures/queue/deque.d.ts +459 -0
- package/dist/types/data-structures/queue/index.d.ts +2 -0
- package/dist/types/data-structures/queue/queue.d.ts +364 -0
- package/dist/types/data-structures/stack/index.d.ts +1 -0
- package/dist/types/data-structures/stack/stack.d.ts +324 -0
- package/dist/types/data-structures/tree/index.d.ts +1 -0
- package/dist/types/data-structures/tree/tree.d.ts +62 -0
- package/dist/types/data-structures/trie/index.d.ts +1 -0
- package/dist/types/data-structures/trie/trie.d.ts +412 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/interfaces/binary-tree.d.ts +60 -0
- package/dist/types/interfaces/doubly-linked-list.d.ts +1 -0
- package/dist/types/interfaces/graph.d.ts +21 -0
- package/dist/types/interfaces/heap.d.ts +1 -0
- package/dist/types/interfaces/index.d.ts +8 -0
- package/dist/types/interfaces/navigator.d.ts +1 -0
- package/dist/types/interfaces/priority-queue.d.ts +1 -0
- package/dist/types/interfaces/segment-tree.d.ts +1 -0
- package/dist/types/interfaces/singly-linked-list.d.ts +1 -0
- package/dist/types/types/common.d.ts +15 -0
- package/dist/types/types/data-structures/base/base.d.ts +13 -0
- package/dist/types/types/data-structures/base/index.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/avl-tree.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/binary-indexed-tree.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/binary-tree.d.ts +29 -0
- package/dist/types/types/data-structures/binary-tree/bst.d.ts +12 -0
- package/dist/types/types/data-structures/binary-tree/index.d.ts +9 -0
- package/dist/types/types/data-structures/binary-tree/red-black-tree.d.ts +3 -0
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -0
- package/dist/types/types/data-structures/binary-tree/tree-counter.d.ts +2 -0
- package/dist/types/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -0
- package/dist/types/types/data-structures/graph/abstract-graph.d.ts +14 -0
- package/dist/types/types/data-structures/graph/directed-graph.d.ts +1 -0
- package/dist/types/types/data-structures/graph/index.d.ts +3 -0
- package/dist/types/types/data-structures/graph/map-graph.d.ts +1 -0
- package/dist/types/types/data-structures/graph/undirected-graph.d.ts +1 -0
- package/dist/types/types/data-structures/hash/hash-map.d.ts +19 -0
- package/dist/types/types/data-structures/hash/index.d.ts +2 -0
- package/dist/types/types/data-structures/heap/heap.d.ts +5 -0
- package/dist/types/types/data-structures/heap/index.d.ts +1 -0
- package/dist/types/types/data-structures/heap/max-heap.d.ts +1 -0
- package/dist/types/types/data-structures/heap/min-heap.d.ts +1 -0
- package/dist/types/types/data-structures/index.d.ts +12 -0
- package/dist/types/types/data-structures/linked-list/doubly-linked-list.d.ts +2 -0
- package/dist/types/types/data-structures/linked-list/index.d.ts +3 -0
- package/dist/types/types/data-structures/linked-list/singly-linked-list.d.ts +2 -0
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +4 -0
- package/dist/types/types/data-structures/matrix/index.d.ts +2 -0
- package/dist/types/types/data-structures/matrix/matrix.d.ts +7 -0
- package/dist/types/types/data-structures/matrix/navigator.d.ts +14 -0
- package/dist/types/types/data-structures/priority-queue/index.d.ts +3 -0
- package/dist/types/types/data-structures/priority-queue/max-priority-queue.d.ts +1 -0
- package/dist/types/types/data-structures/priority-queue/min-priority-queue.d.ts +1 -0
- package/dist/types/types/data-structures/priority-queue/priority-queue.d.ts +2 -0
- package/dist/types/types/data-structures/queue/deque.d.ts +4 -0
- package/dist/types/types/data-structures/queue/index.d.ts +2 -0
- package/dist/types/types/data-structures/queue/queue.d.ts +4 -0
- package/dist/types/types/data-structures/stack/index.d.ts +1 -0
- package/dist/types/types/data-structures/stack/stack.d.ts +2 -0
- package/dist/types/types/data-structures/tree/index.d.ts +1 -0
- package/dist/types/types/data-structures/tree/tree.d.ts +1 -0
- package/dist/types/types/data-structures/trie/index.d.ts +1 -0
- package/dist/types/types/data-structures/trie/trie.d.ts +4 -0
- package/dist/types/types/index.d.ts +3 -0
- package/dist/types/types/utils/index.d.ts +2 -0
- package/dist/types/types/utils/utils.d.ts +22 -0
- package/dist/types/types/utils/validate-type.d.ts +19 -0
- package/dist/types/utils/index.d.ts +2 -0
- package/dist/types/utils/number.d.ts +14 -0
- package/dist/types/utils/utils.d.ts +209 -0
- package/dist/umd/red-black-tree-typed.js +14578 -0
- package/dist/umd/red-black-tree-typed.js.map +1 -0
- package/dist/umd/red-black-tree-typed.min.js +44 -0
- package/dist/umd/red-black-tree-typed.min.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/highlight.css +92 -0
- package/docs/assets/main.js +59 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1383 -0
- package/docs/classes/AVLTree.html +2046 -0
- package/docs/classes/AVLTreeNode.html +263 -0
- package/docs/index.html +523 -0
- package/docs/modules.html +45 -0
- package/jest.config.js +8 -0
- package/package.json +113 -0
- package/src/common/index.ts +23 -0
- package/src/constants/index.ts +4 -0
- package/src/data-structures/base/index.ts +2 -0
- package/src/data-structures/base/iterable-element-base.ts +352 -0
- package/src/data-structures/base/iterable-entry-base.ts +246 -0
- package/src/data-structures/base/linear-base.ts +643 -0
- package/src/data-structures/binary-tree/avl-tree-counter.ts +539 -0
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +438 -0
- package/src/data-structures/binary-tree/avl-tree.ts +840 -0
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +331 -0
- package/src/data-structures/binary-tree/binary-tree.ts +2492 -0
- package/src/data-structures/binary-tree/bst.ts +2024 -0
- package/src/data-structures/binary-tree/index.ts +10 -0
- package/src/data-structures/binary-tree/red-black-tree.ts +767 -0
- package/src/data-structures/binary-tree/segment-tree.ts +324 -0
- package/src/data-structures/binary-tree/tree-counter.ts +575 -0
- package/src/data-structures/binary-tree/tree-multi-map.ts +549 -0
- package/src/data-structures/graph/abstract-graph.ts +1081 -0
- package/src/data-structures/graph/directed-graph.ts +715 -0
- package/src/data-structures/graph/index.ts +4 -0
- package/src/data-structures/graph/map-graph.ts +132 -0
- package/src/data-structures/graph/undirected-graph.ts +626 -0
- package/src/data-structures/hash/hash-map.ts +813 -0
- package/src/data-structures/hash/index.ts +1 -0
- package/src/data-structures/heap/heap.ts +1020 -0
- package/src/data-structures/heap/index.ts +3 -0
- package/src/data-structures/heap/max-heap.ts +47 -0
- package/src/data-structures/heap/min-heap.ts +36 -0
- package/src/data-structures/index.ts +12 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +876 -0
- package/src/data-structures/linked-list/index.ts +3 -0
- package/src/data-structures/linked-list/singly-linked-list.ts +1050 -0
- package/src/data-structures/linked-list/skip-linked-list.ts +173 -0
- package/src/data-structures/matrix/index.ts +2 -0
- package/src/data-structures/matrix/matrix.ts +491 -0
- package/src/data-structures/matrix/navigator.ts +124 -0
- package/src/data-structures/priority-queue/index.ts +3 -0
- package/src/data-structures/priority-queue/max-priority-queue.ts +42 -0
- package/src/data-structures/priority-queue/min-priority-queue.ts +29 -0
- package/src/data-structures/priority-queue/priority-queue.ts +19 -0
- package/src/data-structures/queue/deque.ts +1001 -0
- package/src/data-structures/queue/index.ts +2 -0
- package/src/data-structures/queue/queue.ts +592 -0
- package/src/data-structures/stack/index.ts +1 -0
- package/src/data-structures/stack/stack.ts +469 -0
- package/src/data-structures/tree/index.ts +1 -0
- package/src/data-structures/tree/tree.ts +115 -0
- package/src/data-structures/trie/index.ts +1 -0
- package/src/data-structures/trie/trie.ts +756 -0
- package/src/index.ts +24 -0
- package/src/interfaces/binary-tree.ts +252 -0
- package/src/interfaces/doubly-linked-list.ts +1 -0
- package/src/interfaces/graph.ts +44 -0
- package/src/interfaces/heap.ts +1 -0
- package/src/interfaces/index.ts +8 -0
- package/src/interfaces/navigator.ts +1 -0
- package/src/interfaces/priority-queue.ts +1 -0
- package/src/interfaces/segment-tree.ts +1 -0
- package/src/interfaces/singly-linked-list.ts +1 -0
- package/src/types/common.ts +25 -0
- package/src/types/data-structures/base/base.ts +34 -0
- package/src/types/data-structures/base/index.ts +1 -0
- package/src/types/data-structures/binary-tree/avl-tree-counter.ts +3 -0
- package/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +3 -0
- package/src/types/data-structures/binary-tree/avl-tree.ts +3 -0
- package/src/types/data-structures/binary-tree/binary-indexed-tree.ts +1 -0
- package/src/types/data-structures/binary-tree/binary-tree.ts +31 -0
- package/src/types/data-structures/binary-tree/bst.ts +19 -0
- package/src/types/data-structures/binary-tree/index.ts +9 -0
- package/src/types/data-structures/binary-tree/red-black-tree.ts +5 -0
- package/src/types/data-structures/binary-tree/segment-tree.ts +1 -0
- package/src/types/data-structures/binary-tree/tree-counter.ts +3 -0
- package/src/types/data-structures/binary-tree/tree-multi-map.ts +3 -0
- package/src/types/data-structures/graph/abstract-graph.ts +18 -0
- package/src/types/data-structures/graph/directed-graph.ts +2 -0
- package/src/types/data-structures/graph/index.ts +3 -0
- package/src/types/data-structures/graph/map-graph.ts +1 -0
- package/src/types/data-structures/graph/undirected-graph.ts +1 -0
- package/src/types/data-structures/hash/hash-map.ts +19 -0
- package/src/types/data-structures/hash/index.ts +3 -0
- package/src/types/data-structures/heap/heap.ts +6 -0
- package/src/types/data-structures/heap/index.ts +1 -0
- package/src/types/data-structures/heap/max-heap.ts +1 -0
- package/src/types/data-structures/heap/min-heap.ts +1 -0
- package/src/types/data-structures/index.ts +12 -0
- package/src/types/data-structures/linked-list/doubly-linked-list.ts +3 -0
- package/src/types/data-structures/linked-list/index.ts +3 -0
- package/src/types/data-structures/linked-list/singly-linked-list.ts +3 -0
- package/src/types/data-structures/linked-list/skip-linked-list.ts +1 -0
- package/src/types/data-structures/matrix/index.ts +2 -0
- package/src/types/data-structures/matrix/matrix.ts +7 -0
- package/src/types/data-structures/matrix/navigator.ts +14 -0
- package/src/types/data-structures/priority-queue/index.ts +3 -0
- package/src/types/data-structures/priority-queue/max-priority-queue.ts +1 -0
- package/src/types/data-structures/priority-queue/min-priority-queue.ts +1 -0
- package/src/types/data-structures/priority-queue/priority-queue.ts +3 -0
- package/src/types/data-structures/queue/deque.ts +5 -0
- package/src/types/data-structures/queue/index.ts +2 -0
- package/src/types/data-structures/queue/queue.ts +5 -0
- package/src/types/data-structures/stack/index.ts +1 -0
- package/src/types/data-structures/stack/stack.ts +3 -0
- package/src/types/data-structures/tree/index.ts +1 -0
- package/src/types/data-structures/tree/tree.ts +1 -0
- package/src/types/data-structures/trie/index.ts +1 -0
- package/src/types/data-structures/trie/trie.ts +3 -0
- package/src/types/index.ts +3 -0
- package/src/types/utils/index.ts +2 -0
- package/src/types/utils/utils.ts +33 -0
- package/src/types/utils/validate-type.ts +35 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/number.ts +22 -0
- package/src/utils/utils.ts +350 -0
- package/test/index.test.ts +111 -0
- package/tsconfig.base.json +23 -0
- package/tsconfig.json +12 -0
- package/tsconfig.test.json +8 -0
- package/tsconfig.types.json +15 -0
- package/tsup.config.js +28 -0
- package/tsup.node.config.js +71 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* data-structure-typed
|
|
3
|
+
*
|
|
4
|
+
* @author Pablo Zeng
|
|
5
|
+
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
|
|
6
|
+
* @license MIT License
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { GraphOptions, TopologicalStatus, VertexKey } from '../../types';
|
|
10
|
+
import { AbstractEdge, AbstractGraph, AbstractVertex } from './abstract-graph';
|
|
11
|
+
import { IGraph } from '../../interfaces';
|
|
12
|
+
import { arrayRemove } from '../../utils';
|
|
13
|
+
|
|
14
|
+
export class DirectedVertex<V = any> extends AbstractVertex<V> {
|
|
15
|
+
constructor(key: VertexKey, value?: V) {
|
|
16
|
+
super(key, value);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class DirectedEdge<E = any> extends AbstractEdge<E> {
|
|
21
|
+
src: VertexKey;
|
|
22
|
+
dest: VertexKey;
|
|
23
|
+
|
|
24
|
+
constructor(src: VertexKey, dest: VertexKey, weight?: number, value?: E) {
|
|
25
|
+
super(weight, value);
|
|
26
|
+
this.src = src;
|
|
27
|
+
this.dest = dest;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Directed graph implementation.
|
|
33
|
+
* @template V - Vertex value type.
|
|
34
|
+
* @template E - Edge value type.
|
|
35
|
+
* @template VO - Concrete vertex class (extends AbstractVertex<V>).
|
|
36
|
+
* @template EO - Concrete edge class (extends AbstractEdge<E>).
|
|
37
|
+
* @remarks Time O(1), Space O(1)
|
|
38
|
+
* @example
|
|
39
|
+
* // basic DirectedGraph vertex and edge creation
|
|
40
|
+
* // Create a simple directed graph
|
|
41
|
+
* const graph = new DirectedGraph<string>();
|
|
42
|
+
*
|
|
43
|
+
* // Add vertices
|
|
44
|
+
* graph.addVertex('A');
|
|
45
|
+
* graph.addVertex('B');
|
|
46
|
+
* graph.addVertex('C');
|
|
47
|
+
*
|
|
48
|
+
* // Verify vertices exist
|
|
49
|
+
* console.log(graph.hasVertex('A')); // true;
|
|
50
|
+
* console.log(graph.hasVertex('B')); // true;
|
|
51
|
+
* console.log(graph.hasVertex('C')); // true;
|
|
52
|
+
* console.log(graph.hasVertex('D')); // false;
|
|
53
|
+
*
|
|
54
|
+
* // Check vertex count
|
|
55
|
+
* console.log(graph.size); // 3;
|
|
56
|
+
* @example
|
|
57
|
+
* // DirectedGraph edge operations
|
|
58
|
+
* const graph = new DirectedGraph<string>();
|
|
59
|
+
*
|
|
60
|
+
* // Add vertices
|
|
61
|
+
* graph.addVertex('A');
|
|
62
|
+
* graph.addVertex('B');
|
|
63
|
+
* graph.addVertex('C');
|
|
64
|
+
*
|
|
65
|
+
* // Add directed edges
|
|
66
|
+
* graph.addEdge('A', 'B', 1);
|
|
67
|
+
* graph.addEdge('B', 'C', 2);
|
|
68
|
+
* graph.addEdge('A', 'C', 3);
|
|
69
|
+
*
|
|
70
|
+
* // Verify edges exist
|
|
71
|
+
* console.log(graph.hasEdge('A', 'B')); // true;
|
|
72
|
+
* console.log(graph.hasEdge('B', 'C')); // true;
|
|
73
|
+
* console.log(graph.hasEdge('C', 'B')); // false; // Graph is directed
|
|
74
|
+
*
|
|
75
|
+
* // Get neighbors of A
|
|
76
|
+
* const neighborsA = graph.getNeighbors('A');
|
|
77
|
+
* console.log(neighborsA[0].key); // 'B';
|
|
78
|
+
* console.log(neighborsA[1].key); // 'C';
|
|
79
|
+
* @example
|
|
80
|
+
* // DirectedGraph deleteEdge and vertex operations
|
|
81
|
+
* const graph = new DirectedGraph<string>();
|
|
82
|
+
*
|
|
83
|
+
* // Build a small graph
|
|
84
|
+
* graph.addVertex('X');
|
|
85
|
+
* graph.addVertex('Y');
|
|
86
|
+
* graph.addVertex('Z');
|
|
87
|
+
* graph.addEdge('X', 'Y', 1);
|
|
88
|
+
* graph.addEdge('Y', 'Z', 2);
|
|
89
|
+
*
|
|
90
|
+
* // Delete an edge
|
|
91
|
+
* graph.deleteEdgeSrcToDest('X', 'Y');
|
|
92
|
+
* console.log(graph.hasEdge('X', 'Y')); // false;
|
|
93
|
+
*
|
|
94
|
+
* // Edge in other direction should not exist
|
|
95
|
+
* console.log(graph.hasEdge('Y', 'X')); // false;
|
|
96
|
+
*
|
|
97
|
+
* // Other edges should remain
|
|
98
|
+
* console.log(graph.hasEdge('Y', 'Z')); // true;
|
|
99
|
+
*
|
|
100
|
+
* // Delete a vertex
|
|
101
|
+
* graph.deleteVertex('Y');
|
|
102
|
+
* console.log(graph.hasVertex('Y')); // false;
|
|
103
|
+
* console.log(graph.size); // 2;
|
|
104
|
+
* @example
|
|
105
|
+
* // DirectedGraph topologicalSort for task scheduling
|
|
106
|
+
* const graph = new DirectedGraph<string>();
|
|
107
|
+
*
|
|
108
|
+
* // Build a DAG (Directed Acyclic Graph) for task dependencies
|
|
109
|
+
* graph.addVertex('Design');
|
|
110
|
+
* graph.addVertex('Implement');
|
|
111
|
+
* graph.addVertex('Test');
|
|
112
|
+
* graph.addVertex('Deploy');
|
|
113
|
+
*
|
|
114
|
+
* // Add dependency edges
|
|
115
|
+
* graph.addEdge('Design', 'Implement', 1); // Design must come before Implement
|
|
116
|
+
* graph.addEdge('Implement', 'Test', 1); // Implement must come before Test
|
|
117
|
+
* graph.addEdge('Test', 'Deploy', 1); // Test must come before Deploy
|
|
118
|
+
*
|
|
119
|
+
* // Topological sort gives valid execution order
|
|
120
|
+
* const executionOrder = graph.topologicalSort();
|
|
121
|
+
* console.log(executionOrder); // defined;
|
|
122
|
+
* console.log(executionOrder); // ['Design', 'Implement', 'Test', 'Deploy'];
|
|
123
|
+
*
|
|
124
|
+
* // All vertices should be included
|
|
125
|
+
* console.log(executionOrder?.length); // 4;
|
|
126
|
+
* @example
|
|
127
|
+
* // DirectedGraph dijkstra shortest path for network routing
|
|
128
|
+
* // Build a weighted directed graph representing network nodes and costs
|
|
129
|
+
* const network = new DirectedGraph<string>();
|
|
130
|
+
*
|
|
131
|
+
* // Add network nodes
|
|
132
|
+
* network.addVertex('Router-A');
|
|
133
|
+
* network.addVertex('Router-B');
|
|
134
|
+
* network.addVertex('Router-C');
|
|
135
|
+
* network.addVertex('Router-D');
|
|
136
|
+
* network.addVertex('Router-E');
|
|
137
|
+
*
|
|
138
|
+
* // Add weighted edges (network latency costs)
|
|
139
|
+
* network.addEdge('Router-A', 'Router-B', 5);
|
|
140
|
+
* network.addEdge('Router-A', 'Router-C', 10);
|
|
141
|
+
* network.addEdge('Router-B', 'Router-D', 3);
|
|
142
|
+
* network.addEdge('Router-C', 'Router-D', 2);
|
|
143
|
+
* network.addEdge('Router-D', 'Router-E', 4);
|
|
144
|
+
* network.addEdge('Router-B', 'Router-E', 12);
|
|
145
|
+
*
|
|
146
|
+
* // Find shortest path from Router-A to Router-E
|
|
147
|
+
* const { minDist, minPath } = network.dijkstra('Router-A', 'Router-E', true, true) || {
|
|
148
|
+
* minDist: undefined,
|
|
149
|
+
* minPath: undefined
|
|
150
|
+
* };
|
|
151
|
+
*
|
|
152
|
+
* // Verify shortest path is found
|
|
153
|
+
* console.log(minDist); // defined;
|
|
154
|
+
* console.log(minPath); // defined;
|
|
155
|
+
*
|
|
156
|
+
* // Shortest path should be A -> B -> D -> E with cost 5+3+4=12
|
|
157
|
+
* // Or A -> C -> D -> E with cost 10+2+4=16
|
|
158
|
+
* // So the minimum is 12
|
|
159
|
+
* console.log(minDist); // <= 16;
|
|
160
|
+
*
|
|
161
|
+
* // Verify path is valid (includes start and end)
|
|
162
|
+
* console.log(minPath?.[0].key); // 'Router-A';
|
|
163
|
+
* console.log(minPath?.[minPath.length - 1].key); // 'Router-E';
|
|
164
|
+
*/
|
|
165
|
+
export class DirectedGraph<
|
|
166
|
+
V = any,
|
|
167
|
+
E = any,
|
|
168
|
+
VO extends DirectedVertex<V> = DirectedVertex<V>,
|
|
169
|
+
EO extends DirectedEdge<E> = DirectedEdge<E>
|
|
170
|
+
>
|
|
171
|
+
extends AbstractGraph<V, E, VO, EO>
|
|
172
|
+
implements IGraph<V, E, VO, EO>
|
|
173
|
+
{
|
|
174
|
+
/**
|
|
175
|
+
* Construct a directed graph with runtime defaults.
|
|
176
|
+
* @param options - `GraphOptions<V>` (e.g. `vertexValueInitializer`, `defaultEdgeWeight`).
|
|
177
|
+
* @remarks Time O(1), Space O(1)
|
|
178
|
+
*/
|
|
179
|
+
constructor(options?: Partial<GraphOptions<V>>) {
|
|
180
|
+
super(options);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
protected _outEdgeMap: Map<VO, EO[]> = new Map<VO, EO[]>();
|
|
184
|
+
|
|
185
|
+
get outEdgeMap(): Map<VO, EO[]> {
|
|
186
|
+
return this._outEdgeMap;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
set outEdgeMap(v: Map<VO, EO[]>) {
|
|
190
|
+
this._outEdgeMap = v;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected _inEdgeMap: Map<VO, EO[]> = new Map<VO, EO[]>();
|
|
194
|
+
|
|
195
|
+
get inEdgeMap(): Map<VO, EO[]> {
|
|
196
|
+
return this._inEdgeMap;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
set inEdgeMap(v: Map<VO, EO[]>) {
|
|
200
|
+
this._inEdgeMap = v;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Construct a directed graph from keys with value initializer `v => v`.
|
|
205
|
+
* @template K - Vertex key type.
|
|
206
|
+
* @param keys - Iterable of vertex keys.
|
|
207
|
+
* @returns DirectedGraph with all keys added.
|
|
208
|
+
* @remarks Time O(V), Space O(V)
|
|
209
|
+
*/
|
|
210
|
+
static fromKeys<K extends VertexKey>(keys: Iterable<K>): DirectedGraph<K, any, DirectedVertex<K>, DirectedEdge<any>> {
|
|
211
|
+
const g: DirectedGraph<K, any, DirectedVertex<K>, DirectedEdge<any>> = new DirectedGraph<K, any>({
|
|
212
|
+
vertexValueInitializer: (k: VertexKey) => k as K
|
|
213
|
+
});
|
|
214
|
+
for (const k of keys) g.addVertex(k);
|
|
215
|
+
return g;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Construct a directed graph from `[key, value]` entries.
|
|
220
|
+
* @template V - Vertex value type.
|
|
221
|
+
* @param entries - Iterable of `[key, value]` pairs.
|
|
222
|
+
* @returns DirectedGraph with all vertices added.
|
|
223
|
+
* @remarks Time O(V), Space O(V)
|
|
224
|
+
*/
|
|
225
|
+
static fromEntries<V>(
|
|
226
|
+
entries: Iterable<[VertexKey, V]>
|
|
227
|
+
): DirectedGraph<V, any, DirectedVertex<V>, DirectedEdge<any>> {
|
|
228
|
+
const g: DirectedGraph<V, any, DirectedVertex<V>, DirectedEdge<any>> = new DirectedGraph<V, any>();
|
|
229
|
+
for (const [k, v] of entries) g.addVertex(k, v);
|
|
230
|
+
return g;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create a directed vertex instance. Does not insert into the graph.
|
|
235
|
+
* @param key - Vertex identifier.
|
|
236
|
+
* @param value - Optional payload.
|
|
237
|
+
* @returns Concrete vertex instance.
|
|
238
|
+
* @remarks Time O(1), Space O(1)
|
|
239
|
+
*/
|
|
240
|
+
createVertex(key: VertexKey, value?: VO['value']): VO {
|
|
241
|
+
return new DirectedVertex(key, value) as VO;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Create a directed edge instance. Does not insert into the graph.
|
|
246
|
+
* @param src - Source vertex key.
|
|
247
|
+
* @param dest - Destination vertex key.
|
|
248
|
+
* @param weight - Edge weight; defaults to `defaultEdgeWeight`.
|
|
249
|
+
* @param value - Edge payload.
|
|
250
|
+
* @returns Concrete edge instance.
|
|
251
|
+
* @remarks Time O(1), Space O(1)
|
|
252
|
+
*/
|
|
253
|
+
createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO {
|
|
254
|
+
return new DirectedEdge(src, dest, weight ?? this.options.defaultEdgeWeight ?? 1, value) as EO;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Get the unique edge from `src` to `dest`, if present.
|
|
259
|
+
* @param srcOrKey - Source vertex or key.
|
|
260
|
+
* @param destOrKey - Destination vertex or key.
|
|
261
|
+
* @returns Edge instance or `undefined`.
|
|
262
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
263
|
+
*/
|
|
264
|
+
getEdge(srcOrKey: VO | VertexKey | undefined, destOrKey: VO | VertexKey | undefined): EO | undefined {
|
|
265
|
+
let edgeMap: EO[] = [];
|
|
266
|
+
|
|
267
|
+
if (srcOrKey !== undefined && destOrKey !== undefined) {
|
|
268
|
+
const src: VO | undefined = this._getVertex(srcOrKey);
|
|
269
|
+
const dest: VO | undefined = this._getVertex(destOrKey);
|
|
270
|
+
|
|
271
|
+
if (src && dest) {
|
|
272
|
+
const srcOutEdges = this._outEdgeMap.get(src);
|
|
273
|
+
if (srcOutEdges) {
|
|
274
|
+
edgeMap = srcOutEdges.filter(edge => edge.dest === dest.key);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return edgeMap[0] || undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Delete edge `src -> dest` if present.
|
|
284
|
+
* @param srcOrKey - Source vertex or key.
|
|
285
|
+
* @param destOrKey - Destination vertex or key.
|
|
286
|
+
* @returns Removed edge or `undefined`.
|
|
287
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
288
|
+
*/
|
|
289
|
+
deleteEdgeSrcToDest(srcOrKey: VO | VertexKey, destOrKey: VO | VertexKey): EO | undefined {
|
|
290
|
+
const src: VO | undefined = this._getVertex(srcOrKey);
|
|
291
|
+
const dest: VO | undefined = this._getVertex(destOrKey);
|
|
292
|
+
let removed: EO | undefined = undefined;
|
|
293
|
+
if (!src || !dest) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const srcOutEdges = this._outEdgeMap.get(src);
|
|
298
|
+
if (srcOutEdges) {
|
|
299
|
+
arrayRemove<EO>(srcOutEdges, (edge: EO) => edge.dest === dest.key);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const destInEdges = this._inEdgeMap.get(dest);
|
|
303
|
+
if (destInEdges) {
|
|
304
|
+
removed = arrayRemove<EO>(destInEdges, (edge: EO) => edge.src === src.key)[0] || undefined;
|
|
305
|
+
}
|
|
306
|
+
return removed;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Delete an edge by instance or by `(srcKey, destKey)`.
|
|
311
|
+
* @param edgeOrSrcVertexKey - Edge instance or source vertex/key.
|
|
312
|
+
* @param destVertexKey - Optional destination vertex/key when deleting by pair.
|
|
313
|
+
* @returns Removed edge or `undefined`.
|
|
314
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
315
|
+
*/
|
|
316
|
+
deleteEdge(edgeOrSrcVertexKey: EO | VertexKey, destVertexKey?: VertexKey): EO | undefined {
|
|
317
|
+
let removed: EO | undefined = undefined;
|
|
318
|
+
let src: VO | undefined, dest: VO | undefined;
|
|
319
|
+
if (this.isVertexKey(edgeOrSrcVertexKey)) {
|
|
320
|
+
if (this.isVertexKey(destVertexKey)) {
|
|
321
|
+
src = this._getVertex(edgeOrSrcVertexKey);
|
|
322
|
+
dest = this._getVertex(destVertexKey);
|
|
323
|
+
} else {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
src = this._getVertex(edgeOrSrcVertexKey.src);
|
|
328
|
+
dest = this._getVertex(edgeOrSrcVertexKey.dest);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (src && dest) {
|
|
332
|
+
const srcOutEdges = this._outEdgeMap.get(src);
|
|
333
|
+
if (srcOutEdges && srcOutEdges.length > 0) {
|
|
334
|
+
arrayRemove(srcOutEdges, (edge: EO) => edge.src === src!.key && edge.dest === dest?.key);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const destInEdges = this._inEdgeMap.get(dest);
|
|
338
|
+
if (destInEdges && destInEdges.length > 0) {
|
|
339
|
+
removed = arrayRemove(destInEdges, (edge: EO) => edge.src === src!.key && edge.dest === dest!.key)[0];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return removed;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
deleteVertex(vertexOrKey: VO | VertexKey): boolean {
|
|
347
|
+
let vertexKey: VertexKey;
|
|
348
|
+
let vertex: VO | undefined;
|
|
349
|
+
if (this.isVertexKey(vertexOrKey)) {
|
|
350
|
+
vertex = this.getVertex(vertexOrKey);
|
|
351
|
+
vertexKey = vertexOrKey;
|
|
352
|
+
} else {
|
|
353
|
+
vertex = vertexOrKey;
|
|
354
|
+
vertexKey = this._getVertexKey(vertexOrKey);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (vertex) {
|
|
358
|
+
/**
|
|
359
|
+
* One-step neighbors following outgoing edges.
|
|
360
|
+
* @param vertexOrKey - Vertex or key.
|
|
361
|
+
* @returns Array of neighbor vertices.
|
|
362
|
+
* @remarks Time O(deg_out), Space O(deg_out)
|
|
363
|
+
*/
|
|
364
|
+
const neighbors = this.getNeighbors(vertex);
|
|
365
|
+
for (const neighbor of neighbors) {
|
|
366
|
+
this.deleteEdgeSrcToDest(vertex, neighbor);
|
|
367
|
+
}
|
|
368
|
+
this._outEdgeMap.delete(vertex);
|
|
369
|
+
this._inEdgeMap.delete(vertex);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return this._vertexMap.delete(vertexKey);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
deleteEdgesBetween(v1: VertexKey | VO, v2: VertexKey | VO): EO[] {
|
|
376
|
+
const removed: EO[] = [];
|
|
377
|
+
|
|
378
|
+
if (v1 && v2) {
|
|
379
|
+
const v1ToV2 = this.deleteEdgeSrcToDest(v1, v2);
|
|
380
|
+
const v2ToV1 = this.deleteEdgeSrcToDest(v2, v1);
|
|
381
|
+
|
|
382
|
+
if (v1ToV2) removed.push(v1ToV2);
|
|
383
|
+
if (v2ToV1) removed.push(v2ToV1);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return removed;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Incoming edges of a vertex.
|
|
391
|
+
* @param vertexOrKey - Vertex or key.
|
|
392
|
+
* @returns Array of incoming edges.
|
|
393
|
+
* @remarks Time O(deg_in), Space O(deg_in)
|
|
394
|
+
*/
|
|
395
|
+
incomingEdgesOf(vertexOrKey: VO | VertexKey): EO[] {
|
|
396
|
+
const target = this._getVertex(vertexOrKey);
|
|
397
|
+
if (target) {
|
|
398
|
+
return this.inEdgeMap.get(target) || [];
|
|
399
|
+
}
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Outgoing edges of a vertex.
|
|
405
|
+
* @param vertexOrKey - Vertex or key.
|
|
406
|
+
* @returns Array of outgoing edges.
|
|
407
|
+
* @remarks Time O(deg_out), Space O(deg_out)
|
|
408
|
+
*/
|
|
409
|
+
outgoingEdgesOf(vertexOrKey: VO | VertexKey): EO[] {
|
|
410
|
+
const target = this._getVertex(vertexOrKey);
|
|
411
|
+
if (target) {
|
|
412
|
+
return this._outEdgeMap.get(target) || [];
|
|
413
|
+
}
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Degree (in + out) of a vertex.
|
|
419
|
+
* @param vertexOrKey - Vertex or key.
|
|
420
|
+
* @returns Non-negative integer.
|
|
421
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
422
|
+
*/
|
|
423
|
+
degreeOf(vertexOrKey: VertexKey | VO): number {
|
|
424
|
+
/**
|
|
425
|
+
* In-degree of a vertex.
|
|
426
|
+
* @param vertexOrKey - Vertex or key.
|
|
427
|
+
* @returns Non-negative integer.
|
|
428
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
429
|
+
*/
|
|
430
|
+
/**
|
|
431
|
+
* Out-degree of a vertex.
|
|
432
|
+
* @param vertexOrKey - Vertex or key.
|
|
433
|
+
* @returns Non-negative integer.
|
|
434
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
435
|
+
*/
|
|
436
|
+
return this.outDegreeOf(vertexOrKey) + this.inDegreeOf(vertexOrKey);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
inDegreeOf(vertexOrKey: VertexKey | VO): number {
|
|
440
|
+
return this.incomingEdgesOf(vertexOrKey).length;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
outDegreeOf(vertexOrKey: VertexKey | VO): number {
|
|
444
|
+
return this.outgoingEdgesOf(vertexOrKey).length;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* All incident edges of a vertex.
|
|
449
|
+
* @param vertexOrKey - Vertex or key.
|
|
450
|
+
* @returns Array of incident edges.
|
|
451
|
+
* @remarks Time O(deg_in + deg_out), Space O(deg_in + deg_out)
|
|
452
|
+
*/
|
|
453
|
+
edgesOf(vertexOrKey: VertexKey | VO): EO[] {
|
|
454
|
+
return [...this.outgoingEdgesOf(vertexOrKey), ...this.incomingEdgesOf(vertexOrKey)];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
getEdgeSrc(e: EO): VO | undefined {
|
|
458
|
+
return this._getVertex(e.src);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
getEdgeDest(e: EO): VO | undefined {
|
|
462
|
+
return this._getVertex(e.dest);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Direct children reachable by one outgoing edge.
|
|
467
|
+
* @param vertex - Vertex or key.
|
|
468
|
+
* @returns Array of neighbor vertices.
|
|
469
|
+
* @remarks Time O(deg_out), Space O(deg_out)
|
|
470
|
+
*/
|
|
471
|
+
getDestinations(vertex: VO | VertexKey | undefined): VO[] {
|
|
472
|
+
if (vertex === undefined) {
|
|
473
|
+
return [];
|
|
474
|
+
}
|
|
475
|
+
const destinations: VO[] = [];
|
|
476
|
+
const outgoingEdges = this.outgoingEdgesOf(vertex);
|
|
477
|
+
for (const outEdge of outgoingEdges) {
|
|
478
|
+
const child = this.getEdgeDest(outEdge);
|
|
479
|
+
if (child) {
|
|
480
|
+
destinations.push(child);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return destinations;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Topological sort if DAG; returns `undefined` if a cycle exists.
|
|
488
|
+
* @param propertyName - `'key'` to map to keys; `'vertex'` to keep instances.
|
|
489
|
+
* @returns Array of keys/vertices, or `undefined` when cycle is found.
|
|
490
|
+
* @remarks Time O(V + E), Space O(V)
|
|
491
|
+
*/
|
|
492
|
+
topologicalSort(propertyName?: 'vertex' | 'key'): Array<VO | VertexKey> | undefined {
|
|
493
|
+
propertyName = propertyName ?? 'key';
|
|
494
|
+
|
|
495
|
+
const statusMap: Map<VO | VertexKey, TopologicalStatus> = new Map<VO | VertexKey, TopologicalStatus>();
|
|
496
|
+
for (const entry of this.vertexMap) {
|
|
497
|
+
statusMap.set(entry[1], 0);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
let sorted: (VO | VertexKey)[] = [];
|
|
501
|
+
let hasCycle = false;
|
|
502
|
+
const dfs = (cur: VO | VertexKey) => {
|
|
503
|
+
statusMap.set(cur, 1);
|
|
504
|
+
const children = this.getDestinations(cur);
|
|
505
|
+
for (const child of children) {
|
|
506
|
+
const childStatus = statusMap.get(child);
|
|
507
|
+
if (childStatus === 0) {
|
|
508
|
+
dfs(child);
|
|
509
|
+
} else if (childStatus === 1) {
|
|
510
|
+
hasCycle = true;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
statusMap.set(cur, 2);
|
|
514
|
+
sorted.push(cur);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
for (const entry of this.vertexMap) {
|
|
518
|
+
if (statusMap.get(entry[1]) === 0) {
|
|
519
|
+
dfs(entry[1]);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (hasCycle) return undefined;
|
|
524
|
+
|
|
525
|
+
if (propertyName === 'key') sorted = sorted.map(vertex => (vertex instanceof DirectedVertex ? vertex.key : vertex));
|
|
526
|
+
return sorted.reverse();
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
edgeSet(): EO[] {
|
|
530
|
+
let edgeMap: EO[] = [];
|
|
531
|
+
this._outEdgeMap.forEach(outEdges => {
|
|
532
|
+
edgeMap = [...edgeMap, ...outEdges];
|
|
533
|
+
});
|
|
534
|
+
return edgeMap;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
getNeighbors(vertexOrKey: VO | VertexKey): VO[] {
|
|
538
|
+
const neighbors: VO[] = [];
|
|
539
|
+
const vertex = this._getVertex(vertexOrKey);
|
|
540
|
+
if (vertex) {
|
|
541
|
+
const outEdges = this.outgoingEdgesOf(vertex);
|
|
542
|
+
for (const outEdge of outEdges) {
|
|
543
|
+
const neighbor = this._getVertex(outEdge.dest);
|
|
544
|
+
|
|
545
|
+
if (neighbor) {
|
|
546
|
+
neighbors.push(neighbor);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return neighbors;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Resolve an edge's `[src, dest]` endpoints to vertex instances.
|
|
555
|
+
* @param edge - Edge instance.
|
|
556
|
+
* @returns `[src, dest]` or `undefined` if either endpoint is missing.
|
|
557
|
+
* @remarks Time O(1), Space O(1)
|
|
558
|
+
*/
|
|
559
|
+
getEndsOfEdge(edge: EO): [VO, VO] | undefined {
|
|
560
|
+
if (!this.hasEdge(edge.src, edge.dest)) {
|
|
561
|
+
return undefined;
|
|
562
|
+
}
|
|
563
|
+
const v1 = this._getVertex(edge.src);
|
|
564
|
+
const v2 = this._getVertex(edge.dest);
|
|
565
|
+
if (v1 && v2) {
|
|
566
|
+
return [v1, v2];
|
|
567
|
+
} else {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Whether the graph has no vertices and no edges.
|
|
574
|
+
* @remarks Time O(1), Space O(1)
|
|
575
|
+
*/
|
|
576
|
+
isEmpty(): boolean {
|
|
577
|
+
return this.vertexMap.size === 0 && this.inEdgeMap.size === 0 && this.outEdgeMap.size === 0;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Remove all vertices and edges.
|
|
582
|
+
* @remarks Time O(V + E), Space O(1)
|
|
583
|
+
*/
|
|
584
|
+
clear() {
|
|
585
|
+
this._vertexMap = new Map<VertexKey, VO>();
|
|
586
|
+
this._inEdgeMap = new Map<VO, EO[]>();
|
|
587
|
+
this._outEdgeMap = new Map<VO, EO[]>();
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Deep clone as the same concrete class.
|
|
592
|
+
* @returns A new graph of the same concrete class (`this` type).
|
|
593
|
+
* @remarks Time O(V + E), Space O(V + E)
|
|
594
|
+
*/
|
|
595
|
+
override clone(): this {
|
|
596
|
+
return super.clone();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Tarjan's algorithm for strongly connected components.
|
|
601
|
+
* @returns `{ dfnMap, lowMap, SCCs }`.
|
|
602
|
+
* @remarks Time O(V + E), Space O(V + E)
|
|
603
|
+
*/
|
|
604
|
+
tarjan(): { dfnMap: Map<VO, number>; lowMap: Map<VO, number>; SCCs: Map<number, VO[]> } {
|
|
605
|
+
const dfnMap = new Map<VO, number>();
|
|
606
|
+
const lowMap = new Map<VO, number>();
|
|
607
|
+
const SCCs = new Map<number, VO[]>();
|
|
608
|
+
|
|
609
|
+
let time = 0;
|
|
610
|
+
|
|
611
|
+
const stack: VO[] = [];
|
|
612
|
+
const inStack: Set<VO> = new Set();
|
|
613
|
+
|
|
614
|
+
const dfs = (vertex: VO) => {
|
|
615
|
+
dfnMap.set(vertex, time);
|
|
616
|
+
lowMap.set(vertex, time);
|
|
617
|
+
time++;
|
|
618
|
+
|
|
619
|
+
stack.push(vertex);
|
|
620
|
+
inStack.add(vertex);
|
|
621
|
+
|
|
622
|
+
const neighbors = this.getNeighbors(vertex);
|
|
623
|
+
for (const neighbor of neighbors) {
|
|
624
|
+
if (!dfnMap.has(neighbor)) {
|
|
625
|
+
dfs(neighbor);
|
|
626
|
+
lowMap.set(vertex, Math.min(lowMap.get(vertex)!, lowMap.get(neighbor)!));
|
|
627
|
+
} else if (inStack.has(neighbor)) {
|
|
628
|
+
lowMap.set(vertex, Math.min(lowMap.get(vertex)!, dfnMap.get(neighbor)!));
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (dfnMap.get(vertex) === lowMap.get(vertex)) {
|
|
633
|
+
const SCC: VO[] = [];
|
|
634
|
+
let poppedVertex: VO | undefined;
|
|
635
|
+
|
|
636
|
+
do {
|
|
637
|
+
poppedVertex = stack.pop();
|
|
638
|
+
inStack.delete(poppedVertex!);
|
|
639
|
+
SCC.push(poppedVertex!);
|
|
640
|
+
} while (poppedVertex !== vertex);
|
|
641
|
+
|
|
642
|
+
SCCs.set(SCCs.size, SCC);
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
for (const vertex of this.vertexMap.values()) {
|
|
647
|
+
if (!dfnMap.has(vertex)) {
|
|
648
|
+
dfs(vertex);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return { dfnMap, lowMap, SCCs };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* DFN index map computed by `tarjan()`.
|
|
657
|
+
* @returns Map from vertex to DFN index.
|
|
658
|
+
* @remarks Time O(V), Space O(V)
|
|
659
|
+
*/
|
|
660
|
+
getDFNMap(): Map<VO, number> {
|
|
661
|
+
return this.tarjan().dfnMap;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* LOW link map computed by `tarjan()`.
|
|
666
|
+
* @returns Map from vertex to LOW value.
|
|
667
|
+
* @remarks Time O(V), Space O(V)
|
|
668
|
+
*/
|
|
669
|
+
getLowMap(): Map<VO, number> {
|
|
670
|
+
return this.tarjan().lowMap;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Strongly connected components computed by `tarjan()`.
|
|
675
|
+
* @returns Map from SCC id to vertices.
|
|
676
|
+
* @remarks Time O(#SCC + V), Space O(V)
|
|
677
|
+
*/
|
|
678
|
+
getSCCs(): Map<number, VO[]> {
|
|
679
|
+
return this.tarjan().SCCs;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Internal hook to attach a directed edge into adjacency maps.
|
|
684
|
+
* @param edge - Edge instance.
|
|
685
|
+
* @returns `true` if inserted; otherwise `false`.
|
|
686
|
+
* @remarks Time O(1) avg, Space O(1)
|
|
687
|
+
*/
|
|
688
|
+
protected _addEdge(edge: EO): boolean {
|
|
689
|
+
if (!(this.hasVertex(edge.src) && this.hasVertex(edge.dest))) {
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const srcVertex = this._getVertex(edge.src);
|
|
694
|
+
const destVertex = this._getVertex(edge.dest);
|
|
695
|
+
|
|
696
|
+
if (srcVertex && destVertex) {
|
|
697
|
+
const srcOutEdges = this._outEdgeMap.get(srcVertex);
|
|
698
|
+
if (srcOutEdges) {
|
|
699
|
+
srcOutEdges.push(edge);
|
|
700
|
+
} else {
|
|
701
|
+
this._outEdgeMap.set(srcVertex, [edge]);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const destInEdges = this._inEdgeMap.get(destVertex);
|
|
705
|
+
if (destInEdges) {
|
|
706
|
+
destInEdges.push(edge);
|
|
707
|
+
} else {
|
|
708
|
+
this._inEdgeMap.set(destVertex, [edge]);
|
|
709
|
+
}
|
|
710
|
+
return true;
|
|
711
|
+
} else {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|