tree-multimap-typed 2.2.2 → 2.2.4
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/dist/cjs/index.cjs +245 -72
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +246 -72
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +245 -72
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +246 -72
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -2
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +5 -5
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +98 -5
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
- package/dist/types/data-structures/binary-tree/bst.d.ts +202 -39
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +86 -37
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +7 -7
- 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/types/types/data-structures/binary-tree/bst.d.ts +5 -5
- package/dist/umd/tree-multimap-typed.js +246 -72
- package/dist/umd/tree-multimap-typed.js.map +1 -1
- package/dist/umd/tree-multimap-typed.min.js +3 -3
- package/dist/umd/tree-multimap-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -2
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -8
- package/src/data-structures/binary-tree/avl-tree.ts +100 -7
- package/src/data-structures/binary-tree/binary-tree.ts +117 -7
- package/src/data-structures/binary-tree/bst.ts +431 -93
- package/src/data-structures/binary-tree/red-black-tree.ts +85 -37
- package/src/data-structures/binary-tree/tree-counter.ts +5 -7
- package/src/data-structures/binary-tree/tree-multi-map.ts +9 -10
- 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
- package/src/types/data-structures/binary-tree/bst.ts +5 -5
|
@@ -7,11 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
|
-
|
|
10
|
+
BinaryTreeDeleteResult,
|
|
11
11
|
BSTNOptKeyOrNode,
|
|
12
12
|
BSTOptions,
|
|
13
13
|
BTNRep,
|
|
14
|
-
Comparable,
|
|
15
14
|
Comparator,
|
|
16
15
|
CP,
|
|
17
16
|
DFSOrderPattern,
|
|
@@ -201,8 +200,57 @@ export class BSTNode<K = any, V = any> {
|
|
|
201
200
|
* 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves.
|
|
202
201
|
*
|
|
203
202
|
* @example
|
|
203
|
+
* // basic BST creation and add operation
|
|
204
|
+
* // Create a simple BST with numeric keys
|
|
205
|
+
* const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
206
|
+
*
|
|
207
|
+
* bst.print();
|
|
208
|
+
* // _______8__________
|
|
209
|
+
* // / \
|
|
210
|
+
* // ___4___ ____12_____
|
|
211
|
+
* // / \ / \
|
|
212
|
+
* // _2_ _6_ _10__ _14__
|
|
213
|
+
* // / \ / \ / \ / \
|
|
214
|
+
* // 1 3 5 7 9 11 13 15__
|
|
215
|
+
* // \
|
|
216
|
+
* // 16
|
|
217
|
+
*
|
|
218
|
+
* // Verify size
|
|
219
|
+
* console.log(bst.size); // 16;
|
|
220
|
+
*
|
|
221
|
+
* // Add new elements
|
|
222
|
+
* bst.add(17);
|
|
223
|
+
* bst.add(0);
|
|
224
|
+
* console.log(bst.size); // 18;
|
|
225
|
+
*
|
|
226
|
+
* // Verify keys are searchable
|
|
227
|
+
* console.log(bst.has(11)); // true;
|
|
228
|
+
* console.log(bst.has(100)); // false;
|
|
229
|
+
* @example
|
|
230
|
+
* // BST delete and search after deletion
|
|
231
|
+
* const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
232
|
+
*
|
|
233
|
+
* // Delete a leaf node
|
|
234
|
+
* bst.delete(1);
|
|
235
|
+
* console.log(bst.has(1)); // false;
|
|
236
|
+
*
|
|
237
|
+
* // Delete a node with one child
|
|
238
|
+
* bst.delete(2);
|
|
239
|
+
* console.log(bst.has(2)); // false;
|
|
240
|
+
*
|
|
241
|
+
* // Delete a node with two children
|
|
242
|
+
* bst.delete(3);
|
|
243
|
+
* console.log(bst.has(3)); // false;
|
|
244
|
+
*
|
|
245
|
+
* // Size decreases with each deletion
|
|
246
|
+
* console.log(bst.size); // 13;
|
|
247
|
+
*
|
|
248
|
+
* // Other nodes remain searchable
|
|
249
|
+
* console.log(bst.has(11)); // true;
|
|
250
|
+
* console.log(bst.has(15)); // true;
|
|
251
|
+
* @example
|
|
204
252
|
* // Merge 3 sorted datasets
|
|
205
|
-
*
|
|
253
|
+
* const dataset1 = new BST<number, string>([
|
|
206
254
|
* [1, 'A'],
|
|
207
255
|
* [7, 'G']
|
|
208
256
|
* ]);
|
|
@@ -222,18 +270,58 @@ export class BSTNode<K = any, V = any> {
|
|
|
222
270
|
* merged.merge(dataset3);
|
|
223
271
|
*
|
|
224
272
|
* // Verify merged dataset is in sorted order
|
|
225
|
-
* console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
|
|
273
|
+
* console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
|
|
226
274
|
* @example
|
|
227
|
-
* //
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
275
|
+
* // BST with custom objects for expression evaluation
|
|
276
|
+
* interface Expression {
|
|
277
|
+
* id: number;
|
|
278
|
+
* operator: string;
|
|
279
|
+
* precedence: number;
|
|
280
|
+
* }
|
|
281
|
+
*
|
|
282
|
+
* // BST efficiently stores and retrieves operators by precedence
|
|
283
|
+
* const operatorTree = new BST<number, Expression>(
|
|
284
|
+
* [
|
|
285
|
+
* [1, { id: 1, operator: '+', precedence: 1 }],
|
|
286
|
+
* [2, { id: 2, operator: '*', precedence: 2 }],
|
|
287
|
+
* [3, { id: 3, operator: '/', precedence: 2 }],
|
|
288
|
+
* [4, { id: 4, operator: '-', precedence: 1 }],
|
|
289
|
+
* [5, { id: 5, operator: '^', precedence: 3 }]
|
|
290
|
+
* ],
|
|
291
|
+
* { isMapMode: false }
|
|
292
|
+
* );
|
|
293
|
+
*
|
|
294
|
+
* console.log(operatorTree.size); // 5;
|
|
295
|
+
*
|
|
296
|
+
* // Quick lookup of operators
|
|
297
|
+
* const mult = operatorTree.get(2);
|
|
298
|
+
* console.log(mult?.operator); // '*';
|
|
299
|
+
* console.log(mult?.precedence); // 2;
|
|
300
|
+
*
|
|
301
|
+
* // Check if operator exists
|
|
302
|
+
* console.log(operatorTree.has(5)); // true;
|
|
303
|
+
* console.log(operatorTree.has(99)); // false;
|
|
304
|
+
*
|
|
305
|
+
* // Retrieve operator by precedence level
|
|
306
|
+
* const expNode = operatorTree.getNode(3);
|
|
307
|
+
* console.log(expNode?.key); // 3;
|
|
308
|
+
* console.log(expNode?.value?.precedence); // 2;
|
|
309
|
+
*
|
|
310
|
+
* // Delete operator and verify
|
|
311
|
+
* operatorTree.delete(1);
|
|
312
|
+
* console.log(operatorTree.has(1)); // false;
|
|
313
|
+
* console.log(operatorTree.size); // 4;
|
|
314
|
+
*
|
|
315
|
+
* // Get tree height for optimization analysis
|
|
316
|
+
* const treeHeight = operatorTree.getHeight();
|
|
317
|
+
* console.log(treeHeight); // > 0;
|
|
318
|
+
*
|
|
319
|
+
* // Remaining operators are still accessible
|
|
320
|
+
* const remaining = operatorTree.get(2);
|
|
321
|
+
* console.log(remaining); // defined;
|
|
234
322
|
* @example
|
|
235
323
|
* // Find lowest common ancestor
|
|
236
|
-
*
|
|
324
|
+
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
|
237
325
|
*
|
|
238
326
|
* // LCA helper function
|
|
239
327
|
* const findLCA = (num1: number, num2: number): number | undefined => {
|
|
@@ -253,9 +341,9 @@ export class BSTNode<K = any, V = any> {
|
|
|
253
341
|
* }
|
|
254
342
|
*
|
|
255
343
|
* // Assertions
|
|
256
|
-
* console.log(findLCA(3, 10)); // 7
|
|
257
|
-
* console.log(findLCA(5, 35)); // 15
|
|
258
|
-
* console.log(findLCA(20, 30)); // 25
|
|
344
|
+
* console.log(findLCA(3, 10)); // 7;
|
|
345
|
+
* console.log(findLCA(5, 35)); // 15;
|
|
346
|
+
* console.log(findLCA(20, 30)); // 25;
|
|
259
347
|
*/
|
|
260
348
|
export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implements IBinaryTree<K, V, R> {
|
|
261
349
|
/**
|
|
@@ -267,20 +355,28 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
267
355
|
*/
|
|
268
356
|
constructor(
|
|
269
357
|
keysNodesEntriesOrRaws: Iterable<
|
|
270
|
-
K | BSTNode
|
|
358
|
+
K | BSTNode | [K | null | undefined, V | undefined] | null | undefined | R
|
|
271
359
|
> = [],
|
|
272
360
|
options?: BSTOptions<K, V, R>
|
|
273
361
|
) {
|
|
274
362
|
super([], options);
|
|
275
363
|
|
|
276
364
|
if (options) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (
|
|
365
|
+
|
|
366
|
+
// Use the 'in' operator to check if the field is present
|
|
367
|
+
if ('comparator' in options && options.comparator !== undefined) {
|
|
368
|
+
this._comparator = options.comparator;
|
|
369
|
+
} else {
|
|
370
|
+
this._comparator = this._createDefaultComparator();
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
this._comparator = this._createDefaultComparator();
|
|
280
374
|
}
|
|
375
|
+
|
|
281
376
|
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
|
|
282
377
|
}
|
|
283
378
|
|
|
379
|
+
|
|
284
380
|
protected override _root?: BSTNode<K, V> = undefined;
|
|
285
381
|
|
|
286
382
|
/**
|
|
@@ -293,42 +389,40 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
293
389
|
return this._root;
|
|
294
390
|
}
|
|
295
391
|
|
|
296
|
-
protected _isReverse: boolean = false;
|
|
297
|
-
|
|
298
392
|
/**
|
|
299
|
-
*
|
|
300
|
-
* @remarks Time O(1)
|
|
301
|
-
*
|
|
302
|
-
* @returns True if the tree is reversed (e.g., a max-heap logic).
|
|
393
|
+
* (Protected) Creates the default comparator function for keys that don't have a custom comparator.
|
|
394
|
+
* @remarks Time O(1) Space O(1)
|
|
395
|
+
* @returns The default comparator function.
|
|
303
396
|
*/
|
|
304
|
-
|
|
305
|
-
return
|
|
397
|
+
protected _createDefaultComparator(): Comparator<K> {
|
|
398
|
+
return (a: K, b: K): number => {
|
|
399
|
+
debugger
|
|
400
|
+
// If both keys are comparable (primitive types), use direct comparison
|
|
401
|
+
if (isComparable(a) && isComparable(b)) {
|
|
402
|
+
if (a > b) return 1;
|
|
403
|
+
if (a < b) return -1;
|
|
404
|
+
return 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// If keys are objects and no comparator is provided, throw an error
|
|
408
|
+
if (typeof a === 'object' || typeof b === 'object') {
|
|
409
|
+
throw TypeError(
|
|
410
|
+
`When comparing object type keys, a custom comparator must be provided in the constructor's options!`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Default: keys are equal (fallback case)
|
|
415
|
+
return 0;
|
|
416
|
+
};
|
|
306
417
|
}
|
|
307
418
|
|
|
308
419
|
/**
|
|
309
|
-
* The
|
|
310
|
-
|
|
420
|
+
* The comparator function used to determine the order of keys in the tree.
|
|
421
|
+
|
|
422
|
+
* @remarks Time O(1) Space O(1)
|
|
311
423
|
*/
|
|
312
|
-
protected _comparator: Comparator<K
|
|
313
|
-
|
|
314
|
-
if (a > b) return 1;
|
|
315
|
-
if (a < b) return -1;
|
|
316
|
-
return 0;
|
|
317
|
-
}
|
|
318
|
-
if (this._specifyComparable) {
|
|
319
|
-
const va = this._specifyComparable(a);
|
|
320
|
-
const vb = this._specifyComparable(b);
|
|
321
|
-
if (va > vb) return 1;
|
|
322
|
-
if (va < vb) return -1;
|
|
323
|
-
return 0;
|
|
324
|
-
}
|
|
325
|
-
if (typeof a === 'object' || typeof b === 'object') {
|
|
326
|
-
throw TypeError(
|
|
327
|
-
`When comparing object types, a custom specifyComparable must be defined in the constructor's options.`
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
return 0;
|
|
331
|
-
};
|
|
424
|
+
protected _comparator: Comparator<K>;
|
|
425
|
+
|
|
332
426
|
|
|
333
427
|
/**
|
|
334
428
|
* Gets the comparator function used by the tree.
|
|
@@ -340,18 +434,6 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
340
434
|
return this._comparator;
|
|
341
435
|
}
|
|
342
436
|
|
|
343
|
-
protected _specifyComparable?: (key: K) => Comparable;
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Gets the function used to extract a comparable value from a complex key.
|
|
347
|
-
* @remarks Time O(1)
|
|
348
|
-
*
|
|
349
|
-
* @returns The key-to-comparable conversion function.
|
|
350
|
-
*/
|
|
351
|
-
get specifyComparable(): ((key: K) => Comparable) | undefined {
|
|
352
|
-
return this._specifyComparable;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
437
|
/**
|
|
356
438
|
* (Protected) Creates a new BST node.
|
|
357
439
|
* @remarks Time O(1), Space O(1)
|
|
@@ -400,7 +482,7 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
400
482
|
* @returns True if the key is valid, false otherwise.
|
|
401
483
|
*/
|
|
402
484
|
override isValidKey(key: any): key is K {
|
|
403
|
-
return isComparable(key
|
|
485
|
+
return isComparable(key);
|
|
404
486
|
}
|
|
405
487
|
|
|
406
488
|
/**
|
|
@@ -536,8 +618,8 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
536
618
|
if (isRange) {
|
|
537
619
|
// Range search: Only go left if the current key is >= the lower bound
|
|
538
620
|
const range = keyNodeEntryOrPredicate as Range<K>;
|
|
539
|
-
const leftS =
|
|
540
|
-
const leftI =
|
|
621
|
+
const leftS = range.low;
|
|
622
|
+
const leftI = range.includeLow;
|
|
541
623
|
return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0);
|
|
542
624
|
}
|
|
543
625
|
if (!isRange && !this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
@@ -554,8 +636,8 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
554
636
|
if (isRange) {
|
|
555
637
|
// Range search: Only go right if current key <= upper bound
|
|
556
638
|
const range = keyNodeEntryOrPredicate as Range<K>;
|
|
557
|
-
const rightS =
|
|
558
|
-
const rightI =
|
|
639
|
+
const rightS = range.high;
|
|
640
|
+
const rightI = range.includeHigh;
|
|
559
641
|
return (rightI && this._compare(cur.key, rightS) <= 0) || (!rightI && this._compare(cur.key, rightS) < 0);
|
|
560
642
|
}
|
|
561
643
|
if (!isRange && !this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
@@ -756,6 +838,52 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
756
838
|
return inserted;
|
|
757
839
|
}
|
|
758
840
|
|
|
841
|
+
/**
|
|
842
|
+
* Returns the first node with a key greater than or equal to the given key.
|
|
843
|
+
* This is equivalent to C++ std::lower_bound on a BST.
|
|
844
|
+
* Supports RECURSIVE and ITERATIVE implementations.
|
|
845
|
+
* Time Complexity: O(log n) on average, O(h) where h is tree height.
|
|
846
|
+
* Space Complexity: O(h) for recursion, O(1) for iteration.
|
|
847
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
848
|
+
* @param iterationType The iteration type (RECURSIVE or ITERATIVE). Defaults to this.iterationType.
|
|
849
|
+
* @returns The first node with key >= given key, or undefined if no such node exists.
|
|
850
|
+
*/
|
|
851
|
+
lowerBound(
|
|
852
|
+
keyNodeEntryOrPredicate:
|
|
853
|
+
| K
|
|
854
|
+
| BSTNode<K, V>
|
|
855
|
+
| [K | null | undefined, V | undefined]
|
|
856
|
+
| null
|
|
857
|
+
| undefined
|
|
858
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
859
|
+
iterationType: IterationType = this.iterationType
|
|
860
|
+
): BSTNode<K, V> | undefined {
|
|
861
|
+
return this._bound(keyNodeEntryOrPredicate, true, iterationType);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Returns the first node with a key strictly greater than the given key.
|
|
866
|
+
* This is equivalent to C++ std::upper_bound on a BST.
|
|
867
|
+
* Supports RECURSIVE and ITERATIVE implementations.
|
|
868
|
+
* Time Complexity: O(log n) on average, O(h) where h is tree height.
|
|
869
|
+
* Space Complexity: O(h) for recursion, O(1) for iteration.
|
|
870
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
871
|
+
* @param iterationType The iteration type (RECURSIVE or ITERATIVE). Defaults to this.iterationType.
|
|
872
|
+
* @returns The first node with key > given key, or undefined if no such node exists.
|
|
873
|
+
*/
|
|
874
|
+
upperBound(
|
|
875
|
+
keyNodeEntryOrPredicate:
|
|
876
|
+
| K
|
|
877
|
+
| BSTNode<K, V>
|
|
878
|
+
| [K | null | undefined, V | undefined]
|
|
879
|
+
| null
|
|
880
|
+
| undefined
|
|
881
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
882
|
+
iterationType: IterationType = this.iterationType
|
|
883
|
+
): BSTNode<K, V> | undefined {
|
|
884
|
+
return this._bound(keyNodeEntryOrPredicate, false, iterationType);
|
|
885
|
+
}
|
|
886
|
+
|
|
759
887
|
/**
|
|
760
888
|
* Traverses the tree and returns nodes that are lesser or greater than a target node.
|
|
761
889
|
* @remarks Time O(N), as it performs a full traversal. Space O(log N) or O(N).
|
|
@@ -902,7 +1030,7 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
902
1030
|
*/
|
|
903
1031
|
override map<MK = K, MV = V, MR = any>(
|
|
904
1032
|
callback: EntryCallback<K, V | undefined, [MK, MV]>,
|
|
905
|
-
options?: Partial<
|
|
1033
|
+
options?: Partial<BSTOptions<MK, MV, MR>>,
|
|
906
1034
|
thisArg?: unknown
|
|
907
1035
|
): BST<MK, MV, MR> {
|
|
908
1036
|
const out = this._createLike<MK, MV, MR>([], options);
|
|
@@ -915,34 +1043,245 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
915
1043
|
}
|
|
916
1044
|
|
|
917
1045
|
/**
|
|
918
|
-
* Deletes
|
|
919
|
-
*
|
|
1046
|
+
* Deletes nodes that match a key, node, entry, predicate, or range.
|
|
1047
|
+
*
|
|
1048
|
+
* @remarks
|
|
1049
|
+
* Time Complexity: O(N) for search + O(M log N) for M deletions, where N is tree size.
|
|
1050
|
+
* Space Complexity: O(M) for storing matched nodes and result map.
|
|
1051
|
+
*
|
|
1052
|
+
* @template K - The key type.
|
|
1053
|
+
* @template V - The value type.
|
|
1054
|
+
*
|
|
1055
|
+
* @param keyNodeEntryOrPredicate - The search criteria. Can be one of:
|
|
1056
|
+
* - A key (type K): searches for exact key match using the comparator.
|
|
1057
|
+
* - A BSTNode: searches for the matching node in the tree.
|
|
1058
|
+
* - An entry tuple: searches for the key-value pair.
|
|
1059
|
+
* - A NodePredicate function: tests each node and returns true for matches.
|
|
1060
|
+
* - A Range object: searches for nodes whose keys fall within the specified range (inclusive/exclusive based on range settings).
|
|
1061
|
+
* - null or undefined: treated as no match, returns empty results.
|
|
920
1062
|
*
|
|
921
|
-
* @param
|
|
922
|
-
*
|
|
1063
|
+
* @param onlyOne - If true, stops the search after finding the first match and only deletes that one node.
|
|
1064
|
+
* If false (default), searches for and deletes all matching nodes.
|
|
1065
|
+
*
|
|
1066
|
+
* @param startNode - The node to start the search from. Can be:
|
|
1067
|
+
* - A key, node, or entry: the method resolves it to a node and searches from that subtree.
|
|
1068
|
+
* - null or undefined: defaults to the root, searching the entire tree.
|
|
1069
|
+
* - Default value: this._root (the tree's root).
|
|
1070
|
+
*
|
|
1071
|
+
* @param iterationType - Controls the internal traversal implementation:
|
|
1072
|
+
* - 'RECURSIVE': uses recursive function calls for traversal.
|
|
1073
|
+
* - 'ITERATIVE': uses explicit stack-based iteration.
|
|
1074
|
+
* - Default: this.iterationType (the tree's default iteration mode).
|
|
1075
|
+
*
|
|
1076
|
+
* @returns A Map<K, boolean> containing the deletion results:
|
|
1077
|
+
* - Key: the matched node's key.
|
|
1078
|
+
* - Value: true if the deletion succeeded, false if it failed (e.g., key not found during deletion phase).
|
|
1079
|
+
* - If no nodes match the search criteria, the returned map is empty.
|
|
923
1080
|
*/
|
|
924
|
-
deleteWhere(
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1081
|
+
deleteWhere(
|
|
1082
|
+
keyNodeEntryOrPredicate:
|
|
1083
|
+
| K
|
|
1084
|
+
| BSTNode<K, V>
|
|
1085
|
+
| [K | null | undefined, V | undefined]
|
|
1086
|
+
| null
|
|
1087
|
+
| undefined
|
|
1088
|
+
| NodePredicate<BSTNode<K, V>>
|
|
1089
|
+
| Range<K>,
|
|
1090
|
+
onlyOne = false,
|
|
1091
|
+
startNode: K | BSTNode<K, V> | [K | null | undefined, V | undefined] | null | undefined = this._root,
|
|
1092
|
+
iterationType: IterationType = this.iterationType
|
|
1093
|
+
): BinaryTreeDeleteResult<BSTNode<K, V>>[] {
|
|
928
1094
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1095
|
+
const toDelete = this.search (
|
|
1096
|
+
keyNodeEntryOrPredicate,
|
|
1097
|
+
onlyOne,
|
|
1098
|
+
(node) => node,
|
|
1099
|
+
startNode,
|
|
1100
|
+
iterationType
|
|
1101
|
+
);
|
|
1102
|
+
|
|
1103
|
+
let results : BinaryTreeDeleteResult<BSTNode<K, V>>[] = [];
|
|
1104
|
+
for (const node of toDelete) {
|
|
1105
|
+
const deleteInfo = this.delete(node);
|
|
1106
|
+
results = results.concat(deleteInfo);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
return results;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* (Protected) Core bound search implementation supporting all parameter types.
|
|
1115
|
+
* Unified logic for both lowerBound and upperBound.
|
|
1116
|
+
* Resolves various input types (Key, Node, Entry, Predicate) using parent class utilities.
|
|
1117
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
1118
|
+
* @param isLower - True for lowerBound (>=), false for upperBound (>).
|
|
1119
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1120
|
+
* @returns The first matching node, or undefined if no such node exists.
|
|
1121
|
+
*/
|
|
1122
|
+
protected _bound(
|
|
1123
|
+
keyNodeEntryOrPredicate:
|
|
1124
|
+
| K
|
|
1125
|
+
| BSTNode<K, V>
|
|
1126
|
+
| [K | null | undefined, V | undefined]
|
|
1127
|
+
| null
|
|
1128
|
+
| undefined
|
|
1129
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
1130
|
+
isLower: boolean,
|
|
1131
|
+
iterationType: IterationType
|
|
1132
|
+
): BSTNode<K, V> | undefined {
|
|
1133
|
+
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined) {
|
|
1134
|
+
return undefined;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// Check if input is a predicate function first
|
|
1138
|
+
if (this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
1139
|
+
return this._boundByPredicate(keyNodeEntryOrPredicate, iterationType);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// Resolve input to a comparable key
|
|
1143
|
+
let targetKey: K | undefined;
|
|
1144
|
+
|
|
1145
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
1146
|
+
// Input is a BSTNode - extract its key
|
|
1147
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
1148
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
1149
|
+
// Input is a [key, value] entry - extract the key
|
|
1150
|
+
const key = keyNodeEntryOrPredicate[0];
|
|
1151
|
+
if (key === null || key === undefined) {
|
|
1152
|
+
return undefined;
|
|
934
1153
|
}
|
|
935
|
-
|
|
936
|
-
|
|
1154
|
+
targetKey = key;
|
|
1155
|
+
} else {
|
|
1156
|
+
// Input is a raw key
|
|
1157
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Execute key-based search with binary search optimization
|
|
1161
|
+
if (targetKey !== undefined) {
|
|
1162
|
+
return this._boundByKey(targetKey, isLower, iterationType);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return undefined;
|
|
1166
|
+
}
|
|
937
1167
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1168
|
+
/**
|
|
1169
|
+
* (Protected) Binary search for bound by key with pruning optimization.
|
|
1170
|
+
* Performs standard BST binary search, choosing left or right subtree based on comparator result.
|
|
1171
|
+
* For lowerBound: finds first node where key >= target.
|
|
1172
|
+
* For upperBound: finds first node where key > target.
|
|
1173
|
+
* @param key - The target key to search for.
|
|
1174
|
+
* @param isLower - True for lowerBound (>=), false for upperBound (>).
|
|
1175
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1176
|
+
* @returns The first node matching the bound condition, or undefined if none exists.
|
|
1177
|
+
*/
|
|
1178
|
+
protected _boundByKey(key: K, isLower: boolean, iterationType: IterationType): BSTNode<K, V> | undefined {
|
|
1179
|
+
if (iterationType === 'RECURSIVE') {
|
|
1180
|
+
// Recursive binary search implementation
|
|
1181
|
+
const dfs = (cur: BSTNode<K, V> | null | undefined): BSTNode<K, V> | undefined => {
|
|
1182
|
+
if (!this.isRealNode(cur)) return undefined;
|
|
1183
|
+
|
|
1184
|
+
const cmp = this.comparator(cur.key!, key);
|
|
1185
|
+
const condition = isLower ? cmp >= 0 : cmp > 0;
|
|
1186
|
+
|
|
1187
|
+
if (condition) {
|
|
1188
|
+
// Current node satisfies the bound condition.
|
|
1189
|
+
// Try to find a closer (smaller key) candidate in the left subtree.
|
|
1190
|
+
const leftResult = dfs(cur.left);
|
|
1191
|
+
return leftResult ?? cur;
|
|
1192
|
+
} else {
|
|
1193
|
+
// Current node does not satisfy the condition.
|
|
1194
|
+
// Move right to find larger keys.
|
|
1195
|
+
return dfs(cur.right);
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
return dfs(this.root);
|
|
1200
|
+
} else {
|
|
1201
|
+
// Iterative binary search implementation
|
|
1202
|
+
let current: BSTNode<K, V> | undefined = this.root;
|
|
1203
|
+
let result: BSTNode<K, V> | undefined = undefined;
|
|
1204
|
+
|
|
1205
|
+
while (this.isRealNode(current)) {
|
|
1206
|
+
const cmp = this.comparator(current.key!, key);
|
|
1207
|
+
const condition = isLower ? cmp >= 0 : cmp > 0;
|
|
1208
|
+
|
|
1209
|
+
if (condition) {
|
|
1210
|
+
// Current node is a candidate. Save it and try left subtree for a closer match.
|
|
1211
|
+
result = current;
|
|
1212
|
+
current = current.left ?? undefined;
|
|
1213
|
+
} else {
|
|
1214
|
+
// Move right to find larger keys.
|
|
1215
|
+
current = current.right ?? undefined;
|
|
1216
|
+
}
|
|
942
1217
|
}
|
|
943
|
-
|
|
1218
|
+
|
|
1219
|
+
return result;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* (Protected) In-order traversal search by predicate.
|
|
1225
|
+
* Falls back to linear in-order traversal when predicate-based search is required.
|
|
1226
|
+
* Returns the first node that satisfies the predicate function.
|
|
1227
|
+
* Note: Predicate-based search cannot leverage BST's binary search optimization.
|
|
1228
|
+
* Time Complexity: O(n) since it may visit every node.
|
|
1229
|
+
* @param predicate - The predicate function to test nodes.
|
|
1230
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1231
|
+
* @returns The first node satisfying predicate, or undefined if none found.
|
|
1232
|
+
*/
|
|
1233
|
+
protected _boundByPredicate(
|
|
1234
|
+
predicate: NodePredicate<BSTNode<K, V>>,
|
|
1235
|
+
iterationType: IterationType
|
|
1236
|
+
): BSTNode<K, V> | undefined {
|
|
1237
|
+
if (iterationType === 'RECURSIVE') {
|
|
1238
|
+
// Recursive in-order traversal
|
|
1239
|
+
let result: BSTNode<K, V> | undefined = undefined;
|
|
1240
|
+
|
|
1241
|
+
const dfs = (cur: BSTNode<K, V> | null | undefined): void => {
|
|
1242
|
+
if (result || !this.isRealNode(cur)) return;
|
|
1243
|
+
|
|
1244
|
+
// In-order: process left subtree first
|
|
1245
|
+
if (this.isRealNode(cur.left)) dfs(cur.left);
|
|
1246
|
+
|
|
1247
|
+
// Check current node
|
|
1248
|
+
if (!result && predicate(cur)) {
|
|
1249
|
+
result = cur;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// Process right subtree
|
|
1253
|
+
if (!result && this.isRealNode(cur.right)) dfs(cur.right);
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
dfs(this.root);
|
|
1257
|
+
return result;
|
|
1258
|
+
} else {
|
|
1259
|
+
// Iterative in-order traversal using explicit stack
|
|
1260
|
+
const stack: (BSTNode<K, V> | null | undefined)[] = [];
|
|
1261
|
+
let current: BSTNode<K, V> | null | undefined = this.root;
|
|
1262
|
+
|
|
1263
|
+
while (stack.length > 0 || this.isRealNode(current)) {
|
|
1264
|
+
if (this.isRealNode(current)) {
|
|
1265
|
+
// Go to the leftmost node
|
|
1266
|
+
stack.push(current);
|
|
1267
|
+
current = current.left;
|
|
1268
|
+
} else {
|
|
1269
|
+
// Pop from stack and process
|
|
1270
|
+
const node = stack.pop();
|
|
1271
|
+
if (!this.isRealNode(node)) break;
|
|
1272
|
+
|
|
1273
|
+
// Check if current node satisfies predicate
|
|
1274
|
+
if (predicate(node)) {
|
|
1275
|
+
return node;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Visit right subtree
|
|
1279
|
+
current = node.right;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
return undefined;
|
|
944
1284
|
}
|
|
945
|
-
return false;
|
|
946
1285
|
}
|
|
947
1286
|
|
|
948
1287
|
/**
|
|
@@ -991,8 +1330,7 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
991
1330
|
protected override _snapshotOptions<TK = K, TV = V, TR = R>(): BSTOptions<TK, TV, TR> {
|
|
992
1331
|
return {
|
|
993
1332
|
...super._snapshotOptions<TK, TV, TR>(),
|
|
994
|
-
|
|
995
|
-
isReverse: this.isReverse as BSTOptions<TK, TV, TR>['isReverse']
|
|
1333
|
+
comparator: this._comparator as unknown as BSTOptions<TK, TV, TR>['comparator'],
|
|
996
1334
|
};
|
|
997
1335
|
}
|
|
998
1336
|
|
|
@@ -1026,14 +1364,14 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
1026
1364
|
|
|
1027
1365
|
/**
|
|
1028
1366
|
* (Protected) Compares two keys using the tree's comparator and reverse setting.
|
|
1029
|
-
* @remarks Time O(1)
|
|
1367
|
+
* @remarks Time O(1) Space O(1)
|
|
1030
1368
|
*
|
|
1031
1369
|
* @param a - The first key.
|
|
1032
1370
|
* @param b - The second key.
|
|
1033
1371
|
* @returns A number (1, -1, or 0) representing the comparison.
|
|
1034
1372
|
*/
|
|
1035
1373
|
protected _compare(a: K, b: K) {
|
|
1036
|
-
return this.
|
|
1374
|
+
return this._comparator(a, b);
|
|
1037
1375
|
}
|
|
1038
1376
|
|
|
1039
1377
|
/**
|
|
@@ -1043,7 +1381,7 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
1043
1381
|
* @param key - The key of the node to delete.
|
|
1044
1382
|
* @returns True if the node was found and deleted, false otherwise.
|
|
1045
1383
|
*/
|
|
1046
|
-
|
|
1384
|
+
protected _deleteByKey(key: K): boolean {
|
|
1047
1385
|
let node = this._root as BSTNode<K, V> | undefined;
|
|
1048
1386
|
|
|
1049
1387
|
// 1. Find the node
|