min-heap-typed 1.53.6 → 1.53.7

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.
Files changed (44) hide show
  1. package/dist/common/index.d.ts +12 -0
  2. package/dist/common/index.js +23 -0
  3. package/dist/data-structures/binary-tree/avl-tree-multi-map.js +7 -10
  4. package/dist/data-structures/binary-tree/avl-tree.js +2 -2
  5. package/dist/data-structures/binary-tree/binary-tree.d.ts +54 -19
  6. package/dist/data-structures/binary-tree/binary-tree.js +100 -66
  7. package/dist/data-structures/binary-tree/bst.d.ts +100 -36
  8. package/dist/data-structures/binary-tree/bst.js +185 -66
  9. package/dist/data-structures/binary-tree/rb-tree.d.ts +4 -0
  10. package/dist/data-structures/binary-tree/rb-tree.js +6 -2
  11. package/dist/data-structures/binary-tree/tree-multi-map.js +2 -2
  12. package/dist/data-structures/heap/heap.d.ts +6 -6
  13. package/dist/data-structures/heap/heap.js +6 -6
  14. package/dist/data-structures/linked-list/doubly-linked-list.d.ts +18 -8
  15. package/dist/data-structures/linked-list/doubly-linked-list.js +24 -10
  16. package/dist/data-structures/linked-list/singly-linked-list.d.ts +1 -1
  17. package/dist/data-structures/linked-list/singly-linked-list.js +1 -1
  18. package/dist/data-structures/trie/trie.d.ts +104 -4
  19. package/dist/data-structures/trie/trie.js +116 -12
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.js +2 -1
  22. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
  23. package/dist/types/data-structures/binary-tree/bst.d.ts +3 -2
  24. package/dist/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
  25. package/dist/types/utils/utils.d.ts +10 -6
  26. package/dist/utils/utils.js +4 -2
  27. package/package.json +2 -2
  28. package/src/common/index.ts +19 -0
  29. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -9
  30. package/src/data-structures/binary-tree/avl-tree.ts +3 -2
  31. package/src/data-structures/binary-tree/binary-tree.ts +108 -64
  32. package/src/data-structures/binary-tree/bst.ts +190 -69
  33. package/src/data-structures/binary-tree/rb-tree.ts +6 -2
  34. package/src/data-structures/binary-tree/tree-multi-map.ts +3 -3
  35. package/src/data-structures/heap/heap.ts +39 -39
  36. package/src/data-structures/linked-list/doubly-linked-list.ts +111 -97
  37. package/src/data-structures/linked-list/singly-linked-list.ts +1 -1
  38. package/src/data-structures/trie/trie.ts +116 -11
  39. package/src/index.ts +2 -1
  40. package/src/types/data-structures/binary-tree/binary-tree.ts +1 -1
  41. package/src/types/data-structures/binary-tree/bst.ts +3 -2
  42. package/src/types/data-structures/binary-tree/rb-tree.ts +1 -1
  43. package/src/types/utils/utils.ts +16 -10
  44. package/src/utils/utils.ts +4 -2
