queue-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.
- package/dist/common/index.d.ts +12 -0
- package/dist/common/index.js +23 -0
- package/dist/data-structures/binary-tree/avl-tree-multi-map.js +7 -10
- package/dist/data-structures/binary-tree/avl-tree.js +2 -2
- package/dist/data-structures/binary-tree/binary-tree.d.ts +54 -19
- package/dist/data-structures/binary-tree/binary-tree.js +100 -66
- package/dist/data-structures/binary-tree/bst.d.ts +100 -36
- package/dist/data-structures/binary-tree/bst.js +185 -66
- package/dist/data-structures/binary-tree/rb-tree.d.ts +4 -0
- package/dist/data-structures/binary-tree/rb-tree.js +6 -2
- package/dist/data-structures/binary-tree/tree-multi-map.js +2 -2
- package/dist/data-structures/heap/heap.d.ts +6 -6
- package/dist/data-structures/heap/heap.js +6 -6
- package/dist/data-structures/linked-list/doubly-linked-list.d.ts +18 -8
- package/dist/data-structures/linked-list/doubly-linked-list.js +24 -10
- package/dist/data-structures/linked-list/singly-linked-list.d.ts +1 -1
- package/dist/data-structures/linked-list/singly-linked-list.js +1 -1
- package/dist/data-structures/trie/trie.d.ts +104 -4
- package/dist/data-structures/trie/trie.js +116 -12
- 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 +19 -0
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -9
- package/src/data-structures/binary-tree/avl-tree.ts +3 -2
- package/src/data-structures/binary-tree/binary-tree.ts +108 -64
- package/src/data-structures/binary-tree/bst.ts +190 -69
- package/src/data-structures/binary-tree/rb-tree.ts +6 -2
- package/src/data-structures/binary-tree/tree-multi-map.ts +3 -3
- package/src/data-structures/heap/heap.ts +39 -39
- package/src/data-structures/linked-list/doubly-linked-list.ts +111 -97
- package/src/data-structures/linked-list/singly-linked-list.ts +1 -1
- package/src/data-structures/trie/trie.ts +116 -11
- 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,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 {
|
|
119
|
-
if (
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 `
|
|
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
|
-
*
|
|
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
|
|
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
|
-
):
|
|
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
|
-
|
|
429
|
-
|
|
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 (
|
|
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
|
-
|
|
440
|
-
|
|
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.
|
|
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.
|
|
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 (
|
|
467
|
-
ans.push(cur);
|
|
579
|
+
if (predicate(cur)) {
|
|
580
|
+
ans.push(callback(cur));
|
|
468
581
|
if (onlyOne) return ans;
|
|
469
582
|
}
|
|
470
|
-
if (
|
|
471
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
901
|
+
`When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
|
|
793
902
|
);
|
|
794
903
|
}
|
|
795
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
}
|
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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],
|