doubly-linked-list-typed 1.53.6 → 1.53.8

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 (62) hide show
  1. package/README.md +6 -6
  2. package/dist/common/index.d.ts +12 -0
  3. package/dist/common/index.js +28 -0
  4. package/dist/data-structures/binary-tree/avl-tree-multi-map.d.ts +1 -1
  5. package/dist/data-structures/binary-tree/avl-tree-multi-map.js +9 -12
  6. package/dist/data-structures/binary-tree/avl-tree.js +2 -2
  7. package/dist/data-structures/binary-tree/binary-tree.d.ts +55 -20
  8. package/dist/data-structures/binary-tree/binary-tree.js +102 -68
  9. package/dist/data-structures/binary-tree/bst.d.ts +131 -37
  10. package/dist/data-structures/binary-tree/bst.js +222 -69
  11. package/dist/data-structures/binary-tree/index.d.ts +1 -1
  12. package/dist/data-structures/binary-tree/index.js +1 -1
  13. package/dist/data-structures/binary-tree/{rb-tree.d.ts → red-black-tree.d.ts} +53 -0
  14. package/dist/data-structures/binary-tree/{rb-tree.js → red-black-tree.js} +56 -3
  15. package/dist/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
  16. package/dist/data-structures/binary-tree/tree-multi-map.js +7 -7
  17. package/dist/data-structures/hash/hash-map.d.ts +30 -0
  18. package/dist/data-structures/hash/hash-map.js +30 -0
  19. package/dist/data-structures/heap/heap.d.ts +26 -9
  20. package/dist/data-structures/heap/heap.js +37 -17
  21. package/dist/data-structures/linked-list/doubly-linked-list.d.ts +54 -9
  22. package/dist/data-structures/linked-list/doubly-linked-list.js +80 -19
  23. package/dist/data-structures/linked-list/singly-linked-list.d.ts +35 -2
  24. package/dist/data-structures/linked-list/singly-linked-list.js +55 -11
  25. package/dist/data-structures/queue/deque.d.ts +37 -8
  26. package/dist/data-structures/queue/deque.js +73 -29
  27. package/dist/data-structures/queue/queue.d.ts +41 -1
  28. package/dist/data-structures/queue/queue.js +51 -9
  29. package/dist/data-structures/stack/stack.d.ts +27 -10
  30. package/dist/data-structures/stack/stack.js +39 -20
  31. package/dist/data-structures/trie/trie.d.ts +111 -6
  32. package/dist/data-structures/trie/trie.js +123 -14
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.js +2 -1
  35. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
  36. package/dist/types/data-structures/binary-tree/bst.d.ts +3 -2
  37. package/dist/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
  38. package/dist/types/utils/utils.d.ts +10 -6
  39. package/dist/utils/utils.js +4 -2
  40. package/package.json +2 -2
  41. package/src/common/index.ts +25 -0
  42. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +9 -11
  43. package/src/data-structures/binary-tree/avl-tree.ts +3 -2
  44. package/src/data-structures/binary-tree/binary-tree.ts +110 -66
  45. package/src/data-structures/binary-tree/bst.ts +232 -72
  46. package/src/data-structures/binary-tree/index.ts +1 -1
  47. package/src/data-structures/binary-tree/{rb-tree.ts → red-black-tree.ts} +56 -3
  48. package/src/data-structures/binary-tree/tree-multi-map.ts +6 -6
  49. package/src/data-structures/hash/hash-map.ts +30 -0
  50. package/src/data-structures/heap/heap.ts +72 -49
  51. package/src/data-structures/linked-list/doubly-linked-list.ts +173 -105
  52. package/src/data-structures/linked-list/singly-linked-list.ts +61 -11
  53. package/src/data-structures/queue/deque.ts +72 -28
  54. package/src/data-structures/queue/queue.ts +50 -7
  55. package/src/data-structures/stack/stack.ts +39 -20
  56. package/src/data-structures/trie/trie.ts +123 -13
  57. package/src/index.ts +2 -1
  58. package/src/types/data-structures/binary-tree/binary-tree.ts +1 -1
  59. package/src/types/data-structures/binary-tree/bst.ts +3 -2
  60. package/src/types/data-structures/binary-tree/rb-tree.ts +1 -1
  61. package/src/types/utils/utils.ts +16 -10
  62. 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,62 @@ 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