@@ -11,6 +11,7 @@ import type {
11
11
  BSTNOptKeyOrNode,
12
12
  BSTOptions,
13
13
  BTNRep,
14
+ Comparable,
14
15
  Comparator,
15
16
  CP,
16
17
  DFSOrderPattern,
@@ -23,6 +24,7 @@ import { BinaryTree, BinaryTreeNode } from './binary-tree';
23
24
  import { IBinaryTree } from '../../interfaces';
24
25
  import { Queue } from '../queue';
25
26
  import { isComparable } from '../../utils';
27
+ import { Range } from '../../common';
26
28
 
27
29
  export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNodeNested<K, V>> extends BinaryTreeNode<
28
30
  K,
@@ -92,6 +94,54 @@ export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNod
92
94
  * 5. Logarithmic Operations: Ideal operations like insertion, deletion, and searching are O(log n) time-efficient.
93
95
  * 6. Balance Variability: Can become unbalanced; special types maintain balance.
94
96
  * 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves.
97
+ * @example
98
+ * // Find kth smallest element
99
+ * // Create a BST with some elements
100
+ * const bst = new BST<number>([5, 3, 7, 1, 4, 6, 8]);
101
+ * const sortedKeys = bst.dfs(node => node.key, 'IN');
102
+ *
103
+ * // Helper function to find kth smallest
104
+ * const findKthSmallest = (k: number): number | undefined => {
105
+ * return sortedKeys[k - 1];
106
+ * };
107
+ *
108
+ * // Assertions
109
+ * console.log(findKthSmallest(1)); // 1
110
+ * console.log(findKthSmallest(3)); // 4
111
+ * console.log(findKthSmallest(7)); // 8
112
+ * @example
113
+ * // Find elements in a range
114
+ * const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
115
+ * console.log(bst.search(new Range(5, 10))); // [10, 5, 7]
116
+ * console.log(bst.search(new Range(4, 12))); // [10, 12, 5, 7]
117
+ * console.log(bst.search(new Range(4, 12, true, false))); // [10, 5, 7]
118
+ * console.log(bst.search(new Range(15, 20))); // [15, 18]
119
+ * console.log(bst.search(new Range(15, 20, false))); // [18]
120
+ * @example
121
+ * // Find lowest common ancestor
122
+ * const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
123
+ *
124
+ * function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
125
+ * for (const num of arr1) {
126
+ * if (arr2.indexOf(num) !== -1) {
127
+ * return num;
128
+ * }
129
+ * }
130
+ * return undefined;
131
+ * }
132
+ *
133
+ * // LCA helper function
134
+ * const findLCA = (num1: number, num2: number): number | undefined => {
135
+ * const path1 = bst.getPathToRoot(num1);
136
+ * const path2 = bst.getPathToRoot(num2);
137
+ * // Find the first common ancestor
138
+ * return findFirstCommon(path1, path2);
139
+ * };
140
+ *
141
+ * // Assertions
142
+ * console.log(findLCA(3, 10)); // 7
143
+ * console.log(findLCA(5, 35)); // 15
144
+ * console.log(findLCA(20, 30)); // 25
95
145
  */
96
146
  export class BST<
97
147
  K = any,
@@ -115,8 +165,9 @@ export class BST<
115
165
  super([], options);
116
166
 
117
167
  if (options) {
118
- const { comparator } = options;
119
- if (comparator) this._comparator = comparator;
168
+ const { extractComparable, isReverse } = options;
169
+ if (typeof extractComparable === 'function') this._extractComparable = extractComparable;
170
+ if (isReverse !== undefined) this._isReverse = isReverse;
120
171
  }
121
172
 
122
173
  if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
@@ -132,6 +183,17 @@ export class BST<
132
183
  return this._root;
133
184
  }
134
185
 
186
+ protected _isReverse: boolean = false;
187
+
188
+ /**
189
+ * The above function is a getter method in TypeScript that returns the value of the private property
190
+ * `_isReverse`.
191
+ * @returns The `isReverse` property of the object, which is a boolean value.
192
+ */
193
+ get isReverse(): boolean {
194
+ return this._isReverse;
195
+ }
196
+
135
197
  /**
136
198
  * The function creates a new BSTNode with the given key and value and returns it.
137
199
  * @param {K} key - The key parameter is of type K, which represents the type of the key for the node
@@ -155,8 +217,9 @@ export class BST<
155
217
  return new BST<K, V, R, NODE, TREE>([], {
156
218
  iterationType: this.iterationType,
157
219
  isMapMode: this._isMapMode,
158
- comparator: this._comparator,
220
+ extractComparable: this._extractComparable,
159
221
  toEntryFn: this._toEntryFn,
222
+ isReverse: this._isReverse,
160
223
  ...options
161
224
  }) as TREE;
162
225
  }
@@ -217,11 +280,11 @@ export class BST<
217
280
  * @param {any} key - The `key` parameter is a value that will be checked to determine if it is of
218
281
  * type `K`.
219
282
  * @returns The `override isKey(key: any): key is K` function is returning a boolean value based on
220
- * the result of the `isComparable` function with the condition `this.comparator !==
283
+ * the result of the `isComparable` function with the condition `this._compare !==
221
284
  * this._DEFAULT_COMPARATOR`.
222
285
  */
