deque-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.
- package/dist/common/index.d.ts +12 -0
- package/dist/common/index.js +28 -0
- package/dist/data-structures/binary-tree/avl-tree-multi-map.d.ts +1 -1
- package/dist/data-structures/binary-tree/avl-tree-multi-map.js +9 -12
- package/dist/data-structures/binary-tree/avl-tree.js +2 -2
- package/dist/data-structures/binary-tree/binary-tree.d.ts +55 -20
- package/dist/data-structures/binary-tree/binary-tree.js +102 -68
- package/dist/data-structures/binary-tree/bst.d.ts +131 -37
- package/dist/data-structures/binary-tree/bst.js +222 -69
- package/dist/data-structures/binary-tree/index.d.ts +1 -1
- package/dist/data-structures/binary-tree/index.js +1 -1
- package/dist/data-structures/binary-tree/{rb-tree.d.ts → red-black-tree.d.ts} +53 -0
- package/dist/data-structures/binary-tree/{rb-tree.js → red-black-tree.js} +56 -3
- package/dist/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
- package/dist/data-structures/binary-tree/tree-multi-map.js +7 -7
- package/dist/data-structures/hash/hash-map.d.ts +30 -0
- package/dist/data-structures/hash/hash-map.js +30 -0
- package/dist/data-structures/heap/heap.d.ts +26 -9
- package/dist/data-structures/heap/heap.js +37 -17
- package/dist/data-structures/linked-list/doubly-linked-list.d.ts +54 -9
- package/dist/data-structures/linked-list/doubly-linked-list.js +80 -19
- package/dist/data-structures/linked-list/singly-linked-list.d.ts +35 -2
- package/dist/data-structures/linked-list/singly-linked-list.js +55 -11
- package/dist/data-structures/queue/deque.d.ts +37 -8
- package/dist/data-structures/queue/deque.js +73 -29
- package/dist/data-structures/queue/queue.d.ts +41 -1
- package/dist/data-structures/queue/queue.js +51 -9
- package/dist/data-structures/stack/stack.d.ts +27 -10
- package/dist/data-structures/stack/stack.js +39 -20
- package/dist/data-structures/trie/trie.d.ts +111 -6
- package/dist/data-structures/trie/trie.js +123 -14
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
- package/dist/types/data-structures/binary-tree/bst.d.ts +3 -2
- package/dist/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
- package/dist/types/utils/utils.d.ts +10 -6
- package/dist/utils/utils.js +4 -2
- package/package.json +2 -2
- package/src/common/index.ts +25 -0
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +9 -11
- package/src/data-structures/binary-tree/avl-tree.ts +3 -2
- package/src/data-structures/binary-tree/binary-tree.ts +110 -66
- package/src/data-structures/binary-tree/bst.ts +232 -72
- package/src/data-structures/binary-tree/index.ts +1 -1
- package/src/data-structures/binary-tree/{rb-tree.ts → red-black-tree.ts} +56 -3
- package/src/data-structures/binary-tree/tree-multi-map.ts +6 -6
- package/src/data-structures/hash/hash-map.ts +30 -0
- package/src/data-structures/heap/heap.ts +72 -49
- package/src/data-structures/linked-list/doubly-linked-list.ts +173 -105
- package/src/data-structures/linked-list/singly-linked-list.ts +61 -11
- package/src/data-structures/queue/deque.ts +72 -28
- package/src/data-structures/queue/queue.ts +50 -7
- package/src/data-structures/stack/stack.ts +39 -20
- package/src/data-structures/trie/trie.ts +123 -13
- package/src/index.ts +2 -1
- package/src/types/data-structures/binary-tree/binary-tree.ts +1 -1
- package/src/types/data-structures/binary-tree/bst.ts +3 -2
- package/src/types/data-structures/binary-tree/rb-tree.ts +1 -1
- package/src/types/utils/utils.ts +16 -10
- 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 {
|
|
119
|
-
if (
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 `
|
|
400
|
-
*
|
|
401
|
-
* @param {BTNRep<K, V, NODE> | R | NodePredicate<NODE>} keyNodeEntryRawOrPredicate - The
|
|
402
|
-
* parameter in the `
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
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
|
|
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
|
-
):
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
440
|
-
|
|
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.
|
|
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.
|
|
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 (
|
|
467
|
-
ans.push(cur);
|
|
587
|
+
if (predicate(cur)) {
|
|
588
|
+
ans.push(callback(cur));
|
|
468
589
|
if (onlyOne) return ans;
|
|
469
590
|
}
|
|
470
|
-
if (
|
|
471
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
940
|
+
`When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
|
|
793
941
|
);
|
|
794
942
|
}
|
|
795
|
-
|
|
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
|
}
|
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
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 './
|
|
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
|
-
|
|
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
|
|
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.
|
|
177
|
-
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw
|
|
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.
|
|
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
|
|