+ * // Merge 3 sorted datasets
99
+ * const dataset1 = new BST<number, string>([
100
+ * [1, 'A'],
101
+ * [7, 'G']
102
+ * ]);
103
+ * const dataset2 = [
104
+ * [2, 'B'],
105
+ * [6, 'F']
106
+ * ];
107
+ * const dataset3 = new BST<number, string>([
108
+ * [3, 'C'],
109
+ * [5, 'E'],
110
+ * [4, 'D']
111
+ * ]);
112
+ *
113
+ * // Merge datasets into a single BinarySearchTree
114
+ * const merged = new BST<number, string>(dataset1);
115
+ * merged.addMany(dataset2);
116
+ * merged.merge(dataset3);
117
+ *
118
+ * // Verify merged dataset is in sorted order
119
+ * console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
120
+ * @example
121
+ * // Find elements in a range
122
+ * const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
123
+ * console.log(bst.search(new Range(5, 10))); // [10, 5, 7]
124
+ * console.log(bst.rangeSearch([4, 12], node => node.key.toString())); // ['10', '12', '5', '7']
125
+ * console.log(bst.search(new Range(4, 12, true, false))); // [10, 5, 7]
126
+ * console.log(bst.rangeSearch([15, 20])); // [15, 18]
127
+ * console.log(bst.search(new Range(15, 20, false))); // [18]
128
+ * @example
129
+ * // Find lowest common ancestor
130
+ * const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
131
+ *
132
+ * // LCA helper function
133
+ * const findLCA = (num1: number, num2: number): number | undefined => {
134
+ * const path1 = bst.getPathToRoot(num1);
135
+ * const path2 = bst.getPathToRoot(num2);
136
+ * // Find the first common ancestor
137
+ * return findFirstCommon(path1, path2);
138
+ * };
139
+ *
140
+ * function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
141
+ * for (const num of arr1) {
142
+ * if (arr2.indexOf(num) !== -1) {
143
+ * return num;
144
+ * }
145
+ * }
146
+ * return undefined;
147
+ * }
148
+ *
149
+ * // Assertions
150
+ * console.log(findLCA(3, 10)); // 7
151
+ * console.log(findLCA(5, 35)); // 15
152
+ * console.log(findLCA(20, 30)); // 25
95
153
  */
96
154
  export class BST<
97
155
  K = any,
@@ -115,8 +173,9 @@ export class BST<
115
173
  super([], options);
116
174
 
117
175
  if (options) {
118
- const { comparator } = options;
119
- if (comparator) this._comparator = comparator;
176
+ const { extractComparable, isReverse } = options;
177
+ if (typeof extractComparable === 'function') this._extractComparable = extractComparable;
178
+ if (isReverse !== undefined) this._isReverse = isReverse;
120
179
  }
121
180
 
122
181
  if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
@@ -132,6 +191,17 @@ export class BST<
132
191
  return this._root;
133
192
  }
134
193
 
194
+ protected _isReverse: boolean = false;
195
+
196
+ /**
197
+ * The above function is a getter method in TypeScript that returns the value of the private property
198
+ * `_isReverse`.
199
+ * @returns The `isReverse` property of the object, which is a boolean value.
200
+ */
201
+ get isReverse(): boolean {
202
+ return this._isReverse;
203
+ }
204
+
135
205
  /**
136
206
  * The function creates a new BSTNode with the given key and value and returns it.
137
207
  * @param {K} key - The key parameter is of type K, which represents the type of the key for the node
@@ -155,8 +225,9 @@ export class BST<
155
225
  return new BST<K, V, R, NODE, TREE>([], {
156
226
  iterationType: this.iterationType,
157
227
  isMapMode: this._isMapMode,
158
- comparator: this._comparator,
228
+ extractComparable: this._extractComparable,
159
229
  toEntryFn: this._toEntryFn,
230
+ isReverse: this._isReverse,
160
231
  ...options
161
232
  }) as TREE;
162
233
  }
@@ -170,11 +241,11 @@ export class BST<
170
241
  * value associated with a key in a key-value pair.
171
242
  * @returns either a NODE object or undefined.
172
243
  */