223
286
  override isKey(key: any): key is K {
224
- return isComparable(key, this.comparator !== this._DEFAULT_COMPARATOR);
287
+ return isComparable(key, this._extractComparable !== undefined);
225
288
  }
226
289
 
227
290
  /**
@@ -248,11 +311,11 @@ export class BST<
248
311
 
249
312
  let current = this._root;
250
313
  while (current !== undefined) {
251
- if (this.comparator(current.key, newNode.key) === 0) {
314
+ if (this._compare(current.key, newNode.key) === 0) {
252
315
  this._replaceNode(current, newNode);
253
316
  if (this._isMapMode) this._setValue(current.key, newValue);
254
317
  return true;
255
- } else if (this.comparator(current.key, newNode.key) > 0) {
318
+ } else if (this._compare(current.key, newNode.key) > 0) {
256
319
  if (current.left === undefined) {
257
320
  current.left = newNode;
258
321
  if (this._isMapMode) this._setValue(newNode?.key, newValue);
@@ -350,7 +413,7 @@ export class BST<
350
413
  }
351
414
 
352
415
  if (keyA !== undefined && keyA !== null && keyB !== undefined && keyB !== null) {
353
- return this.comparator(keyA, keyB);
416
+ return this._compare(keyA, keyB);
354
417
  }
355
418
  return 0;
356
419
  });
@@ -392,64 +455,114 @@ export class BST<
392
455
  return inserted;
393
456
  }
394
457
 
458
+ /**
459
+ * Time Complexity: O(n)
460
+ * Space Complexity: O(1)
461
+ *
462
+ * The `merge` function overrides the base class method by adding elements from another
463
+ * binary search tree.
464
+ * @param anotherTree - `anotherTree` is an instance of a Binary Search Tree (BST) with key type `K`,
465
+ * value type `V`, return type `R`, node type `NODE`, and tree type `TREE`.
466
+ */
467
+ override merge(anotherTree: BST<K, V, R, NODE, TREE>) {
468
+ this.addMany(anotherTree, [], false);
469
+ }
470
+
395
471
  /**
396
472
  * Time Complexity: O(log n)
397
473
  * Space Complexity: O(k + log n)
398
474
  *
399
- * The function `getNodes` in TypeScript overrides the base class method to retrieve nodes based on a
400
- * given keyNodeEntryRawOrPredicate and iteration type.
401
- * @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The `keyNodeEntryRawOrPredicate`
402
- * parameter in the `getNodes` method is used to filter the nodes that will be returned. It can be a
403
- * key, a node, an entry, or a custom keyNodeEntryRawOrPredicate function that determines whether a node should be
404
- * included in the result.
405
- * @param [onlyOne=false] - The `onlyOne` parameter in the `getNodes` method is a boolean flag that
406
- * determines whether to return only the first node that matches the keyNodeEntryRawOrPredicate (`true`) or all nodes
407
- * that match the keyNodeEntryRawOrPredicate (`false`). If `onlyOne` is set to `true`, the method will stop iterating
408
- * and
409
- * @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the
410
- * `getNodes` method is used to specify the starting point for traversing the tree when searching for
411
- * nodes that match a given keyNodeEntryRawOrPredicate. It represents the root node of the subtree where the search
412
- * should begin. If not explicitly provided, the default value for `begin
413
- * @param {IterationType} iterationType - The `iterationType` parameter in the `getNodes` method
414
- * specifies the type of iteration to be performed when traversing the nodes of a binary tree. It can
415
- * have two possible values:
416
- * @returns The `getNodes` method returns an array of nodes that satisfy the given keyNodeEntryRawOrPredicate.
475
+ * The function `search` in TypeScript overrides the search behavior in a binary tree structure based
476
+ * on specified criteria.
477
+ * @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The
478
+ * `keyNodeEntryRawOrPredicate` parameter in the `override search` method can accept one of the
479
+ * following types:
480
+ * @param [onlyOne=false] - The `onlyOne` parameter is a boolean flag that determines whether the
481
+ * search should stop after finding the first matching node. If `onlyOne` is set to `true`, the
482
+ * search will return as soon as a matching node is found. If `onlyOne` is set to `false`, the
483
+ * @param {C} callback - The `callback` parameter in the `override search` function is a function
484
+ * that will be called on each node that matches the search criteria. It is of type `C`, which
485
+ * extends `NodeCallback<NODE>`. The callback function should accept a node of type `NODE` as its
486
+ * argument and
487
+ * @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the `override search`
488
+ * method represents the node from which the search operation will begin. It is the starting point
489
+ * for searching within the tree data structure. The method ensures that the `startNode` is a valid
490
+ * node before proceeding with the search operation. If the `
491
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `override search`
492
+ * function determines the type of iteration to be used during the search operation. It can have two
493
+ * possible values:
494
+ * @returns The `override search` method returns an array of values that match the search criteria
495
+ * specified by the input parameters. The method performs a search operation on a binary tree
496
+ * structure based on the provided key, predicate, and other options. The search results are
497
+ * collected in an array and returned as the output of the method.
417
498
  */
