red-black-tree-typed 2.2.2 → 2.2.3
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/README.md +92 -37
- package/dist/cjs/index.cjs +163 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +164 -0
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +163 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +164 -0
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +96 -2
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
- package/dist/types/data-structures/binary-tree/bst.d.ts +156 -13
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +84 -35
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
- package/dist/types/data-structures/graph/directed-graph.d.ts +126 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +160 -1
- package/dist/types/data-structures/hash/hash-map.d.ts +110 -27
- package/dist/types/data-structures/heap/heap.d.ts +107 -58
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +72 -404
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +121 -5
- package/dist/types/data-structures/queue/deque.d.ts +95 -67
- package/dist/types/data-structures/queue/queue.d.ts +90 -34
- package/dist/types/data-structures/stack/stack.d.ts +58 -40
- package/dist/types/data-structures/trie/trie.d.ts +109 -47
- package/dist/types/interfaces/binary-tree.d.ts +1 -0
- package/dist/umd/red-black-tree-typed.js +164 -0
- package/dist/umd/red-black-tree-typed.js.map +1 -1
- package/dist/umd/red-black-tree-typed.min.js +3 -3
- package/dist/umd/red-black-tree-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/data-structures/binary-tree/avl-tree.ts +96 -2
- package/src/data-structures/binary-tree/binary-tree.ts +117 -7
- package/src/data-structures/binary-tree/bst.ts +322 -13
- package/src/data-structures/binary-tree/red-black-tree.ts +84 -35
- package/src/data-structures/binary-tree/tree-multi-map.ts +2 -2
- package/src/data-structures/graph/directed-graph.ts +126 -1
- package/src/data-structures/graph/undirected-graph.ts +160 -1
- package/src/data-structures/hash/hash-map.ts +110 -27
- package/src/data-structures/heap/heap.ts +107 -58
- package/src/data-structures/linked-list/doubly-linked-list.ts +72 -404
- package/src/data-structures/linked-list/singly-linked-list.ts +121 -5
- package/src/data-structures/queue/deque.ts +95 -67
- package/src/data-structures/queue/queue.ts +90 -34
- package/src/data-structures/stack/stack.ts +58 -40
- package/src/data-structures/trie/trie.ts +109 -47
- package/src/interfaces/binary-tree.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "red-black-tree-typed",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "red black tree",
|
|
5
5
|
"browser": "dist/umd/red-black-tree-typed.min.js",
|
|
6
6
|
"umd:main": "dist/umd/red-black-tree-typed.min.js",
|
|
@@ -180,6 +180,6 @@
|
|
|
180
180
|
"typescript": "^4.9.5"
|
|
181
181
|
},
|
|
182
182
|
"dependencies": {
|
|
183
|
-
"data-structure-typed": "^2.2.
|
|
183
|
+
"data-structure-typed": "^2.2.3"
|
|
184
184
|
}
|
|
185
185
|
}
|
|
@@ -198,8 +198,102 @@ export class AVLTreeNode<K = any, V = any> {
|
|
|
198
198
|
* 7. Path Length: The path length from the root to any leaf is longer compared to an unbalanced BST, but shorter than a linear chain of nodes.
|
|
199
199
|
*
|
|
200
200
|
* @example
|
|
201
|
+
* // basic AVLTree creation and add operation
|
|
202
|
+
* // Create a simple AVLTree with initial values
|
|
203
|
+
* const tree = new AVLTree([5, 2, 8, 1, 9]);
|
|
204
|
+
*
|
|
205
|
+
* tree.print();
|
|
206
|
+
* // _2___
|
|
207
|
+
* // / \
|
|
208
|
+
* // 1 _8_
|
|
209
|
+
* // / \
|
|
210
|
+
* // 5 9
|
|
211
|
+
*
|
|
212
|
+
* // Verify the tree maintains sorted order
|
|
213
|
+
* console.log([...tree.keys()]); // [1, 2, 5, 8, 9];
|
|
214
|
+
*
|
|
215
|
+
* // Check size
|
|
216
|
+
* console.log(tree.size); // 5;
|
|
217
|
+
*
|
|
218
|
+
* // Add a new element
|
|
219
|
+
* tree.add(3);
|
|
220
|
+
* console.log(tree.size); // 6;
|
|
221
|
+
* console.log([...tree.keys()]); // [1, 2, 3, 5, 8, 9];
|
|
222
|
+
* @example
|
|
223
|
+
* // AVLTree has and get operations
|
|
224
|
+
* const tree = new AVLTree<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
225
|
+
*
|
|
226
|
+
* // Check if element exists
|
|
227
|
+
* console.log(tree.has(6)); // true;
|
|
228
|
+
* console.log(tree.has(99)); // false;
|
|
229
|
+
*
|
|
230
|
+
* // Get node by key
|
|
231
|
+
* const node = tree.getNode(6);
|
|
232
|
+
* console.log(node?.key); // 6;
|
|
233
|
+
*
|
|
234
|
+
* // Verify tree is balanced
|
|
235
|
+
* console.log(tree.isAVLBalanced()); // true;
|
|
236
|
+
* @example
|
|
237
|
+
* // AVLTree delete and balance verification
|
|
238
|
+
* const tree = new AVLTree([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
239
|
+
*
|
|
240
|
+
* // Delete an element
|
|
241
|
+
* tree.delete(10);
|
|
242
|
+
* console.log(tree.has(10)); // false;
|
|
243
|
+
*
|
|
244
|
+
* // Tree should remain balanced after deletion
|
|
245
|
+
* console.log(tree.isAVLBalanced()); // true;
|
|
246
|
+
*
|
|
247
|
+
* // Size decreased
|
|
248
|
+
* console.log(tree.size); // 15;
|
|
249
|
+
*
|
|
250
|
+
* // Remaining elements are still sorted
|
|
251
|
+
* const keys = [...tree.keys()];
|
|
252
|
+
* console.log(keys); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16];
|
|
253
|
+
* @example
|
|
254
|
+
* // AVLTree for university ranking system with strict balance
|
|
255
|
+
* interface University {
|
|
256
|
+
* name: string;
|
|
257
|
+
* rank: number;
|
|
258
|
+
* students: number;
|
|
259
|
+
* }
|
|
260
|
+
*
|
|
261
|
+
* // AVLTree provides highest search efficiency with strict balance
|
|
262
|
+
* // (every node's left/right subtrees differ by at most 1 in height)
|
|
263
|
+
* const universityTree = new AVLTree<number, University>([
|
|
264
|
+
* [1, { name: 'MIT', rank: 1, students: 1200 }],
|
|
265
|
+
* [5, { name: 'Stanford', rank: 5, students: 1800 }],
|
|
266
|
+
* [3, { name: 'Harvard', rank: 3, students: 2300 }],
|
|
267
|
+
* [2, { name: 'Caltech', rank: 2, students: 400 }],
|
|
268
|
+
* [4, { name: 'CMU', rank: 4, students: 1500 }]
|
|
269
|
+
* ]);
|
|
270
|
+
*
|
|
271
|
+
* // Quick lookup by rank
|
|
272
|
+
* const mit = universityTree.get(1);
|
|
273
|
+
* console.log(mit?.name); // 'MIT';
|
|
274
|
+
*
|
|
275
|
+
* const cmulevel = universityTree.getHeight(4);
|
|
276
|
+
* console.log(typeof cmulevel); // 'number';
|
|
277
|
+
*
|
|
278
|
+
* // Tree maintains strict balance during insertions and deletions
|
|
279
|
+
* console.log(universityTree.isAVLBalanced()); // true;
|
|
280
|
+
*
|
|
281
|
+
* // Add more universities
|
|
282
|
+
* universityTree.add(6, { name: 'Oxford', rank: 6, students: 2000 });
|
|
283
|
+
* console.log(universityTree.isAVLBalanced()); // true;
|
|
284
|
+
*
|
|
285
|
+
* // Delete and verify balance is maintained
|
|
286
|
+
* universityTree.delete(2);
|
|
287
|
+
* console.log(universityTree.has(2)); // false;
|
|
288
|
+
* console.log(universityTree.isAVLBalanced()); // true;
|
|
289
|
+
*
|
|
290
|
+
* // Get all remaining universities in rank order
|
|
291
|
+
* const remainingRanks = [...universityTree.keys()];
|
|
292
|
+
* console.log(remainingRanks); // [1, 3, 4, 5, 6];
|
|
293
|
+
* console.log(universityTree.size); // 5;
|
|
294
|
+
* @example
|
|
201
295
|
* // Find elements in a range
|
|
202
|
-
*
|
|
296
|
+
* // In interval queries, AVL trees, with their strictly balanced structure and lower height, offer better query efficiency, making them ideal for frequent and high-performance interval queries. In contrast, Red-Black trees, with lower update costs, are more suitable for scenarios involving frequent insertions and deletions where the requirements for interval queries are less demanding.
|
|
203
297
|
* type Datum = { timestamp: Date; temperature: number };
|
|
204
298
|
* // Fixed dataset of CPU temperature readings
|
|
205
299
|
* const cpuData: Datum[] = [
|
|
@@ -260,7 +354,7 @@ export class AVLTreeNode<K = any, V = any> {
|
|
|
260
354
|
* // { minute: 13, temperature: 60.2 },
|
|
261
355
|
* // { minute: 14, temperature: 59.8 },
|
|
262
356
|
* // { minute: 15, temperature: 58.6 }
|
|
263
|
-
* // ]
|
|
357
|
+
* // ];
|
|
264
358
|
*/
|
|
265
359
|
export class AVLTree<K = any, V = any, R = any> extends BST<K, V, R> implements IBinaryTree<K, V, R> {
|
|
266
360
|
/**
|
|
@@ -205,8 +205,86 @@ export class BinaryTreeNode<K = any, V = any> {
|
|
|
205
205
|
* 5. Leaf Nodes: Nodes without children are leaves.
|
|
206
206
|
*
|
|
207
207
|
* @example
|
|
208
|
+
* // basic BinaryTree creation and insertion
|
|
209
|
+
* // Create a BinaryTree with entries
|
|
210
|
+
* const entries: [number, string][] = [
|
|
211
|
+
* [6, 'six'],
|
|
212
|
+
* [1, 'one'],
|
|
213
|
+
* [2, 'two'],
|
|
214
|
+
* [7, 'seven'],
|
|
215
|
+
* [5, 'five'],
|
|
216
|
+
* [3, 'three'],
|
|
217
|
+
* [4, 'four'],
|
|
218
|
+
* [9, 'nine'],
|
|
219
|
+
* [8, 'eight']
|
|
220
|
+
* ];
|
|
221
|
+
*
|
|
222
|
+
* const tree = new BinaryTree(entries);
|
|
223
|
+
*
|
|
224
|
+
* // Verify size
|
|
225
|
+
* console.log(tree.size); // 9;
|
|
226
|
+
*
|
|
227
|
+
* // Add new element
|
|
228
|
+
* tree.add(10, 'ten');
|
|
229
|
+
* console.log(tree.size); // 10;
|
|
230
|
+
* @example
|
|
231
|
+
* // BinaryTree get and has operations
|
|
232
|
+
* const tree = new BinaryTree(
|
|
233
|
+
* [
|
|
234
|
+
* [5, 'five'],
|
|
235
|
+
* [3, 'three'],
|
|
236
|
+
* [7, 'seven'],
|
|
237
|
+
* [1, 'one'],
|
|
238
|
+
* [4, 'four'],
|
|
239
|
+
* [6, 'six'],
|
|
240
|
+
* [8, 'eight']
|
|
241
|
+
* ],
|
|
242
|
+
* { isMapMode: false }
|
|
243
|
+
* );
|
|
244
|
+
*
|
|
245
|
+
* // Check if key exists
|
|
246
|
+
* console.log(tree.has(5)); // true;
|
|
247
|
+
* console.log(tree.has(10)); // false;
|
|
248
|
+
*
|
|
249
|
+
* // Get value by key
|
|
250
|
+
* console.log(tree.get(3)); // 'three';
|
|
251
|
+
* console.log(tree.get(7)); // 'seven';
|
|
252
|
+
* console.log(tree.get(100)); // undefined;
|
|
253
|
+
*
|
|
254
|
+
* // Get node structure
|
|
255
|
+
* const node = tree.getNode(5);
|
|
256
|
+
* console.log(node?.key); // 5;
|
|
257
|
+
* console.log(node?.value); // 'five';
|
|
258
|
+
* @example
|
|
259
|
+
* // BinaryTree level-order traversal
|
|
260
|
+
* const tree = new BinaryTree([
|
|
261
|
+
* [1, 'one'],
|
|
262
|
+
* [2, 'two'],
|
|
263
|
+
* [3, 'three'],
|
|
264
|
+
* [4, 'four'],
|
|
265
|
+
* [5, 'five'],
|
|
266
|
+
* [6, 'six'],
|
|
267
|
+
* [7, 'seven']
|
|
268
|
+
* ]);
|
|
269
|
+
*
|
|
270
|
+
* // Binary tree maintains level-order insertion
|
|
271
|
+
* // Complete binary tree structure
|
|
272
|
+
* console.log(tree.size); // 7;
|
|
273
|
+
*
|
|
274
|
+
* // Verify all keys are present
|
|
275
|
+
* console.log(tree.has(1)); // true;
|
|
276
|
+
* console.log(tree.has(4)); // true;
|
|
277
|
+
* console.log(tree.has(7)); // true;
|
|
278
|
+
*
|
|
279
|
+
* // Iterate through tree
|
|
280
|
+
* const keys: number[] = [];
|
|
281
|
+
* for (const [key] of tree) {
|
|
282
|
+
* keys.push(key);
|
|
283
|
+
* }
|
|
284
|
+
* console.log(keys.length); // 7;
|
|
285
|
+
* @example
|
|
208
286
|
* // determine loan approval using a decision tree
|
|
209
|
-
*
|
|
287
|
+
* // Decision tree structure
|
|
210
288
|
* const loanDecisionTree = new BinaryTree<string>(
|
|
211
289
|
* ['stableIncome', 'goodCredit', 'Rejected', 'Approved', 'Rejected'],
|
|
212
290
|
* { isDuplicate: true }
|
|
@@ -228,19 +306,19 @@ export class BinaryTreeNode<K = any, V = any> {
|
|
|
228
306
|
* }
|
|
229
307
|
*
|
|
230
308
|
* // Test case 1: Stable income and good credit score
|
|
231
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: true })); // 'Approved'
|
|
309
|
+
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: true })); // 'Approved';
|
|
232
310
|
*
|
|
233
311
|
* // Test case 2: Stable income but poor credit score
|
|
234
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: false })); // 'Rejected'
|
|
312
|
+
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: true, goodCredit: false })); // 'Rejected';
|
|
235
313
|
*
|
|
236
314
|
* // Test case 3: No stable income
|
|
237
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: true })); // 'Rejected'
|
|
315
|
+
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: true })); // 'Rejected';
|
|
238
316
|
*
|
|
239
317
|
* // Test case 4: No stable income and poor credit score
|
|
240
|
-
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: false })); // 'Rejected'
|
|
318
|
+
* console.log(determineLoanApproval(loanDecisionTree.root, { stableIncome: false, goodCredit: false })); // 'Rejected';
|
|
241
319
|
* @example
|
|
242
320
|
* // evaluate the arithmetic expression represented by the binary tree
|
|
243
|
-
*
|
|
321
|
+
* const expressionTree = new BinaryTree<number | string>(['+', 3, '*', null, null, 5, '-', null, null, 2, 8]);
|
|
244
322
|
*
|
|
245
323
|
* function evaluate(node?: BinaryTreeNode<number | string> | null): number {
|
|
246
324
|
* if (!node) return 0;
|
|
@@ -265,7 +343,7 @@ export class BinaryTreeNode<K = any, V = any> {
|
|
|
265
343
|
* }
|
|
266
344
|
* }
|
|
267
345
|
*
|
|
268
|
-
* console.log(evaluate(expressionTree.root)); // -27
|
|
346
|
+
* console.log(evaluate(expressionTree.root)); // -27;
|
|
269
347
|
*/
|
|
270
348
|
export class BinaryTree<K = any, V = any, R = any>
|
|
271
349
|
extends IterableEntryBase<K, V | undefined>
|
|
@@ -620,6 +698,21 @@ export class BinaryTree<K = any, V = any, R = any>
|
|
|
620
698
|
return false; // Should not happen if tree is not full?
|
|
621
699
|
}
|
|
622
700
|
|
|
701
|
+
/**
|
|
702
|
+
* Adds or updates a new node to the tree.
|
|
703
|
+
* @remarks Time O(log N), For BST, Red-Black Tree, and AVL Tree subclasses, the worst-case time is O(log N). This implementation adds the node at the first available position in a level-order (BFS) traversal. This is NOT a Binary Search Tree insertion. Time O(N), where N is the number of nodes. It must traverse level-by-level to find an empty slot. Space O(N) in the worst case for the BFS queue (e.g., a full last level).
|
|
704
|
+
*
|
|
705
|
+
* @param keyNodeOrEntry - The key, node, or entry to add or update.
|
|
706
|
+
* @param [value] - The value, if providing just a key.
|
|
707
|
+
* @returns True if the addition was successful, false otherwise.
|
|
708
|
+
*/
|
|
709
|
+
set(
|
|
710
|
+
keyNodeOrEntry: K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined,
|
|
711
|
+
value?: V
|
|
712
|
+
): boolean {
|
|
713
|
+
return this.add(keyNodeOrEntry, value);
|
|
714
|
+
}
|
|
715
|
+
|
|
623
716
|
/**
|
|
624
717
|
* Adds multiple items to the tree.
|
|
625
718
|
* @remarks Time O(N * M), where N is the number of items to add and M is the size of the tree at insertion (due to O(M) `add` operation). Space O(M) (from `add`) + O(N) (for the `inserted` array).
|
|
@@ -657,6 +750,23 @@ export class BinaryTree<K = any, V = any, R = any>
|
|
|
657
750
|
return inserted;
|
|
658
751
|
}
|
|
659
752
|
|
|
753
|
+
/**
|
|
754
|
+
* Adds or updates multiple items to the tree.
|
|
755
|
+
* @remarks Time O(N * M), where N is the number of items to add and M is the size of the tree at insertion (due to O(M) `add` operation). Space O(M) (from `add`) + O(N) (for the `inserted` array).
|
|
756
|
+
*
|
|
757
|
+
* @param keysNodesEntriesOrRaws - An iterable of items to add or update.
|
|
758
|
+
* @param [values] - An optional parallel iterable of values.
|
|
759
|
+
* @returns An array of booleans indicating the success of each individual `add` operation.
|
|
760
|
+
*/
|
|
761
|
+
setMany(
|
|
762
|
+
keysNodesEntriesOrRaws: Iterable<
|
|
763
|
+
K | BinaryTreeNode<K, V> | [K | null | undefined, V | undefined] | null | undefined | R
|
|
764
|
+
>,
|
|
765
|
+
values?: Iterable<V | undefined>
|
|
766
|
+
): boolean[] {
|
|
767
|
+
return this.addMany(keysNodesEntriesOrRaws, values);
|
|
768
|
+
}
|
|
769
|
+
|
|
660
770
|
/**
|
|
661
771
|
* Merges another tree into this one by adding all its nodes.
|
|
662
772
|
* @remarks Time O(N * M), same as `addMany`, where N is the size of `anotherTree` and M is the size of this tree. Space O(M) (from `add`).
|
|
@@ -201,8 +201,57 @@ export class BSTNode<K = any, V = any> {
|
|
|
201
201
|
* 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves.
|
|
202
202
|
*
|
|
203
203
|
* @example
|
|
204
|
+
* // basic BST creation and add operation
|
|
205
|
+
* // Create a simple BST with numeric keys
|
|
206
|
+
* const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
207
|
+
*
|
|
208
|
+
* bst.print();
|
|
209
|
+
* // _______8__________
|
|
210
|
+
* // / \
|
|
211
|
+
* // ___4___ ____12_____
|
|
212
|
+
* // / \ / \
|
|
213
|
+
* // _2_ _6_ _10__ _14__
|
|
214
|
+
* // / \ / \ / \ / \
|
|
215
|
+
* // 1 3 5 7 9 11 13 15__
|
|
216
|
+
* // \
|
|
217
|
+
* // 16
|
|
218
|
+
*
|
|
219
|
+
* // Verify size
|
|
220
|
+
* console.log(bst.size); // 16;
|
|
221
|
+
*
|
|
222
|
+
* // Add new elements
|
|
223
|
+
* bst.add(17);
|
|
224
|
+
* bst.add(0);
|
|
225
|
+
* console.log(bst.size); // 18;
|
|
226
|
+
*
|
|
227
|
+
* // Verify keys are searchable
|
|
228
|
+
* console.log(bst.has(11)); // true;
|
|
229
|
+
* console.log(bst.has(100)); // false;
|
|
230
|
+
* @example
|
|
231
|
+
* // BST delete and search after deletion
|
|
232
|
+
* const bst = new BST<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
233
|
+
*
|
|
234
|
+
* // Delete a leaf node
|
|
235
|
+
* bst.delete(1);
|
|
236
|
+
* console.log(bst.has(1)); // false;
|
|
237
|
+
*
|
|
238
|
+
* // Delete a node with one child
|
|
239
|
+
* bst.delete(2);
|
|
240
|
+
* console.log(bst.has(2)); // false;
|
|
241
|
+
*
|
|
242
|
+
* // Delete a node with two children
|
|
243
|
+
* bst.delete(3);
|
|
244
|
+
* console.log(bst.has(3)); // false;
|
|
245
|
+
*
|
|
246
|
+
* // Size decreases with each deletion
|
|
247
|
+
* console.log(bst.size); // 13;
|
|
248
|
+
*
|
|
249
|
+
* // Other nodes remain searchable
|
|
250
|
+
* console.log(bst.has(11)); // true;
|
|
251
|
+
* console.log(bst.has(15)); // true;
|
|
252
|
+
* @example
|
|
204
253
|
* // Merge 3 sorted datasets
|
|
205
|
-
*
|
|
254
|
+
* const dataset1 = new BST<number, string>([
|
|
206
255
|
* [1, 'A'],
|
|
207
256
|
* [7, 'G']
|
|
208
257
|
* ]);
|
|
@@ -222,18 +271,58 @@ export class BSTNode<K = any, V = any> {
|
|
|
222
271
|
* merged.merge(dataset3);
|
|
223
272
|
*
|
|
224
273
|
* // Verify merged dataset is in sorted order
|
|
225
|
-
* console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
|
|
274
|
+
* console.log([...merged.values()]); // ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
|
|
226
275
|
* @example
|
|
227
|
-
* //
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
276
|
+
* // BST with custom objects for expression evaluation
|
|
277
|
+
* interface Expression {
|
|
278
|
+
* id: number;
|
|
279
|
+
* operator: string;
|
|
280
|
+
* precedence: number;
|
|
281
|
+
* }
|
|
282
|
+
*
|
|
283
|
+
* // BST efficiently stores and retrieves operators by precedence
|
|
284
|
+
* const operatorTree = new BST<number, Expression>(
|
|
285
|
+
* [
|
|
286
|
+
* [1, { id: 1, operator: '+', precedence: 1 }],
|
|
287
|
+
* [2, { id: 2, operator: '*', precedence: 2 }],
|
|
288
|
+
* [3, { id: 3, operator: '/', precedence: 2 }],
|
|
289
|
+
* [4, { id: 4, operator: '-', precedence: 1 }],
|
|
290
|
+
* [5, { id: 5, operator: '^', precedence: 3 }]
|
|
291
|
+
* ],
|
|
292
|
+
* { isMapMode: false }
|
|
293
|
+
* );
|
|
294
|
+
*
|
|
295
|
+
* console.log(operatorTree.size); // 5;
|
|
296
|
+
*
|
|
297
|
+
* // Quick lookup of operators
|
|
298
|
+
* const mult = operatorTree.get(2);
|
|
299
|
+
* console.log(mult?.operator); // '*';
|
|
300
|
+
* console.log(mult?.precedence); // 2;
|
|
301
|
+
*
|
|
302
|
+
* // Check if operator exists
|
|
303
|
+
* console.log(operatorTree.has(5)); // true;
|
|
304
|
+
* console.log(operatorTree.has(99)); // false;
|
|
305
|
+
*
|
|
306
|
+
* // Retrieve operator by precedence level
|
|
307
|
+
* const expNode = operatorTree.getNode(3);
|
|
308
|
+
* console.log(expNode?.key); // 3;
|
|
309
|
+
* console.log(expNode?.value?.precedence); // 2;
|
|
310
|
+
*
|
|
311
|
+
* // Delete operator and verify
|
|
312
|
+
* operatorTree.delete(1);
|
|
313
|
+
* console.log(operatorTree.has(1)); // false;
|
|
314
|
+
* console.log(operatorTree.size); // 4;
|
|
315
|
+
*
|
|
316
|
+
* // Get tree height for optimization analysis
|
|
317
|
+
* const treeHeight = operatorTree.getHeight();
|
|
318
|
+
* console.log(treeHeight); // > 0;
|
|
319
|
+
*
|
|
320
|
+
* // Remaining operators are still accessible
|
|
321
|
+
* const remaining = operatorTree.get(2);
|
|
322
|
+
* console.log(remaining); // defined;
|
|
234
323
|
* @example
|
|
235
324
|
* // Find lowest common ancestor
|
|
236
|
-
*
|
|
325
|
+
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
|
237
326
|
*
|
|
238
327
|
* // LCA helper function
|
|
239
328
|
* const findLCA = (num1: number, num2: number): number | undefined => {
|
|
@@ -253,9 +342,9 @@ export class BSTNode<K = any, V = any> {
|
|
|
253
342
|
* }
|
|
254
343
|
*
|
|
255
344
|
* // Assertions
|
|
256
|
-
* console.log(findLCA(3, 10)); // 7
|
|
257
|
-
* console.log(findLCA(5, 35)); // 15
|
|
258
|
-
* console.log(findLCA(20, 30)); // 25
|
|
345
|
+
* console.log(findLCA(3, 10)); // 7;
|
|
346
|
+
* console.log(findLCA(5, 35)); // 15;
|
|
347
|
+
* console.log(findLCA(20, 30)); // 25;
|
|
259
348
|
*/
|
|
260
349
|
export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implements IBinaryTree<K, V, R> {
|
|
261
350
|
/**
|
|
@@ -756,6 +845,52 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
756
845
|
return inserted;
|
|
757
846
|
}
|
|
758
847
|
|
|
848
|
+
/**
|
|
849
|
+
* Returns the first node with a key greater than or equal to the given key.
|
|
850
|
+
* This is equivalent to C++ std::lower_bound on a BST.
|
|
851
|
+
* Supports RECURSIVE and ITERATIVE implementations.
|
|
852
|
+
* Time Complexity: O(log n) on average, O(h) where h is tree height.
|
|
853
|
+
* Space Complexity: O(h) for recursion, O(1) for iteration.
|
|
854
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
855
|
+
* @param iterationType The iteration type (RECURSIVE or ITERATIVE). Defaults to this.iterationType.
|
|
856
|
+
* @returns The first node with key >= given key, or undefined if no such node exists.
|
|
857
|
+
*/
|
|
858
|
+
lowerBound(
|
|
859
|
+
keyNodeEntryOrPredicate:
|
|
860
|
+
| K
|
|
861
|
+
| BSTNode<K, V>
|
|
862
|
+
| [K | null | undefined, V | undefined]
|
|
863
|
+
| null
|
|
864
|
+
| undefined
|
|
865
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
866
|
+
iterationType: IterationType = this.iterationType
|
|
867
|
+
): BSTNode<K, V> | undefined {
|
|
868
|
+
return this._bound(keyNodeEntryOrPredicate, true, iterationType);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Returns the first node with a key strictly greater than the given key.
|
|
873
|
+
* This is equivalent to C++ std::upper_bound on a BST.
|
|
874
|
+
* Supports RECURSIVE and ITERATIVE implementations.
|
|
875
|
+
* Time Complexity: O(log n) on average, O(h) where h is tree height.
|
|
876
|
+
* Space Complexity: O(h) for recursion, O(1) for iteration.
|
|
877
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
878
|
+
* @param iterationType The iteration type (RECURSIVE or ITERATIVE). Defaults to this.iterationType.
|
|
879
|
+
* @returns The first node with key > given key, or undefined if no such node exists.
|
|
880
|
+
*/
|
|
881
|
+
upperBound(
|
|
882
|
+
keyNodeEntryOrPredicate:
|
|
883
|
+
| K
|
|
884
|
+
| BSTNode<K, V>
|
|
885
|
+
| [K | null | undefined, V | undefined]
|
|
886
|
+
| null
|
|
887
|
+
| undefined
|
|
888
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
889
|
+
iterationType: IterationType = this.iterationType
|
|
890
|
+
): BSTNode<K, V> | undefined {
|
|
891
|
+
return this._bound(keyNodeEntryOrPredicate, false, iterationType);
|
|
892
|
+
}
|
|
893
|
+
|
|
759
894
|
/**
|
|
760
895
|
* Traverses the tree and returns nodes that are lesser or greater than a target node.
|
|
761
896
|
* @remarks Time O(N), as it performs a full traversal. Space O(log N) or O(N).
|
|
@@ -945,6 +1080,180 @@ export class BST<K = any, V = any, R = any> extends BinaryTree<K, V, R> implemen
|
|
|
945
1080
|
return false;
|
|
946
1081
|
}
|
|
947
1082
|
|
|
1083
|
+
/**
|
|
1084
|
+
* (Protected) Core bound search implementation supporting all parameter types.
|
|
1085
|
+
* Unified logic for both lowerBound and upperBound.
|
|
1086
|
+
* Resolves various input types (Key, Node, Entry, Predicate) using parent class utilities.
|
|
1087
|
+
* @param keyNodeEntryOrPredicate - The key, node, entry, or predicate function to search for.
|
|
1088
|
+
* @param isLower - True for lowerBound (>=), false for upperBound (>).
|
|
1089
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1090
|
+
* @returns The first matching node, or undefined if no such node exists.
|
|
1091
|
+
*/
|
|
1092
|
+
protected _bound(
|
|
1093
|
+
keyNodeEntryOrPredicate:
|
|
1094
|
+
| K
|
|
1095
|
+
| BSTNode<K, V>
|
|
1096
|
+
| [K | null | undefined, V | undefined]
|
|
1097
|
+
| null
|
|
1098
|
+
| undefined
|
|
1099
|
+
| NodePredicate<BSTNode<K, V>>,
|
|
1100
|
+
isLower: boolean,
|
|
1101
|
+
iterationType: IterationType
|
|
1102
|
+
): BSTNode<K, V> | undefined {
|
|
1103
|
+
if (keyNodeEntryOrPredicate === null || keyNodeEntryOrPredicate === undefined) {
|
|
1104
|
+
return undefined;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Check if input is a predicate function first
|
|
1108
|
+
if (this._isPredicate(keyNodeEntryOrPredicate)) {
|
|
1109
|
+
return this._boundByPredicate(keyNodeEntryOrPredicate, iterationType);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Resolve input to a comparable key
|
|
1113
|
+
let targetKey: K | undefined;
|
|
1114
|
+
|
|
1115
|
+
if (this.isNode(keyNodeEntryOrPredicate)) {
|
|
1116
|
+
// Input is a BSTNode - extract its key
|
|
1117
|
+
targetKey = keyNodeEntryOrPredicate.key;
|
|
1118
|
+
} else if (this.isEntry(keyNodeEntryOrPredicate)) {
|
|
1119
|
+
// Input is a [key, value] entry - extract the key
|
|
1120
|
+
const key = keyNodeEntryOrPredicate[0];
|
|
1121
|
+
if (key === null || key === undefined) {
|
|
1122
|
+
return undefined;
|
|
1123
|
+
}
|
|
1124
|
+
targetKey = key;
|
|
1125
|
+
} else {
|
|
1126
|
+
// Input is a raw key
|
|
1127
|
+
targetKey = keyNodeEntryOrPredicate;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Execute key-based search with binary search optimization
|
|
1131
|
+
if (targetKey !== undefined) {
|
|
1132
|
+
return this._boundByKey(targetKey, isLower, iterationType);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
return undefined;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* (Protected) Binary search for bound by key with pruning optimization.
|
|
1140
|
+
* Performs standard BST binary search, choosing left or right subtree based on comparator result.
|
|
1141
|
+
* For lowerBound: finds first node where key >= target.
|
|
1142
|
+
* For upperBound: finds first node where key > target.
|
|
1143
|
+
* @param key - The target key to search for.
|
|
1144
|
+
* @param isLower - True for lowerBound (>=), false for upperBound (>).
|
|
1145
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1146
|
+
* @returns The first node matching the bound condition, or undefined if none exists.
|
|
1147
|
+
*/
|
|
1148
|
+
protected _boundByKey(key: K, isLower: boolean, iterationType: IterationType): BSTNode<K, V> | undefined {
|
|
1149
|
+
if (iterationType === 'RECURSIVE') {
|
|
1150
|
+
// Recursive binary search implementation
|
|
1151
|
+
const dfs = (cur: BSTNode<K, V> | null | undefined): BSTNode<K, V> | undefined => {
|
|
1152
|
+
if (!this.isRealNode(cur)) return undefined;
|
|
1153
|
+
|
|
1154
|
+
const cmp = this.comparator(cur.key!, key);
|
|
1155
|
+
const condition = isLower ? cmp >= 0 : cmp > 0;
|
|
1156
|
+
|
|
1157
|
+
if (condition) {
|
|
1158
|
+
// Current node satisfies the bound condition.
|
|
1159
|
+
// Try to find a closer (smaller key) candidate in the left subtree.
|
|
1160
|
+
const leftResult = dfs(cur.left);
|
|
1161
|
+
return leftResult ?? cur;
|
|
1162
|
+
} else {
|
|
1163
|
+
// Current node does not satisfy the condition.
|
|
1164
|
+
// Move right to find larger keys.
|
|
1165
|
+
return dfs(cur.right);
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
return dfs(this.root);
|
|
1170
|
+
} else {
|
|
1171
|
+
// Iterative binary search implementation
|
|
1172
|
+
let current: BSTNode<K, V> | undefined = this.root;
|
|
1173
|
+
let result: BSTNode<K, V> | undefined = undefined;
|
|
1174
|
+
|
|
1175
|
+
while (this.isRealNode(current)) {
|
|
1176
|
+
const cmp = this.comparator(current.key!, key);
|
|
1177
|
+
const condition = isLower ? cmp >= 0 : cmp > 0;
|
|
1178
|
+
|
|
1179
|
+
if (condition) {
|
|
1180
|
+
// Current node is a candidate. Save it and try left subtree for a closer match.
|
|
1181
|
+
result = current;
|
|
1182
|
+
current = current.left ?? undefined;
|
|
1183
|
+
} else {
|
|
1184
|
+
// Move right to find larger keys.
|
|
1185
|
+
current = current.right ?? undefined;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
return result;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* (Protected) In-order traversal search by predicate.
|
|
1195
|
+
* Falls back to linear in-order traversal when predicate-based search is required.
|
|
1196
|
+
* Returns the first node that satisfies the predicate function.
|
|
1197
|
+
* Note: Predicate-based search cannot leverage BST's binary search optimization.
|
|
1198
|
+
* Time Complexity: O(n) since it may visit every node.
|
|
1199
|
+
* @param predicate - The predicate function to test nodes.
|
|
1200
|
+
* @param iterationType - The iteration type (RECURSIVE or ITERATIVE).
|
|
1201
|
+
* @returns The first node satisfying predicate, or undefined if none found.
|
|
1202
|
+
*/
|
|
1203
|
+
protected _boundByPredicate(
|
|
1204
|
+
predicate: NodePredicate<BSTNode<K, V>>,
|
|
1205
|
+
iterationType: IterationType
|
|
1206
|
+
): BSTNode<K, V> | undefined {
|
|
1207
|
+
if (iterationType === 'RECURSIVE') {
|
|
1208
|
+
// Recursive in-order traversal
|
|
1209
|
+
let result: BSTNode<K, V> | undefined = undefined;
|
|
1210
|
+
|
|
1211
|
+
const dfs = (cur: BSTNode<K, V> | null | undefined): void => {
|
|
1212
|
+
if (result || !this.isRealNode(cur)) return;
|
|
1213
|
+
|
|
1214
|
+
// In-order: process left subtree first
|
|
1215
|
+
if (this.isRealNode(cur.left)) dfs(cur.left);
|
|
1216
|
+
|
|
1217
|
+
// Check current node
|
|
1218
|
+
if (!result && predicate(cur)) {
|
|
1219
|
+
result = cur;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// Process right subtree
|
|
1223
|
+
if (!result && this.isRealNode(cur.right)) dfs(cur.right);
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
dfs(this.root);
|
|
1227
|
+
return result;
|
|
1228
|
+
} else {
|
|
1229
|
+
// Iterative in-order traversal using explicit stack
|
|
1230
|
+
const stack: (BSTNode<K, V> | null | undefined)[] = [];
|
|
1231
|
+
let current: BSTNode<K, V> | null | undefined = this.root;
|
|
1232
|
+
|
|
1233
|
+
while (stack.length > 0 || this.isRealNode(current)) {
|
|
1234
|
+
if (this.isRealNode(current)) {
|
|
1235
|
+
// Go to the leftmost node
|
|
1236
|
+
stack.push(current);
|
|
1237
|
+
current = current.left;
|
|
1238
|
+
} else {
|
|
1239
|
+
// Pop from stack and process
|
|
1240
|
+
const node = stack.pop();
|
|
1241
|
+
if (!this.isRealNode(node)) break;
|
|
1242
|
+
|
|
1243
|
+
// Check if current node satisfies predicate
|
|
1244
|
+
if (predicate(node)) {
|
|
1245
|
+
return node;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Visit right subtree
|
|
1249
|
+
current = node.right;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return undefined;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
948
1257
|
/**
|
|
949
1258
|
* (Protected) Creates a new, empty instance of the same BST constructor.
|
|
950
1259
|
* @remarks Time O(1)
|