173
- override keyValueNodeEntryRawToNodeAndValue(
244
+ protected override _keyValueNodeEntryRawToNodeAndValue(
174
245
  keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R,
175
246
  value?: V
176
247
  ): [OptNode<NODE>, V | undefined] {
177
- const [node, entryValue] = super.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
248
+ const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
178
249
  if (node === null) return [undefined, undefined];
179
250
  return [node, value ?? entryValue];
180
251
  }
@@ -217,11 +288,11 @@ export class BST<
217
288
  * @param {any} key - The `key` parameter is a value that will be checked to determine if it is of
218
289
  * type `K`.
219
290
  * @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 !==
291
+ * the result of the `isComparable` function with the condition `this._compare !==
221
292
  * this._DEFAULT_COMPARATOR`.
222
293
  */
223
294
  override isKey(key: any): key is K {
224
- return isComparable(key, this.comparator !== this._DEFAULT_COMPARATOR);
295
+ return isComparable(key, this._extractComparable !== undefined);
225
296
  }
226
297
 
227
298
  /**
@@ -236,7 +307,7 @@ export class BST<
236
307
  * @returns a boolean value.
237
308
  */
238
309
  override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V): boolean {
239
- const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
310
+ const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
240
311
  if (newNode === undefined) return false;
241
312
 
242
313
  if (this._root === undefined) {
@@ -248,11 +319,11 @@ export class BST<
248
319
 
249
320
  let current = this._root;
250
321
  while (current !== undefined) {
251
- if (this.comparator(current.key, newNode.key) === 0) {
322
+ if (this._compare(current.key, newNode.key) === 0) {
252
323
  this._replaceNode(current, newNode);
253
324
  if (this._isMapMode) this._setValue(current.key, newValue);
254
325
  return true;
255
- } else if (this.comparator(current.key, newNode.key) > 0) {
326
+ } else if (this._compare(current.key, newNode.key) > 0) {
256
327
  if (current.left === undefined) {
257
328
  current.left = newNode;
258
329
  if (this._isMapMode) this._setValue(newNode?.key, newValue);
@@ -350,7 +421,7 @@ export class BST<
350
421
  }
351
422
 
352
423
  if (keyA !== undefined && keyA !== null && keyB !== undefined && keyB !== null) {
353
- return this.comparator(keyA, keyB);
424
+ return this._compare(keyA, keyB);
354
425
  }
355
426
  return 0;
356
427
  });
@@ -392,64 +463,114 @@ export class BST<
392
463
  return inserted;
393
464
  }
394
465
 
466
+ /**
467
+ * Time Complexity: O(n)
468
+ * Space Complexity: O(1)
469
+ *
470
+ * The `merge` function overrides the base class method by adding elements from another
471
+ * binary search tree.
472
+ * @param anotherTree - `anotherTree` is an instance of a Binary Search Tree (BST) with key type `K`,
473
+ * value type `V`, return type `R`, node type `NODE`, and tree type `TREE`.
474
+ */
475
+ override merge(anotherTree: BST<K, V, R, NODE, TREE>) {
476
+ this.addMany(anotherTree, [], false);
477
+ }
478
+
395
479
  /**
396
480
  * Time Complexity: O(log n)
397
481
  * Space Complexity: O(k + log n)
398
482
  *
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.
483
+ * The function `search` in TypeScript overrides the search behavior in a binary tree structure based
484
+ * on specified criteria.
485
+ * @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The
486
+ * `keyNodeEntryRawOrPredicate` parameter in the `override search` method can accept one of the
487
+ * following types:
488
+ * @param [onlyOne=false] - The `onlyOne` parameter is a boolean flag that determines whether the
489
+ * search should stop after finding the first matching node. If `onlyOne` is set to `true`, the
490
+ * search will return as soon as a matching node is found. If `onlyOne` is set to `false`, the
491
+ * @param {C} callback - The `callback` parameter in the `override search` function is a function
492
+ * that will be called on each node that matches the search criteria. It is of type `C`, which
493
+ * extends `NodeCallback<NODE>`. The callback function should accept a node of type `NODE` as its
494
+ * argument and
495
+ * @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the `override search`
496
+ * method represents the node from which the search operation will begin. It is the starting point
497
+ * for searching within the tree data structure. The method ensures that the `startNode` is a valid
498
+ * node before proceeding with the search operation. If the `
499
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `override search`
500
+ * function determines the type of iteration to be used during the search operation. It can have two
501
+ * possible values:
502
+ * @returns The `override search` method returns an array of values that match the search criteria
503
+ * specified by the input parameters. The method performs a search operation on a binary tree
504
+ * structure based on the provided key, predicate, and other options. The search results are
505
+ * collected in an array and returned as the output of the method.
417
506
  */
418
- override getNodes(
419
- keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE>,
507
+ override search<C extends NodeCallback<NODE>>(
508
+ keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE> | Range<K>,
420
509
  onlyOne = false,
510
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
421
511
  startNode: BTNRep<K, V, NODE> | R = this._root,
422
512
  iterationType: IterationType = this.iterationType
423
- ): NODE[] {
513
+ ): ReturnType<C>[] {
424
514
  if (keyNodeEntryRawOrPredicate === undefined) return [];
425
515
  if (keyNodeEntryRawOrPredicate === null) return [];
426
516
  startNode = this.ensureNode(startNode);
427
517
  if (!startNode) return [];
428
- const callback = this._ensurePredicate(keyNodeEntryRawOrPredicate);
429
- const ans: NODE[] = [];
518
+ let predicate: NodePredicate<NODE>;
430
519
 
520
+ const isRange = this.isRange(keyNodeEntryRawOrPredicate);
521
+ // Set predicate based on parameter type
522
+ if (isRange) {
523
+ predicate = node => keyNodeEntryRawOrPredicate.isInRange(node.key, this._comparator);
524
+ } else {
525
+ predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
526
+ }
527
+ const isToLeftByRange = (cur: NODE) => {
528
+ if (isRange) {
529
+ const range = keyNodeEntryRawOrPredicate;
530
+ const leftS = this.isReverse ? range.high : range.low;
531
+ const leftI = this.isReverse ? range.includeHigh : range.includeLow;
532
+ return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0);
533
+ }
534
+ return false;
535
+ };
536
+
537
+ const isToRightByRange = (cur: NODE) => {
538
+ if (isRange) {
539
+ const range = keyNodeEntryRawOrPredicate;
540
+ const rightS = this.isReverse ? range.low : range.high;
541
+ const rightI = this.isReverse ? range.includeLow : range.includeLow;
542
+
543
+ return (rightI && this._compare(cur.key, rightS) <= 0) || (!rightI && this._compare(cur.key, rightS) < 0);
544
+ }
545
+ return false;
546
+ };
547
+ const ans: ReturnType<C>[] = [];
431
548
  if (iterationType === 'RECURSIVE') {
432
549
  const dfs = (cur: NODE) => {
433
- if (callback(cur)) {
434
- ans.push(cur);
550
+ if (predicate(cur)) {
551
+ ans.push(callback(cur));
435
552
  if (onlyOne) return;
436
553
  }
437
554
 
438
555
  if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
439
- if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
440
- const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate);
556
+
557
+ if (isRange) {
558
+ if (this.isRealNode(cur.left) && isToLeftByRange(cur)) dfs(cur.left);
559
+ if (this.isRealNode(cur.right) && isToRightByRange(cur)) dfs(cur.right);
560
+ } else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
561
+ const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
441
562
  if (
442
563
  this.isRealNode(cur.left) &&
443
564
  benchmarkKey !== null &&
444
565
  benchmarkKey !== undefined &&
445
- this.comparator(cur.key, benchmarkKey) > 0
566
+ this._compare(cur.key, benchmarkKey) > 0
446
567
  )
447
568
  dfs(cur.left);
448
569
  if (
449
570
  this.isRealNode(cur.right) &&
450
571
  benchmarkKey !== null &&
451
572
  benchmarkKey !== undefined &&
452
- this.comparator(cur.key, benchmarkKey) < 0
573
+ this._compare(cur.key, benchmarkKey) < 0
453
574
  )
454
575
  dfs(cur.right);
455
576
  } else {
@@ -463,24 +584,27 @@ export class BST<
463
584
  const stack = [startNode];
464
585
  while (stack.length > 0) {
465
586
  const cur = stack.pop()!;
466
- if (callback(cur)) {
467
- ans.push(cur);
587
+ if (predicate(cur)) {
588
+ ans.push(callback(cur));
468
589
  if (onlyOne) return ans;
469
590
  }
470
- if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
471
- const benchmarkKey = this._getKey(keyNodeEntryRawOrPredicate);
591
+ if (isRange) {
592
+ if (this.isRealNode(cur.left) && isToLeftByRange(cur)) stack.push(cur.left);
593
+ if (this.isRealNode(cur.right) && isToRightByRange(cur)) stack.push(cur.right);
594
+ } else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
595
+ const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
472
596
  if (
473
597
  this.isRealNode(cur.right) &&
474
598
  benchmarkKey !== null &&
475
599
  benchmarkKey !== undefined &&
476
- this.comparator(cur.key, benchmarkKey) < 0
600
+ this._compare(cur.key, benchmarkKey) < 0
477
601
  )
478
602
  stack.push(cur.right);
479
603
  if (
480
604
  this.isRealNode(cur.left) &&
481
605
  benchmarkKey !== null &&
482
606
  benchmarkKey !== undefined &&
483
- this.comparator(cur.key, benchmarkKey) > 0
607
+ this._compare(cur.key, benchmarkKey) > 0
484
608
  )
485
609
  stack.push(cur.left);
486
610
  } else {
@@ -493,6 +617,37 @@ export class BST<
493
617
  return ans;
494
618
  }
495
619
 
620
+ /**
621
+ * Time Complexity: O(log n)
622
+ * Space Complexity: O(n)
623
+ *
624
+ * The `rangeSearch` function searches for nodes within a specified range in a binary search tree.
625
+ * @param {Range<K> | [K, K]} range - The `range` parameter in the `rangeSearch` function can be
626
+ * either a `Range` object or an array of two elements representing the range boundaries.
627
+ * @param {C} callback - The `callback` parameter in the `rangeSearch` function is a callback
628
+ * function that is used to process each node that is found within the specified range during the
629
+ * search operation. It is of type `NodeCallback<NODE>`, where `NODE` is the type of nodes in the
630
+ * data structure.
631
+ * @param {BTNRep<K, V, NODE> | R} startNode - The `startNode` parameter in the `rangeSearch`
632
+ * function represents the node from which the search for nodes within the specified range will
633
+ * begin. It is the starting point for the range search operation.
634
+ * @param {IterationType} iterationType - The `iterationType` parameter in the `rangeSearch` function
635
+ * is used to specify the type of iteration to be performed during the search operation. It has a
636
+ * default value of `this.iterationType`, which suggests that it is likely a property of the class or
637
+ * object that the `rangeSearch`
638
+ * @returns The `rangeSearch` function is returning the result of calling the `search` method with
639
+ * the specified parameters.
640
+ */
641
+ rangeSearch<C extends NodeCallback<NODE>>(
642
+ range: Range<K> | [K, K],
643
+ callback: C = this._DEFAULT_NODE_CALLBACK as C,
644
+ startNode: BTNRep<K, V, NODE> | R = this._root,
645
+ iterationType: IterationType = this.iterationType
646
+ ) {
647
+ const searchRange: Range<K> = range instanceof Range ? range : new Range(range[0], range[1]);
648
+ return this.search(searchRange, false, callback, startNode, iterationType);
649
+ }
650
+
496
651
  /**
497
652
  * Time Complexity: O(log n)
498
653
  * Space Complexity: O(1)
@@ -521,23 +676,6 @@ export class BST<
521
676
  return this.getNodes(keyNodeEntryRawOrPredicate, true, startNode, iterationType)[0] ?? undefined;
522
677
  }
523
678
 
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
679
  /**
542
680
  * Time complexity: O(n)
543
681
  * Space complexity: O(n)
@@ -654,9 +792,9 @@ export class BST<
654
792
 
655
793
  if (iterationType === 'RECURSIVE') {
656
794
  const dfs = (cur: NODE) => {
657
- const compared = this.comparator(cur.key, targetKey);
795
+ const compared = this._compare(cur.key, targetKey);
658
796
  if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
659
-
797
+ // TODO here can be optimized to O(log n)
660
798
  if (this.isRealNode(cur.left)) dfs(cur.left);
661
799
  if (this.isRealNode(cur.right)) dfs(cur.right);
662
800
  };
@@ -668,7 +806,7 @@ export class BST<
668
806
  while (queue.size > 0) {
669
807
  const cur = queue.shift();
670
808
  if (this.isRealNode(cur)) {
671
- const compared = this.comparator(cur.key, targetKey);
809
+ const compared = this._compare(cur.key, targetKey);
672
810
  if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
673
811
 
674
812
  if (this.isRealNode(cur.left)) queue.push(cur.left);
@@ -786,19 +924,26 @@ export class BST<
786
924
  return balanced;
787
925
  }
788
926
 
789
- protected _DEFAULT_COMPARATOR = (a: K, b: K): number => {
927
+ protected _comparator: Comparator<K> = (a: K, b: K): number => {
928
+ if (isComparable(a) && isComparable(b)) {
929
+ if (a > b) return 1;
930
+ if (a < b) return -1;
931
+ return 0;
932
+ }
933
+ if (this._extractComparable) {
934
+ if (this._extractComparable(a) > this._extractComparable(b)) return 1;
935
+ if (this._extractComparable(a) < this._extractComparable(b)) return -1;
936
+ return 0;
937
+ }
790
938
  if (typeof a === 'object' || typeof b === 'object') {
791
939
  throw TypeError(
792
- `When comparing object types, a custom comparator must be defined in the constructor's options parameter.`
940
+ `When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
793
941
  );
794
942
  }
795
- if (a > b) return 1;
796
- if (a < b) return -1;
943
+
797
944
  return 0;
798
945
  };
799
946
 
800
- protected _comparator: Comparator<K> = this._DEFAULT_COMPARATOR;
801
-
802
947
  /**
803
948
  * The function returns the value of the _comparator property.
804
949
  * @returns The `_comparator` property is being returned.
@@ -807,6 +952,17 @@ export class BST<
807
952
  return this._comparator;
808
953
  }
809
954
 
955
+ protected _extractComparable?: (key: K) => Comparable;
956
+
957
+ /**
958
+ * This function returns the value of the `_extractComparable` property.
959
+ * @returns The method `extractComparable()` is being returned, which is a getter method for the
960
+ * `_extractComparable` property.
961
+ */
962
+ get extractComparable() {
963
+ return this._extractComparable;
964
+ }
965
+
810
966
  /**
811
967
  * The function sets the root of a tree-like structure and updates the parent property of the new
812
968
  * root.
@@ -818,4 +974,8 @@ export class BST<
818
974
  }
819
975
  this._root = v;
820
976
  }
977
+
978
+ protected _compare(a: K, b: K) {
979
+ return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b);
980
+ }
821
981
  }
@@ -3,6 +3,6 @@ export * from './bst';
3
3
  export * from './binary-indexed-tree';
4
4
  export * from './segment-tree';
5
5
  export * from './avl-tree';
6
- export * from './rb-tree';
6
+ export * from './red-black-tree';
7
7
  export * from './avl-tree-multi-map';
8
8
  export * from './tree-multi-map';
@@ -51,6 +51,59 @@ 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
+ * @example
58
+ * // Find elements in a range
59
+ * const bst = new RedBlackTree<number>([10, 5, 15, 3, 7, 12, 18]);
60
+ * console.log(bst.search(new Range(5, 10))); // [5, 10, 7]
61
+ * console.log(bst.search(new Range(4, 12))); // [5, 10, 12, 7]
62
+ * console.log(bst.search(new Range(15, 20))); // [15, 18]
63
+ * @example
64
+ * // using Red-Black Tree as a price-based index for stock data
65
+ * // Define the structure of individual stock records
66
+ * interface StockRecord {
67
+ * price: number; // Stock price (key for indexing)
68
+ * symbol: string; // Stock ticker symbol
69
+ * volume: number; // Trade volume
70
+ * }
71
+ *
72
+ * // Simulate stock market data as it might come from an external feed
73
+ * const marketStockData: StockRecord[] = [
74
+ * { price: 142.5, symbol: 'AAPL', volume: 1000000 },
75
+ * { price: 335.2, symbol: 'MSFT', volume: 800000 },
76
+ * { price: 3285.04, symbol: 'AMZN', volume: 500000 },
77
+ * { price: 267.98, symbol: 'META', volume: 750000 },
78
+ * { price: 234.57, symbol: 'GOOGL', volume: 900000 }
79
+ * ];
80
+ *
81
+ * // Extend the stock record type to include metadata for database usage
82
+ * type StockTableRecord = StockRecord & { lastUpdated: Date };
83
+ *
84
+ * // Create a Red-Black Tree to index stock records by price
85
+ * // Simulates a database index with stock price as the key for quick lookups
86
+ * const priceIndex = new RedBlackTree<number, StockTableRecord, StockRecord>(marketStockData, {
87
+ * toEntryFn: stockRecord => [
88
+ * stockRecord.price, // Use stock price as the key
89
+ * {
90
+ * ...stockRecord,
91
+ * lastUpdated: new Date() // Add a timestamp for when the record was indexed
92
+ * }
93
+ * ]
94
+ * });
95
+ *
96
+ * // Query the stock with the highest price
97
+ * const highestPricedStock = priceIndex.getRightMost();
98
+ * console.log(priceIndex.get(highestPricedStock)?.symbol); // 'AMZN' // Amazon has the highest price
99
+ *
100
+ * // Query stocks within a specific price range (200 to 400)
101
+ * const stocksInRange = priceIndex.rangeSearch(
102
+ * [200, 400], // Price range
103
+ * node => priceIndex.get(node)?.symbol // Extract stock symbols for the result
104
+ * );
105
+ * console.log(stocksInRange); // ['GOOGL', 'MSFT', 'META']
106
+ */
54
107
  export class RedBlackTree<
55
108
  K = any,
56
109
  V = any,
@@ -119,7 +172,7 @@ export class RedBlackTree<
119
172
  return new RedBlackTree<K, V, R, NODE, TREE>([], {
120
173
  iterationType: this.iterationType,
121
174
  isMapMode: this._isMapMode,
122
- comparator: this._comparator,
175
+ extractComparable: this._extractComparable,
123
176
  toEntryFn: this._toEntryFn,
124
177
  ...options
125
178
  }) as TREE;
@@ -203,7 +256,7 @@ export class RedBlackTree<
203
256
  * returns true. If the node cannot be added or updated, the method returns false.
204
257
  */
205
258
  override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V): boolean {
206
- const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
259
+ const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
207
260
  if (!this.isRealNode(newNode)) return false;
208
261
 
209
262
  const insertStatus = this._insert(newNode);
@@ -351,7 +404,7 @@ export class RedBlackTree<
351
404
 
352
405
  while (this.isRealNode(current)) {
353
406
  parent = current;
354
- const compared = this.comparator(node.key, current.key);
407
+ const compared = this._compare(node.key, current.key);
355
408
  if (compared < 0) {
356
409
  current = current.left ?? this.NIL;
357
410
  } else if (compared > 0) {
@@ -17,7 +17,7 @@ import type {
17
17
  TreeMultiMapOptions
18
18
  } from '../../types';
19
19
  import { IBinaryTree } from '../../interfaces';
20
- import { RedBlackTree, RedBlackTreeNode } from './rb-tree';
20
+ import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
21
21
 
22
22
  export class TreeMultiMapNode<
23
23
  K = any,
@@ -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;
@@ -157,7 +157,7 @@ export class TreeMultiMap<
157
157
  * times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
158
158
  * @returns either a NODE object or undefined.
159
159
  */
160
- override keyValueNodeEntryRawToNodeAndValue(
160
+ protected override _keyValueNodeEntryRawToNodeAndValue(
161
161
  keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R,
162
162
  value?: V,
163
163
  count = 1
@@ -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
  }
@@ -212,7 +212,7 @@ export class TreeMultiMap<
212
212
  * was successful, and false otherwise.
213
213
  */
214
214
  override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V, count = 1): boolean {
215
- const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
215
+ const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
216
216
  const orgCount = newNode?.count || 0;
217
217
  const isSuccessAdded = super.add(newNode, newValue);
218
218