stack-typed 2.2.2 → 2.2.3
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/README.md +66 -46
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +96 -2
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
- package/dist/types/data-structures/binary-tree/bst.d.ts +156 -13
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +84 -35
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
- package/dist/types/data-structures/graph/directed-graph.d.ts +126 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +160 -1
- package/dist/types/data-structures/hash/hash-map.d.ts +110 -27
- package/dist/types/data-structures/heap/heap.d.ts +107 -58
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +72 -404
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +121 -5
- package/dist/types/data-structures/queue/deque.d.ts +95 -67
- package/dist/types/data-structures/queue/queue.d.ts +90 -34
- package/dist/types/data-structures/stack/stack.d.ts +58 -40
- package/dist/types/data-structures/trie/trie.d.ts +109 -47
- package/dist/types/interfaces/binary-tree.d.ts +1 -0
- package/dist/umd/stack-typed.js.map +1 -1
- package/dist/umd/stack-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/binary-tree/avl-tree.ts +96 -2
- package/src/data-structures/binary-tree/binary-tree.ts +117 -7
- package/src/data-structures/binary-tree/bst.ts +322 -13
- package/src/data-structures/binary-tree/red-black-tree.ts +84 -35
- package/src/data-structures/binary-tree/tree-multi-map.ts +2 -2
- package/src/data-structures/graph/directed-graph.ts +126 -1
- package/src/data-structures/graph/undirected-graph.ts +160 -1
- package/src/data-structures/hash/hash-map.ts +110 -27
- package/src/data-structures/heap/heap.ts +107 -58
- package/src/data-structures/linked-list/doubly-linked-list.ts +72 -404
- package/src/data-structures/linked-list/singly-linked-list.ts +121 -5
- package/src/data-structures/queue/deque.ts +95 -67
- package/src/data-structures/queue/queue.ts +90 -34
- package/src/data-structures/stack/stack.ts +58 -40
- package/src/data-structures/trie/trie.ts +109 -47
- package/src/interfaces/binary-tree.ts +2 -0
|
@@ -201,8 +201,57 @@ export class BSTNode<K = any, V = any> {
|
|
|
201
201
|
* 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves.
|
|
202
202
|
*
|
|
203
203
|
* @example
|
|
204
|
+
* // basic BST creation and add operation
|
|
205
|
+
* // Create a simple BST with numeric keys
|
|
206
|
+
* const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
207
|
+
*
|
|
208
|
+
* bst.print();
|
|
209
|
+
* // _______8__________
|
|
210
|
+
* // / \
|
|
211
|
+
* // ___4___ ____12_____
|
|
212
|
+
* // / \ / \
|
|
213
|
+
* // _2_ _6_ _10__ _14__
|
|
214
|
+
* // / \ / \ / \ / \
|
|
215
|
+
* // 1 3 5 7 9 11 13 15__
|
|
216
|
+
* // \
|
|
217
|
+
* // 16
|
|
218
|
+
*
|
|
219
|
+
* // Verify size
|
|
220
|
+
* console.log(bst.size); // 16;
|
|
221
|
+
*
|
|
222
|
+
* // Add new elements
|
|
223
|
+
* bst.add(17);
|
|
224
|
+
* bst.add(0);
|
|
225
|
+
* console.log(bst.size); // 18;
|
|
226
|
+
*
|
|
227
|
+
* // Verify keys are searchable
|
|
228
|
+
* console.log(bst.has(11)); // true;
|
|
229
|
+
* console.log(bst.has(100)); // false;
|
|
230
|
+
* @example
|
|
231
|
+
* // BST delete and search after deletion
|
|
232
|
+
* const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
233
|
+
*
|
|
234
|
+
* // Delete a leaf node
|
|
235
|
+
* bst.delete(1);
|
|
236
|
+
* console.log(bst.has(1)); // false;
|
|
237
|
+
*
|
|
238
|
+
* // Delete a node with one child
|
|
239
|
+
* bst.delete(2);
|
|
240
|
+
* console.log(bst.has(2)); // false;
|
|
241
|
+
*
|
|
242
|
+
* // Delete a node with two children
|
|
243
|
+
* bst.delete(3);
|
|
244
|
+
* console.log(bst.has(3)); // false;
|
|
245
|
+
*
|
|
246
|
+
* // Size decreases with each deletion
|
|
247
|
+
* console.log(bst.size); // 13;
|
|
248
|
+
*
|
|
249
|
+
* // Other nodes remain searchable
|
|
250
|
+
* console.log(bst.has(11)); // true;
|
|
251
|
+
* console.log(bst.has(15)); // true;
|
|
252
|
+
* @example
|
|
204
253
|
* // Merge 3 sorted datasets
|
|
205
|
-
*
|
|
254
|
+
* const dataset1 = new BST<number, string>([
|
|
206
255
|
* [1, 'A'],
|
|
207
256
|
* [7, 'G']
|
|
208
257
|
* ]);
|
|
@@ -222,18 +271,58 @@ export class BSTNode<K = any, V = any> {
|
|
|
222
271
|
* merged.merge(dataset3);
|
|
223
272
|
*
|
|
224
273
|
* // Verify merged dataset is in sorted order
|
|
225
|
-
* console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
|
|
274
|
+
* console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
|
|
226
275
|
* @example
|
|
227
|
-
* //
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
276
|
+
* // BST with custom objects for expression evaluation
|
|
277
|
+
* interface Expression {
|
|
278
|
+
* id: number;
|
|
279
|
+
* operator: string;
|
|
280
|
+
* precedence: number;
|
|
281
|
+
* }
|
|
282
|
+
*
|
|
283
|
+
* // BST efficiently stores and retrieves operators by precedence
|
|
284
|
+
* const operatorTree = new BST<number, Expression>(
|
|
285
|
+
* [
|
|
286
|
+
* [1, { id: 1, operator: '+', precedence: 1 }],
|
|
287
|
+
* [2, { id: 2, operator: '*', precedence: 2 }],
|
|
288
|
+
* [3, { id: 3, operator: '/', precedence: 2 }],
|
|
289
|
+
* [4, { id: 4, operator: '-', precedence: 1 }],
|
|
290
|
+
* [5, { id: 5, operator: '^', precedence: 3 }]
|
|
291
|
+
* ],
|
|
292
|
+
* { isMapMode: false }
|
|
293
|
+
* );
|
|
294
|
+
*
|
|
295
|
+
* console.log(operatorTree.size); // 5;
|
|
296
|
+
*
|
|
297
|
+
* // Quick lookup of operators
|
|
298
|
+
* const mult = operatorTree.get(2);
|
|
299
|
+
* console.log(mult?.operator); // '*';
|
|
300
|
+
* console.log(mult?.precedence); // 2;
|
|
301
|
+
*
|
|
302
|
+
* // Check if operator exists
|
|
303
|
+
* console.log(operatorTree.has(5)); // true;
|
|
304
|
+
* console.log(operatorTree.has(99)); // false;
|
|
305
|
+
*
|
|
306
|
+
* // Retrieve operator by precedence level
|
|
307
|
+
* const expNode = operatorTree.getNode(3);
|
|
308
|
+
* console.log(expNode?.key); // 3;
|
|
309
|
+
* console.log(expNode?.value?.precedence); // 2;
|
|
310
|
+
*
|
|
311
|
+
* // Delete operator and verify
|
|
312
|
+
* operatorTree.delete(1);
|
|
313
|
+
* console.log(operatorTree.has(1)); // false;
|
|
314
|
+
* console.log(operatorTree.size); // 4;
|
|
315
|
+
*
|
|
316
|
+
* // Get tree height for optimization analysis
|
|
317
|
+
* const treeHeight = operatorTree.getHeight();
|
|
318
|
+
* console.log(treeHeight); // > 0;
|
|
319
|
+
*
|
|
320
|
+
* // Remaining operators are still accessible
|
|
321
|
+
* const remaining = operatorTree.get(2);
|
|
322
|
+
* console.log(remaining); // defined;
|
|
234
323
|
* @example
|
|
235
324
|
* // Find lowest common ancestor
|
|
236
|
-
*
|
|
325
|
+
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
|
237
326
|
*
|
|
238
327
|
* // LCA helper function
|
|
239
328
|
* const findLCA = (num1: number, num2: number): number | undefined => {
|
|
@@ -253,9 +342,9 @@ export class BSTNode<K = any, V = any> {
|
|
|
253
342
|
* }
|
|
254
343
|
*
|
|
255
344
|
* // Assertions
|
|
256
|
-
* console.log(findLCA(3, 10)); // 7
|
|
257
|
-
* console.log(findLCA(5, 35)); // 15
|
|
258
|
-
* console.log(findLCA(20, 30)); // 25
|
|
345
|
+
* console.log(findLCA(3, 10)); // 7;
|
|
346
|
+
* console.log(findLCA(5, 35)); // 15;
|
|
347
|
+
* console.log(findLCA(20, 30)); // 25;
|
|
259
348
|
*/
|
|
260
349
|
export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implements IBinaryTree<K, V, R> {
|
|
261
350
|
/**
|
|
@@ -756,6 +845,52 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
756
845
|
return inserted;
|
|
757
846
|
}
|
|
758
847
|
|
|
848
|
+
/**
|
|
849
|
+
* Returns the first node with a key greater than or equal to the given key.
|
|
850
|
+
* This is equivalent to C++ std::lower_bound on a BST.
|
|
851
|
+
* Supports RECURSIVE and ITERATIVE implementations.
|
|
852
|
+
* Time Complexity: O(log n) on average, O(h) where h is tree height.
|
|
853
|
+
* Space Complexity: O(h) for recursion, O(1) for iteration.
|
|
854
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
855
|
+
* @param iterationType The iteration type (RECURSIVE or ITERATIVE). Defaults to this.iterationType.
|
|
856
|
+
* @returns The first node with key >= given key, or undefined if no such node exists.
|
|
857
|
+
*/
|
|
858
|
+
lowerBound(
|
|
859
|
+
keyNodeEntryOrPredicate:
|
|
860
|
+
| K
|
|
861
|
+
| BSTNode<K, V>
|
|
862
|
+
| [K | null | undefined, V | undefined]
|
|
863
|
+
| null
|
|
864
|
+
| undefined
|
|
865
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
866
|
+
iterationType: IterationType = this.iterationType
|
|
867
|
+
): BSTNode<K, V> | undefined {
|
|
868
|
+
return this._bound(keyNodeEntryOrPredicate, true, iterationType);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Returns the first node with a key strictly greater than the given key.
|
|
873
|
+
* This is equivalent to C++ std::upper_bound on a BST.
|
|
874
|
+
* Supports RECURSIVE and ITERATIVE implementations.
|
|
875
|
+
* Time Complexity: O(log n) on average, O(h) where h is tree height.
|
|
876
|
+
* Space Complexity: O(h) for recursion, O(1) for iteration.
|
|
877
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
878
|
+
* @param iterationType The iteration type (RECURSIVE or ITERATIVE). Defaults to this.iterationType.
|
|
879
|
+
* @returns The first node with key > given key, or undefined if no such node exists.
|
|
880
|
+
*/
|
|
881
|
+
upperBound(
|
|
882
|
+
keyNodeEntryOrPredicate:
|
|
883
|
+
| K
|
|
884
|
+
| BSTNode<K, V>
|
|
885
|
+
| [K | null | undefined, V | undefined]
|
|
886
|
+
| null
|
|
887
|
+
| undefined
|
|
888
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
889
|
+
iterationType: IterationType = this.iterationType
|
|
890
|
+
): BSTNode<K, V> | undefined {
|
|
891
|
+
return this._bound(keyNodeEntryOrPredicate, false, iterationType);
|
|
892
|
+
}
|
|
893
|
+
|
|
759
894
|
/**
|
|
760
895
|
* Traverses the tree and returns nodes that are lesser or greater than a target node.
|
|
761
896
|
* @remarks Time O(N), as it performs a full traversal. Space O(log N) or O(N).
|
|
@@ -945,6 +1080,180 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
945
1080
|
return false;
|
|
946
1081
|
}
|
|
947
1082
|
|
|
1083
|
+
/**
|
|
1084
|
+
* (Protected) Core bound search implementation supporting all parameter types.
|
|
1085
|
+
* Unified logic for both lowerBound and upperBound.
|
|
1086
|
+
* Resolves various input types (Key, Node, Entry, Predicate) using parent class utilities.
|
|
1087
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
1088
|
+
* @param isLower - True for lowerBound (>=), false for upperBound (>).
|
|
1089
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1090
|
+
* @returns The first matching node, or undefined if no such node exists.
|
|
1091
|
+
*/
|
|
1092
|
+
protected _bound(
|
|
1093
|
+
keyNodeEntryOrPredicate:
|
|
1094
|
+
| K
|
|
1095
|
+
| BSTNode<K, V>
|
|
1096
|
+
| [K | null | undefined, V | undefined]
|
|
1097
|
+
| null
|
|
1098
|
+
| undefined
|
|
1099
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
1100
|
+
isLower: boolean,
|
|
1101
|
+
iterationType: IterationType
|
|
1102
|
+
): BSTNode<K, V> | undefined {
|
|
1103
|
+
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined) {
|
|
1104
|
+
return undefined;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Check if input is a predicate function first
|
|
1108
|
+
if (this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
1109
|
+
return this._boundByPredicate(keyNodeEntryOrPredicate, iterationType);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Resolve input to a comparable key
|
|
1113
|
+
let targetKey: K | undefined;
|
|
1114
|
+
|
|
1115
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
1116
|
+
// Input is a BSTNode - extract its key
|
|
1117
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
1118
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
1119
|
+
// Input is a [key, value] entry - extract the key
|
|
1120
|
+
const key = keyNodeEntryOrPredicate[0];
|
|
1121
|
+
if (key === null || key === undefined) {
|
|
1122
|
+
return undefined;
|
|
1123
|
+
}
|
|
1124
|
+
targetKey = key;
|
|
1125
|
+
} else {
|
|
1126
|
+
// Input is a raw key
|
|
1127
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Execute key-based search with binary search optimization
|
|
1131
|
+
if (targetKey !== undefined) {
|
|
1132
|
+
return this._boundByKey(targetKey, isLower, iterationType);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
return undefined;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* (Protected) Binary search for bound by key with pruning optimization.
|
|
1140
|
+
* Performs standard BST binary search, choosing left or right subtree based on comparator result.
|
|
1141
|
+
* For lowerBound: finds first node where key >= target.
|
|
1142
|
+
* For upperBound: finds first node where key > target.
|
|
1143
|
+
* @param key - The target key to search for.
|
|
1144
|
+
* @param isLower - True for lowerBound (>=), false for upperBound (>).
|
|
1145
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1146
|
+
* @returns The first node matching the bound condition, or undefined if none exists.
|
|
1147
|
+
*/
|
|
1148
|
+
protected _boundByKey(key: K, isLower: boolean, iterationType: IterationType): BSTNode<K, V> | undefined {
|
|
1149
|
+
if (iterationType === 'RECURSIVE') {
|
|
1150
|
+
// Recursive binary search implementation
|
|
1151
|
+
const dfs = (cur: BSTNode<K, V> | null | undefined): BSTNode<K, V> | undefined => {
|
|
1152
|
+
if (!this.isRealNode(cur)) return undefined;
|
|
1153
|
+
|
|
1154
|
+
const cmp = this.comparator(cur.key!, key);
|
|
1155
|
+
const condition = isLower ? cmp >= 0 : cmp > 0;
|
|
1156
|
+
|
|
1157
|
+
if (condition) {
|
|
1158
|
+
// Current node satisfies the bound condition.
|
|
1159
|
+
// Try to find a closer (smaller key) candidate in the left subtree.
|
|
1160
|
+
const leftResult = dfs(cur.left);
|
|
1161
|
+
return leftResult ?? cur;
|
|
1162
|
+
} else {
|
|
1163
|
+
// Current node does not satisfy the condition.
|
|
1164
|
+
// Move right to find larger keys.
|
|
1165
|
+
return dfs(cur.right);
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
return dfs(this.root);
|
|
1170
|
+
} else {
|
|
1171
|
+
// Iterative binary search implementation
|
|
1172
|
+
let current: BSTNode<K, V> | undefined = this.root;
|
|
1173
|
+
let result: BSTNode<K, V> | undefined = undefined;
|
|
1174
|
+
|
|
1175
|
+
while (this.isRealNode(current)) {
|
|
1176
|
+
const cmp = this.comparator(current.key!, key);
|
|
1177
|
+
const condition = isLower ? cmp >= 0 : cmp > 0;
|
|
1178
|
+
|
|
1179
|
+
if (condition) {
|
|
1180
|
+
// Current node is a candidate. Save it and try left subtree for a closer match.
|
|
1181
|
+
result = current;
|
|
1182
|
+
current = current.left ?? undefined;
|
|
1183
|
+
} else {
|
|
1184
|
+
// Move right to find larger keys.
|
|
1185
|
+
current = current.right ?? undefined;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
return result;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* (Protected) In-order traversal search by predicate.
|
|
1195
|
+
* Falls back to linear in-order traversal when predicate-based search is required.
|
|
1196
|
+
* Returns the first node that satisfies the predicate function.
|
|
1197
|
+
* Note: Predicate-based search cannot leverage BST's binary search optimization.
|
|
1198
|
+
* Time Complexity: O(n) since it may visit every node.
|
|
1199
|
+
* @param predicate - The predicate function to test nodes.
|
|
1200
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1201
|
+
* @returns The first node satisfying predicate, or undefined if none found.
|
|
1202
|
+
*/
|
|
1203
|
+
protected _boundByPredicate(
|
|
1204
|
+
predicate: NodePredicate<BSTNode<K, V>>,
|
|
1205
|
+
iterationType: IterationType
|
|
1206
|
+
): BSTNode<K, V> | undefined {
|
|
1207
|
+
if (iterationType === 'RECURSIVE') {
|
|
1208
|
+
// Recursive in-order traversal
|
|
1209
|
+
let result: BSTNode<K, V> | undefined = undefined;
|
|
1210
|
+
|
|
1211
|
+
const dfs = (cur: BSTNode<K, V> | null | undefined): void => {
|
|
1212
|
+
if (result || !this.isRealNode(cur)) return;
|
|
1213
|
+
|
|
1214
|
+
// In-order: process left subtree first
|
|
1215
|
+
if (this.isRealNode(cur.left)) dfs(cur.left);
|
|
1216
|
+
|
|
1217
|
+
// Check current node
|
|
1218
|
+
if (!result && predicate(cur)) {
|
|
1219
|
+
result = cur;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// Process right subtree
|
|
1223
|
+
if (!result && this.isRealNode(cur.right)) dfs(cur.right);
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
dfs(this.root);
|
|
1227
|
+
return result;
|
|
1228
|
+
} else {
|
|
1229
|
+
// Iterative in-order traversal using explicit stack
|
|
1230
|
+
const stack: (BSTNode<K, V> | null | undefined)[] = [];
|
|
1231
|
+
let current: BSTNode<K, V> | null | undefined = this.root;
|
|
1232
|
+
|
|
1233
|
+
while (stack.length > 0 || this.isRealNode(current)) {
|
|
1234
|
+
if (this.isRealNode(current)) {
|
|
1235
|
+
// Go to the leftmost node
|
|
1236
|
+
stack.push(current);
|
|
1237
|
+
current = current.left;
|
|
1238
|
+
} else {
|
|
1239
|
+
// Pop from stack and process
|
|
1240
|
+
const node = stack.pop();
|
|
1241
|
+
if (!this.isRealNode(node)) break;
|
|
1242
|
+
|
|
1243
|
+
// Check if current node satisfies predicate
|
|
1244
|
+
if (predicate(node)) {
|
|
1245
|
+
return node;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Visit right subtree
|
|
1249
|
+
current = node.right;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return undefined;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
948
1257
|
/**
|
|
949
1258
|
* (Protected) Creates a new, empty instance of the same BST constructor.
|
|
950
1259
|
* @remarks Time O(1)
|
|
@@ -188,48 +188,97 @@ export class RedBlackTreeNode<K = any, V = any> {
|
|
|
188
188
|
* 2. It is BST itself. Compared with Heap which is not completely ordered, RedBlackTree is completely ordered.
|
|
189
189
|
*
|
|
190
190
|
* @example
|
|
191
|
-
* //
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
191
|
+
* // basic Red-Black Tree with simple number keys
|
|
192
|
+
* // Create a simple Red-Black Tree with numeric keys
|
|
193
|
+
* const tree = new RedBlackTree([5, 2, 8, 1, 9]);
|
|
194
|
+
*
|
|
195
|
+
* tree.print();
|
|
196
|
+
* // _2___
|
|
197
|
+
* // / \
|
|
198
|
+
* // 1 _8_
|
|
199
|
+
* // / \
|
|
200
|
+
* // 5 9
|
|
201
|
+
*
|
|
202
|
+
* // Verify the tree maintains sorted order
|
|
203
|
+
* console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
|
|
204
|
+
*
|
|
205
|
+
* // Check size
|
|
206
|
+
* console.log(tree.size); // 5;
|
|
207
|
+
* @example
|
|
208
|
+
* // Red-Black Tree with key-value pairs for lookups
|
|
209
|
+
* interface Employee {
|
|
210
|
+
* id: number;
|
|
211
|
+
* name: string;
|
|
197
212
|
* }
|
|
198
213
|
*
|
|
199
|
-
* //
|
|
200
|
-
* const
|
|
201
|
-
* {
|
|
202
|
-
* {
|
|
203
|
-
* {
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
214
|
+
* // Create tree with employee data
|
|
215
|
+
* const employees = new RedBlackTree<number, Employee>([
|
|
216
|
+
* [1, { id: 1, name: 'Alice' }],
|
|
217
|
+
* [3, { id: 3, name: 'Charlie' }],
|
|
218
|
+
* [2, { id: 2, name: 'Bob' }]
|
|
219
|
+
* ]);
|
|
220
|
+
*
|
|
221
|
+
* // Retrieve employee by ID
|
|
222
|
+
* const alice = employees.get(1);
|
|
223
|
+
* console.log(alice?.name); // 'Alice';
|
|
224
|
+
*
|
|
225
|
+
* // Verify sorted order by ID
|
|
226
|
+
* console.log([...employees.keys()]); // [1, 2, 3];
|
|
227
|
+
* @example
|
|
228
|
+
* // Red-Black Tree range search for filtering
|
|
229
|
+
* interface Product {
|
|
230
|
+
* name: string;
|
|
231
|
+
* price: number;
|
|
232
|
+
* }
|
|
207
233
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
234
|
+
* const products = new RedBlackTree<number, Product>([
|
|
235
|
+
* [10, { name: 'Item A', price: 10 }],
|
|
236
|
+
* [25, { name: 'Item B', price: 25 }],
|
|
237
|
+
* [40, { name: 'Item C', price: 40 }],
|
|
238
|
+
* [50, { name: 'Item D', price: 50 }]
|
|
239
|
+
* ]);
|
|
210
240
|
*
|
|
211
|
-
* //
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* toEntryFn: stockRecord => [
|
|
215
|
-
* stockRecord.price, // Use stock price as the key
|
|
216
|
-
* {
|
|
217
|
-
* ...stockRecord,
|
|
218
|
-
* lastUpdated: new Date() // Add a timestamp for when the record was indexed
|
|
219
|
-
* }
|
|
220
|
-
* ]
|
|
241
|
+
* // Find products in price range [20, 45]
|
|
242
|
+
* const pricesInRange = products.rangeSearch([20, 45], node => {
|
|
243
|
+
* return products.get(node)?.name;
|
|
221
244
|
* });
|
|
222
245
|
*
|
|
223
|
-
* //
|
|
224
|
-
*
|
|
225
|
-
*
|
|
246
|
+
* console.log(pricesInRange); // ['Item B', 'Item C'];
|
|
247
|
+
* @example
|
|
248
|
+
* // Red-Black Tree as database index for stock market data
|
|
249
|
+
* interface StockPrice {
|
|
250
|
+
* symbol: string;
|
|
251
|
+
* volume: number;
|
|
252
|
+
* timestamp: Date;
|
|
253
|
+
* }
|
|
254
|
+
*
|
|
255
|
+
* // Simulate real-time stock price index
|
|
256
|
+
* const priceIndex = new RedBlackTree<number, StockPrice>([
|
|
257
|
+
* [142.5, { symbol: 'AAPL', volume: 1000000, timestamp: new Date() }],
|
|
258
|
+
* [335.2, { symbol: 'MSFT', volume: 800000, timestamp: new Date() }],
|
|
259
|
+
* [3285.04, { symbol: 'AMZN', volume: 500000, timestamp: new Date() }],
|
|
260
|
+
* [267.98, { symbol: 'META', volume: 750000, timestamp: new Date() }],
|
|
261
|
+
* [234.57, { symbol: 'GOOGL', volume: 900000, timestamp: new Date() }]
|
|
262
|
+
* ]);
|
|
263
|
+
*
|
|
264
|
+
* // Find highest-priced stock
|
|
265
|
+
* const maxPrice = priceIndex.getRightMost();
|
|
266
|
+
* console.log(priceIndex.get(maxPrice)?.symbol); // 'AMZN';
|
|
267
|
+
*
|
|
268
|
+
* // Find stocks in price range [200, 400] for portfolio balancing
|
|
269
|
+
* const stocksInRange = priceIndex.rangeSearch([200, 400], node => {
|
|
270
|
+
* const stock = priceIndex.get(node);
|
|
271
|
+
* return {
|
|
272
|
+
* symbol: stock?.symbol,
|
|
273
|
+
* price: node,
|
|
274
|
+
* volume: stock?.volume
|
|
275
|
+
* };
|
|
276
|
+
* });
|
|
226
277
|
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* );
|
|
232
|
-
* console.log(stocksInRange); // ['GOOGL', 'META', 'MSFT']
|
|
278
|
+
* console.log(stocksInRange.length); // 3;
|
|
279
|
+
* console.log(stocksInRange.some((s: any) => s.symbol === 'GOOGL')); // true;
|
|
280
|
+
* console.log(stocksInRange.some((s: any) => s.symbol === 'META')); // true;
|
|
281
|
+
* console.log(stocksInRange.some((s: any) => s.symbol === 'MSFT')); // true;
|
|
233
282
|
*/
|
|
234
283
|
|
|
235
284
|
export class RedBlackTree<K = any, V = any, R = any> extends BST<K, V, R> implements IBinaryTree<K, V, R> {
|
|
@@ -186,7 +186,7 @@ export class TreeMultiMapNode<K = any, V = any> {
|
|
|
186
186
|
*
|
|
187
187
|
* @example
|
|
188
188
|
* // players ranked by score with their equipment
|
|
189
|
-
*
|
|
189
|
+
* type Equipment = {
|
|
190
190
|
* name: string; // Equipment name
|
|
191
191
|
* quality: 'legendary' | 'epic' | 'rare' | 'common';
|
|
192
192
|
* level: number;
|
|
@@ -347,7 +347,7 @@ export class TreeMultiMapNode<K = any, V = any> {
|
|
|
347
347
|
* // },
|
|
348
348
|
* // { name: 'Level 3 Backpack', quality: 'epic', level: 80 }
|
|
349
349
|
* // ]
|
|
350
|
-
* // ]
|
|
350
|
+
* // ];
|
|
351
351
|
*/
|
|
352
352
|
export class TreeMultiMap<K = any, V = any, R = any> extends RedBlackTree<K, V[], R> implements IBinaryTree<K, V[], R> {
|
|
353
353
|
/**
|
|
@@ -35,7 +35,132 @@ export class DirectedEdge<E = any> extends AbstractEdge<E> {
|
|
|
35
35
|
* @template VO - Concrete vertex class (extends AbstractVertex<V>).
|
|
36
36
|
* @template EO - Concrete edge class (extends AbstractEdge<E>).
|
|
37
37
|
* @remarks Time O(1), Space O(1)
|
|
38
|
-
* @example
|
|
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';
|
|
39
164
|
*/
|
|
40
165
|
export class DirectedGraph<
|
|
41
166
|
V = any,
|