418
- override getNodes(
419
- keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE>,
499
+ override search<C extends NodeCallback<NODE>>(
500
+ keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE> | Range<K>,
420
501
  onlyOne = false,
502
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
421
503
  startNode: BTNRep<K, V, NODE> | R = this._root,
422
504
  iterationType: IterationType = this.iterationType
423
- ): NODE[] {
505
+ ): ReturnType<C>[] {
424
506
  if (keyNodeEntryRawOrPredicate === undefined) return [];
425
507
  if (keyNodeEntryRawOrPredicate === null) return [];
426
508
  startNode = this.ensureNode(startNode);
427
509
  if (!startNode) return [];
428
- const callback = this._ensurePredicate(keyNodeEntryRawOrPredicate);
429
- const ans: NODE[] = [];
510
+ let predicate: NodePredicate<NODE>;
511
+
512
+ const isRange = this.isRange(keyNodeEntryRawOrPredicate);
513
+ // Set predicate based on parameter type
514
+ if (isRange) {
515
+ predicate = node => keyNodeEntryRawOrPredicate.isInRange(node.key, this._comparator);
516
+ } else {
517
+ predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
518
+ }
519
+ const isToLeftByRange = (cur: NODE) => {
520
+ if (isRange) {
521
+ const range = keyNodeEntryRawOrPredicate;
522
+ const leftS = this.isReverse ? range.high : range.low;
523
+ const leftI = this.isReverse ? range.includeHigh : range.includeLow;
524
+ return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0);
525
+ }
526
+ return false;
527
+ };
430
528
 
529
+ const isToRightByRange = (cur: NODE) => {
530
+ if (isRange) {
531
+ const range = keyNodeEntryRawOrPredicate;
532
+ const rightS = this.isReverse ? range.low : range.high;
533
+ const rightI = this.isReverse ? range.includeLow : range.includeLow;
534
+
535
+ return (rightI && this._compare(cur.key, rightS) <= 0) || (!rightI && this._compare(cur.key, rightS) < 0);
536
+ }
537
+ return false;
538
+ };
539
+ const ans: ReturnType<C>[] = [];
431
540
  if (iterationType === 'RECURSIVE') {
432
541
  const dfs = (cur: NODE) => {
433
- if (callback(cur)) {
434
- ans.push(cur);
542
+ if (predicate(cur)) {
543
+ ans.push(callback(cur));
435
544
  if (onlyOne) return;
436
545
  }
437
546
 
438
547
  if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
439
- if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
440
- const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate);
548
+
549
+ if (isRange) {
550
+ if (this.isRealNode(cur.left) && isToLeftByRange(cur)) dfs(cur.left);
551
+ if (this.isRealNode(cur.right) && isToRightByRange(cur)) dfs(cur.right);
552
+ } else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
553
+ const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
441
554
  if (
442
555
  this.isRealNode(cur.left) &&
443
556
  benchmarkKey !== null &&
444
557
  benchmarkKey !== undefined &&
445
- this.comparator(cur.key, benchmarkKey) > 0
558
+ this._compare(cur.key, benchmarkKey) > 0
446
559
  )
447
560
  dfs(cur.left);
448
561
  if (
449
562
  this.isRealNode(cur.right) &&
450
563
  benchmarkKey !== null &&
451
564
  benchmarkKey !== undefined &&
452
- this.comparator(cur.key, benchmarkKey) < 0
565
+ this._compare(cur.key, benchmarkKey) < 0
453
566
  )
454
567
  dfs(cur.right);
455
568
  } else {
@@ -463,24 +576,27 @@ export class BST<
463
576
  const stack = [startNode];
464
577
  while (stack.length > 0) {
465
578
  const cur = stack.pop()!;
466
- if (callback(cur)) {
467
- ans.push(cur);
579
+ if (predicate(cur)) {
580
+ ans.push(callback(cur));
468
581
  if (onlyOne) return ans;
469
582
  }
470
- if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
471
- const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate);
583
+ if (isRange) {
584
+ if (this.isRealNode(cur.left) && isToLeftByRange(cur)) stack.push(cur.left);
585
+ if (this.isRealNode(cur.right) && isToRightByRange(cur)) stack.push(cur.right);
586
+ } else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
587
+ const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
472
588
  if (
473
589
  this.isRealNode(cur.right) &&
474
590
  benchmarkKey !== null &&
475
591
  benchmarkKey !== undefined &&
476
- this.comparator(cur.key, benchmarkKey) < 0
592
+ this._compare(cur.key, benchmarkKey) < 0
477
593
  )
478
594
  stack.push(cur.right);
479
595
  if (
480
596
  this.isRealNode(cur.left) &&
481
597
  benchmarkKey !== null &&
482
598
  benchmarkKey !== undefined &&
483
- this.comparator(cur.key, benchmarkKey) > 0
599
+ this._compare(cur.key, benchmarkKey) > 0
484
600
  )
485
601
  stack.push(cur.left);
486
602
  } else {
@@ -521,23 +637,6 @@ export class BST<
521
637
  return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined;
522
638
  }
523
639
 
524
- /**
525
- * Time Complexity: O(log n)
526
- * Space Complexity: O(1)
527
- *
528
- * The function `getNodeByKey` returns a node with a specific key from a tree data structure.
529
- * @param {K} key - The key parameter is the value used to search for a specific node in the tree. It
530
- * is typically a unique identifier or a value that can be used to determine the position of the node
531
- * in the tree structure.
532
- * @param {IterationType} [iterationType=ITERATIVE] - The `iterationType` parameter is an optional
533
- * parameter that specifies the type of iteration to be used when searching for a node in the tree.
534
- * It has a default value of `'ITERATIVE'`.
535
- * @returns The method is returning a NODE object or undefined.
536
- */
537
- override getNodeByKey(key: K, iterationType: IterationType = this.iterationType): OptNode<NODE> {
538
- return this.getNode(key, this._root, iterationType);
539
- }
540
-
541
640
  /**
542
641
  * Time complexity: O(n)
543
642
  * Space complexity: O(n)
@@ -654,9 +753,9 @@ export class BST<
654
753
 
655
754
  if (iterationType === 'RECURSIVE') {
656
755
  const dfs = (cur: NODE) => {
657
- const compared = this.comparator(cur.key, targetKey);
756
+ const compared = this._compare(cur.key, targetKey);
658
757
  if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
659
-
758
+ // TODO here can be optimized to O(log n)
660
759
  if (this.isRealNode(cur.left)) dfs(cur.left);
661
760
  if (this.isRealNode(cur.right)) dfs(cur.right);
662
761
  };
@@ -668,7 +767,7 @@ export class BST<
668
767
  while (queue.size > 0) {
669
768
  const cur = queue.shift();
670
769
  if (this.isRealNode(cur)) {
671
- const compared = this.comparator(cur.key, targetKey);
770
+ const compared = this._compare(cur.key, targetKey);
672
771
  if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
673
772
 
674
773
  if (this.isRealNode(cur.left)) queue.push(cur.left);
@@ -786,19 +885,26 @@ export class BST<
786
885
  return balanced;
787
886
  }
788
887
 
789
- protected _DEFAULT_COMPARATOR = (a: K, b: K): number => {
888
+ protected _comparator: Comparator<K> = (a: K, b: K): number => {
889
+ if (isComparable(a) && isComparable(b)) {
890
+ if (a > b) return 1;
891
+ if (a < b) return -1;
892
+ return 0;
893
+ }
894
+ if (this._extractComparable) {
895
+ if (this._extractComparable(a) > this._extractComparable(b)) return 1;
896
+ if (this._extractComparable(a) < this._extractComparable(b)) return -1;
897
+ return 0;
898
+ }
790
899
  if (typeof a === 'object' || typeof b === 'object') {
791
900
  throw TypeError(
792
- `When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
901
+ `When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
793
902
  );
794
903
  }
795
- if (a > b) return 1;
796
- if (a < b) return -1;
904
+
797
905
  return 0;
798
906
  };
799
907
 
800
- protected _comparator: Comparator<K> = this._DEFAULT_COMPARATOR;
801
-
802
908
  /**
803
909
  * The function returns the value of the _comparator property.
804
910
  * @returns The `_comparator` property is being returned.
@@ -807,6 +913,17 @@ export class BST<
807
913
  return this._comparator;
808
914
  }
809
915
 
916
+ protected _extractComparable?: (key: K) => Comparable;
917
+
918
+ /**
919
+ * This function returns the value of the `_extractComparable` property.
920
+ * @returns The method `extractComparable()` is being returned, which is a getter method for the
921
+ * `_extractComparable` property.
922
+ */
923
+ get extractComparable() {
924
+ return this._extractComparable;
925
+ }
926
+
810
927
  /**
811
928
  * The function sets the root of a tree-like structure and updates the parent property of the new
812
929
  * root.
@@ -818,4 +935,8 @@ export class BST<
818
935
  }
819
936
  this._root = v;
820
937
  }
938
+
939
+ protected _compare(a: K, b: K) {
940
+ return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b);
941
+ }
821
942
  }
@@ -51,6 +51,10 @@ export class RedBlackTreeNode<
51
51
  }
52
52
  }
53
53
 
54
+ /**
55
+ * 1. Efficient self-balancing, but not completely balanced. Compared with AVLTree, the addition and deletion efficiency is high but the query efficiency is slightly lower.
56
+ * 2. It is BST itself. Compared with Heap which is not completely ordered, RedBlackTree is completely ordered.
57
+ */
54
58
  export class RedBlackTree<
55
59
  K = any,
56
60
  V = any,
@@ -119,7 +123,7 @@ export class RedBlackTree<
119
123
  return new RedBlackTree<K, V, R, NODE, TREE>([], {
120
124
  iterationType: this.iterationType,
121
125
  isMapMode: this._isMapMode,
122
- comparator: this._comparator,
126
+ extractComparable: this._extractComparable,
123
127
  toEntryFn: this._toEntryFn,
124
128
  ...options
125
129
  }) as TREE;
@@ -351,7 +355,7 @@ export class RedBlackTree<
351
355
 
352
356
  while (this.isRealNode(current)) {
353
357
  parent = current;
354
- const compared = this.comparator(node.key, current.key);
358
+ const compared = this._compare(node.key, current.key);
355
359
  if (compared < 0) {
356
360
  current = current.left ?? this.NIL;
357
361
  } else if (compared > 0) {
@@ -139,7 +139,7 @@ export class TreeMultiMap<
139
139
  return new TreeMultiMap<K, V, R, NODE, TREE>([], {
140
140
  iterationType: this.iterationType,
141
141
  isMapMode: this._isMapMode,
142
- comparator: this._comparator,
142
+ extractComparable: this._extractComparable,
143
143
  toEntryFn: this._toEntryFn,
144
144
  ...options
145
145
  }) as TREE;
@@ -173,8 +173,8 @@ export class TreeMultiMap<
173
173
  if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
174
174
  }
175
175
 
176
- if (this._toEntryFn) {
177
- const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R);
176
+ if (this.isRaw(keyNodeEntryOrRaw)) {
177
+ const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
178
178
  const finalValue = value ?? entryValue;
179
179
  if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
180
180
  }
@@ -19,9 +19,9 @@ import { IterableElementBase } from '../base';
19
19
  * 6. Non-linear Search: While a heap allows rapid access to its largest or smallest element, it is less efficient for other operations, such as searching for a specific element, as it is not designed for these tasks.
20
20
  * 7. Efficient Sorting Algorithms: For example, heap sort. Heap sort uses the properties of a heap to sort elements.
21
21
  * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prime's minimum-spanning tree algorithm, which use heaps to improve performance.
22
- * @example
23
- * // Use Heap to sort an array
24
- * function heapSort(arr: number[]): number[] {
22
+ * @example
23
+ * // Use Heap to sort an array
24
+ * function heapSort(arr: number[]): number[] {
25
25
  * const heap = new Heap<number>(arr, { comparator: (a, b) => a - b });
26
26
  * const sorted: number[] = [];
27
27
  * while (!heap.isEmpty()) {
@@ -29,12 +29,12 @@ import { IterableElementBase } from '../base';
29
29
  * }
30
30
  * return sorted;
31
31
  * }
32
- *
32
+ *
33
33
  * const array = [5, 3, 8, 4, 1, 2];
34
34
  * console.log(heapSort(array)); // [1, 2, 3, 4, 5, 8]
35
- * @example
36
- * // Use Heap to solve top k problems
37
- * function topKElements(arr: number[], k: number): number[] {
35
+ * @example
36
+ * // Use Heap to solve top k problems
37
+ * function topKElements(arr: number[], k: number): number[] {
38
38
  * const heap = new Heap<number>([], { comparator: (a, b) => b - a }); // Max heap
39
39
  * arr.forEach(num => {
40
40
  * heap.add(num);
@@ -42,28 +42,28 @@ import { IterableElementBase } from '../base';
42
42
  * });
43
43
  * return heap.toArray();
44
44
  * }
45
- *
45
+ *
46
46
  * const numbers = [10, 30, 20, 5, 15, 25];
47
47
  * console.log(topKElements(numbers, 3)); // [15, 10, 5]
48
- * @example
49
- * // Use Heap to merge sorted sequences
50
- * function mergeSortedSequences(sequences: number[][]): number[] {
48
+ * @example
49
+ * // Use Heap to merge sorted sequences
50
+ * function mergeSortedSequences(sequences: number[][]): number[] {
51
51
  * const heap = new Heap<{ value: number; seqIndex: number; itemIndex: number }>([], {
52
52
  * comparator: (a, b) => a.value - b.value // Min heap
53
53
  * });
54
- *
54
+ *
55
55
  * // Initialize heap
56
56
  * sequences.forEach((seq, seqIndex) => {
57
57
  * if (seq.length) {
58
58
  * heap.add({ value: seq[0], seqIndex, itemIndex: 0 });
59
59
  * }
60
60
  * });
61
- *
61
+ *
62
62
  * const merged: number[] = [];
63
63
  * while (!heap.isEmpty()) {
64
64
  * const { value, seqIndex, itemIndex } = heap.poll()!;
65
65
  * merged.push(value);
66
- *
66
+ *
67
67
  * if (itemIndex + 1 < sequences[seqIndex].length) {
68
68
  * heap.add({
69
69
  * value: sequences[seqIndex][itemIndex + 1],
@@ -72,42 +72,42 @@ import { IterableElementBase } from '../base';
72
72
  * });
73
73
  * }
74
74
  * }
75
- *
75
+ *
76
76
  * return merged;
77
77
  * }
78
- *
78
+ *
79
79
  * const sequences = [
80
80
  * [1, 4, 7],
81
81
  * [2, 5, 8],
82
82
  * [3, 6, 9]
83
83
  * ];
84
84
  * console.log(mergeSortedSequences(sequences)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
85
- * @example
86
- * // Use Heap to dynamically maintain the median
87
- * class MedianFinder {
85
+ * @example
86
+ * // Use Heap to dynamically maintain the median
87
+ * class MedianFinder {
88
88
  * private low: MaxHeap<number>; // Max heap, stores the smaller half
89
89
  * private high: MinHeap<number>; // Min heap, stores the larger half
90
- *
90
+ *
91
91
  * constructor() {
92
92
  * this.low = new MaxHeap<number>([]);
93
93
  * this.high = new MinHeap<number>([]);
94
94
  * }
95
- *
95
+ *
96
96
  * addNum(num: number): void {
97
97
  * if (this.low.isEmpty() || num <= this.low.peek()!) this.low.add(num);
98
98
  * else this.high.add(num);
99
- *
99
+ *
100
100
  * // Balance heaps
101
101
  * if (this.low.size > this.high.size + 1) this.high.add(this.low.poll()!);
102
102
  * else if (this.high.size > this.low.size) this.low.add(this.high.poll()!);
103
103
  * }
104
- *
104
+ *
105
105
  * findMedian(): number {
106
106
  * if (this.low.size === this.high.size) return (this.low.peek()! + this.high.peek()!) / 2;
107
107
  * return this.low.peek()!;
108
108
  * }
109
109
  * }
110
- *
110
+ *
111
111
  * const medianFinder = new MedianFinder();
112
112
  * medianFinder.addNum(10);
113
113
  * console.log(medianFinder.findMedian()); // 10
@@ -119,42 +119,42 @@ import { IterableElementBase } from '../base';
119
119
  * console.log(medianFinder.findMedian()); // 25
120
120
  * medianFinder.addNum(50);
121
121
  * console.log(medianFinder.findMedian()); // 30
122
- * @example
123
- * // Use Heap for load balancing
124
- * function loadBalance(requests: number[], servers: number): number[] {
122
+ * @example
123
+ * // Use Heap for load balancing
124
+ * function loadBalance(requests: number[], servers: number): number[] {
125
125
  * const serverHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // min heap
126
126
  * const serverLoads = new Array(servers).fill(0);
127
- *
127
+ *
128
128
  * for (let i = 0; i < servers; i++) {
129
129
  * serverHeap.add({ id: i, load: 0 });
130
130
  * }
131
- *
131
+ *
132
132
  * requests.forEach(req => {
133
133
  * const server = serverHeap.poll()!;
134
134
  * serverLoads[server.id] += req;
135
135
  * server.load += req;
136
136
  * serverHeap.add(server); // The server after updating the load is re-entered into the heap
137
137
  * });
138
- *
138
+ *
139
139
  * return serverLoads;
140
140
  * }
141
- *
141
+ *
142
142
  * const requests = [5, 2, 8, 3, 7];
143
143
  * console.log(loadBalance(requests, 3)); // [12, 8, 5]
144
- * @example
145
- * // Use Heap to schedule tasks
146
- * type Task = [string, number];
147
- *
144
+ * @example
145
+ * // Use Heap to schedule tasks
146
+ * type Task = [string, number];
147
+ *
148
148
  * function scheduleTasks(tasks: Task[], machines: number): Map<number, Task[]> {
149
149
  * const machineHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // Min heap
150
150
  * const allocation = new Map<number, Task[]>();
151
- *
151
+ *
152
152
  * // Initialize the load on each machine
153
153
  * for (let i = 0; i < machines; i++) {
154
154
  * machineHeap.add({ id: i, load: 0 });
155
155
  * allocation.set(i, []);
156
156
  * }
157
- *
157
+ *
158
158
  * // Assign tasks
159
159
  * tasks.forEach(([task, load]) => {
160
160
  * const machine = machineHeap.poll()!;
@@ -162,10 +162,10 @@ import { IterableElementBase } from '../base';
162
162
  * machine.load += load;
163
163
  * machineHeap.add(machine); // The machine after updating the load is re-entered into the heap
164
164
  * });
165
- *
165
+ *
166
166
  * return allocation;
167
167
  * }
168
- *
168
+ *
169
169
  * const tasks: Task[] = [
170
170
  * ['Task1', 3],
171
171
  * ['Task2', 1],