data-structure-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/CHANGELOG.md +1 -1
- package/README.md +16 -25
- package/benchmark/report.html +32 -5
- package/benchmark/report.json +326 -23
- package/dist/cjs/common/index.d.ts +12 -0
- package/dist/cjs/common/index.js +24 -0
- package/dist/cjs/common/index.js.map +1 -0
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js +7 -10
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree.js +2 -2
- package/dist/cjs/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/binary-tree.d.ts +54 -19
- package/dist/cjs/data-structures/binary-tree/binary-tree.js +100 -66
- package/dist/cjs/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/bst.d.ts +100 -36
- package/dist/cjs/data-structures/binary-tree/bst.js +185 -66
- package/dist/cjs/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/rb-tree.d.ts +4 -0
- package/dist/cjs/data-structures/binary-tree/rb-tree.js +6 -2
- package/dist/cjs/data-structures/binary-tree/rb-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js +2 -2
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/heap/heap.d.ts +6 -6
- package/dist/cjs/data-structures/heap/heap.js +6 -6
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.d.ts +18 -8
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js +24 -10
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/linked-list/singly-linked-list.d.ts +1 -1
- package/dist/cjs/data-structures/linked-list/singly-linked-list.js +1 -1
- package/dist/cjs/data-structures/linked-list/singly-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/trie/trie.d.ts +110 -4
- package/dist/cjs/data-structures/trie/trie.js +122 -12
- package/dist/cjs/data-structures/trie/trie.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
- package/dist/cjs/types/data-structures/binary-tree/bst.d.ts +3 -2
- package/dist/cjs/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
- package/dist/cjs/types/utils/utils.d.ts +10 -6
- package/dist/cjs/utils/utils.js +4 -2
- package/dist/cjs/utils/utils.js.map +1 -1
- package/dist/mjs/common/index.d.ts +12 -0
- package/dist/mjs/common/index.js +24 -0
- package/dist/mjs/common/index.js.map +1 -0
- package/dist/mjs/data-structures/binary-tree/avl-tree-multi-map.js +8 -10
- package/dist/mjs/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/avl-tree.js +3 -2
- package/dist/mjs/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/binary-tree.d.ts +54 -19
- package/dist/mjs/data-structures/binary-tree/binary-tree.js +95 -61
- package/dist/mjs/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/bst.d.ts +100 -36
- package/dist/mjs/data-structures/binary-tree/bst.js +187 -66
- package/dist/mjs/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/rb-tree.d.ts +4 -0
- package/dist/mjs/data-structures/binary-tree/rb-tree.js +6 -2
- package/dist/mjs/data-structures/binary-tree/rb-tree.js.map +1 -1
- package/dist/mjs/data-structures/binary-tree/tree-multi-map.js +2 -2
- package/dist/mjs/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/mjs/data-structures/heap/heap.d.ts +6 -6
- package/dist/mjs/data-structures/heap/heap.js +6 -6
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.d.ts +18 -8
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.js +24 -10
- package/dist/mjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/mjs/data-structures/linked-list/singly-linked-list.d.ts +1 -1
- package/dist/mjs/data-structures/linked-list/singly-linked-list.js +1 -1
- package/dist/mjs/data-structures/linked-list/singly-linked-list.js.map +1 -1
- package/dist/mjs/data-structures/trie/trie.d.ts +110 -4
- package/dist/mjs/data-structures/trie/trie.js +122 -12
- package/dist/mjs/data-structures/trie/trie.js.map +1 -1
- package/dist/mjs/index.d.ts +1 -1
- package/dist/mjs/index.js +1 -1
- package/dist/mjs/index.js.map +1 -1
- package/dist/mjs/types/data-structures/binary-tree/binary-tree.d.ts +1 -1
- package/dist/mjs/types/data-structures/binary-tree/bst.d.ts +3 -2
- package/dist/mjs/types/data-structures/binary-tree/rb-tree.d.ts +1 -1
- package/dist/mjs/types/utils/utils.d.ts +10 -6
- package/dist/mjs/utils/utils.js +4 -2
- package/dist/mjs/utils/utils.js.map +1 -1
- package/dist/umd/data-structure-typed.js +299 -156
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +7 -7
- 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 +1 -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
- package/test/performance/data-structures/binary-tree/avl-tree.test.ts +3 -0
- package/test/performance/data-structures/binary-tree/rb-tree.test.ts +4 -1
- package/test/performance/reportor.ts +38 -33
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +12 -12
- package/test/unit/data-structures/binary-tree/bst.test.ts +79 -3
- package/test/unit/data-structures/binary-tree/overall.test.ts +14 -22
- package/test/unit/data-structures/binary-tree/rb-tree.test.ts +100 -1
- package/test/unit/data-structures/trie/trie.test.ts +151 -0
- package/test/unit/utils/utils.test.ts +6 -6
- package/dist/cjs/constants/index.d.ts +0 -4
- package/dist/cjs/constants/index.js +0 -9
- package/dist/cjs/constants/index.js.map +0 -1
- package/dist/mjs/constants/index.d.ts +0 -4
- package/dist/mjs/constants/index.js +0 -6
- package/dist/mjs/constants/index.js.map +0 -1
- package/src/constants/index.ts +0 -4
|
@@ -92,13 +92,100 @@ export class TrieNode {
|
|
|
92
92
|
* 9. Spell Check: Checking the spelling of words.
|
|
93
93
|
* 10. IP Routing: Used in certain types of IP routing algorithms.
|
|
94
94
|
* 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data.
|
|
95
|
+
* @example
|
|
96
|
+
* // Autocomplete: Prefix validation and checking
|
|
97
|
+
* const autocomplete = new Trie<string>(['gmail.com', 'gmail.co.nz', 'gmail.co.jp', 'yahoo.com', 'outlook.com']);
|
|
98
|
+
*
|
|
99
|
+
* // Get all completions for a prefix
|
|
100
|
+
* const gmailCompletions = autocomplete.getWords('gmail');
|
|
101
|
+
* console.log(gmailCompletions); // ['gmail.com', 'gmail.co.nz', 'gmail.co.jp']
|
|
102
|
+
* @example
|
|
103
|
+
* // File System Path Operations
|
|
104
|
+
* const fileSystem = new Trie<string>([
|
|
105
|
+
* '/home/user/documents/file1.txt',
|
|
106
|
+
* '/home/user/documents/file2.txt',
|
|
107
|
+
* '/home/user/pictures/photo.jpg',
|
|
108
|
+
* '/home/user/pictures/vacation/',
|
|
109
|
+
* '/home/user/downloads'
|
|
110
|
+
* ]);
|
|
111
|
+
*
|
|
112
|
+
* // Find common directory prefix
|
|
113
|
+
* console.log(fileSystem.getLongestCommonPrefix()); // '/home/user/'
|
|
114
|
+
*
|
|
115
|
+
* // List all files in a directory
|
|
116
|
+
* const documentsFiles = fileSystem.getWords('/home/user/documents/');
|
|
117
|
+
* console.log(documentsFiles); // ['/home/user/documents/file1.txt', '/home/user/documents/file2.txt']
|
|
118
|
+
* @example
|
|
119
|
+
* // Autocomplete: Basic word suggestions
|
|
120
|
+
* // Create a trie for autocomplete
|
|
121
|
+
* const autocomplete = new Trie<string>([
|
|
122
|
+
* 'function',
|
|
123
|
+
* 'functional',
|
|
124
|
+
* 'functions',
|
|
125
|
+
* 'class',
|
|
126
|
+
* 'classes',
|
|
127
|
+
* 'classical',
|
|
128
|
+
* 'closure',
|
|
129
|
+
* 'const',
|
|
130
|
+
* 'constructor'
|
|
131
|
+
* ]);
|
|
132
|
+
*
|
|
133
|
+
* // Test autocomplete with different prefixes
|
|
134
|
+
* console.log(autocomplete.getWords('fun')); // ['functional', 'functions', 'function']
|
|
135
|
+
* console.log(autocomplete.getWords('cla')); // ['classes', 'classical', 'class']
|
|
136
|
+
* console.log(autocomplete.getWords('con')); // ['constructor', 'const']
|
|
137
|
+
*
|
|
138
|
+
* // Test with non-matching prefix
|
|
139
|
+
* console.log(autocomplete.getWords('xyz')); // []
|
|
140
|
+
* @example
|
|
141
|
+
* // Dictionary: Case-insensitive word lookup
|
|
142
|
+
* // Create a case-insensitive dictionary
|
|
143
|
+
* const dictionary = new Trie<string>([], { caseSensitive: false });
|
|
144
|
+
*
|
|
145
|
+
* // Add words with mixed casing
|
|
146
|
+
* dictionary.add('Hello');
|
|
147
|
+
* dictionary.add('WORLD');
|
|
148
|
+
* dictionary.add('JavaScript');
|
|
149
|
+
*
|
|
150
|
+
* // Test lookups with different casings
|
|
151
|
+
* console.log(dictionary.has('hello')); // true
|
|
152
|
+
* console.log(dictionary.has('HELLO')); // true
|
|
153
|
+
* console.log(dictionary.has('Hello')); // true
|
|
154
|
+
* console.log(dictionary.has('javascript')); // true
|
|
155
|
+
* console.log(dictionary.has('JAVASCRIPT')); // true
|
|
156
|
+
* @example
|
|
157
|
+
* // IP Address Routing Table
|
|
158
|
+
* // Add IP address prefixes and their corresponding routes
|
|
159
|
+
* const routes = {
|
|
160
|
+
* '192.168.1': 'LAN_SUBNET_1',
|
|
161
|
+
* '192.168.2': 'LAN_SUBNET_2',
|
|
162
|
+
* '10.0.0': 'PRIVATE_NETWORK_1',
|
|
163
|
+
* '10.0.1': 'PRIVATE_NETWORK_2'
|
|
164
|
+
* };
|
|
165
|
+
*
|
|
166
|
+
* const ipRoutingTable = new Trie<string>(Object.keys(routes));
|
|
167
|
+
*
|
|
168
|
+
* // Check IP address prefix matching
|
|
169
|
+
* console.log(ipRoutingTable.hasPrefix('192.168.1')); // true
|
|
170
|
+
* console.log(ipRoutingTable.hasPrefix('192.168.2')); // true
|
|
171
|
+
*
|
|
172
|
+
* // Validate IP address belongs to subnet
|
|
173
|
+
* const ip = '192.168.1.100';
|
|
174
|
+
* const subnet = ip.split('.').slice(0, 3).join('.');
|
|
175
|
+
* console.log(ipRoutingTable.hasPrefix(subnet)); // true
|
|
95
176
|
*/
|
|
96
177
|
export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|
97
178
|
/**
|
|
98
|
-
* The constructor
|
|
99
|
-
*
|
|
100
|
-
* @param
|
|
101
|
-
*
|
|
179
|
+
* The constructor initializes a Trie data structure with optional options and words provided as
|
|
180
|
+
* input.
|
|
181
|
+
* @param {Iterable<string> | Iterable<R>} words - The `words` parameter in the constructor is an
|
|
182
|
+
* iterable containing either strings or elements of type `R`. It is used to initialize the Trie with
|
|
183
|
+
* a list of words or elements. If no `words` are provided, an empty iterable is used as the default
|
|
184
|
+
* value.
|
|
185
|
+
* @param [options] - The `options` parameter in the constructor is an optional object that can
|
|
186
|
+
* contain configuration options for the Trie data structure. One of the options it can have is
|
|
187
|
+
* `caseSensitive`, which is a boolean value indicating whether the Trie should be case-sensitive or
|
|
188
|
+
* not. If `caseSensitive` is set to `
|
|
102
189
|
*/
|
|
103
190
|
constructor(words: Iterable<string> | Iterable<R> = [], options?: TrieOptions<R>) {
|
|
104
191
|
super(options);
|
|
@@ -107,13 +194,7 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|
|
107
194
|
if (caseSensitive !== undefined) this._caseSensitive = caseSensitive;
|
|
108
195
|
}
|
|
109
196
|
if (words) {
|
|
110
|
-
|
|
111
|
-
if (this.toElementFn) {
|
|
112
|
-
this.add(this.toElementFn(word as R));
|
|
113
|
-
} else {
|
|
114
|
-
this.add(word as string);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
197
|
+
this.addMany(words);
|
|
117
198
|
}
|
|
118
199
|
}
|
|
119
200
|
|
|
@@ -175,6 +256,30 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|
|
175
256
|
return isNewWord;
|
|
176
257
|
}
|
|
177
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Time Complexity: O(n * l)
|
|
261
|
+
* Space Complexity: O(1)
|
|
262
|
+
*
|
|
263
|
+
* The `addMany` function in TypeScript takes an iterable of strings or elements of type R, converts
|
|
264
|
+
* them using a provided function if available, and adds them to a data structure while returning an
|
|
265
|
+
* array of boolean values indicating success.
|
|
266
|
+
* @param {Iterable<string> | Iterable<R>} words - The `words` parameter in the `addMany` function is
|
|
267
|
+
* an iterable that contains either strings or elements of type `R`.
|
|
268
|
+
* @returns The `addMany` method returns an array of boolean values indicating whether each word in
|
|
269
|
+
* the input iterable was successfully added to the data structure.
|
|
270
|
+
*/
|
|
271
|
+
addMany(words: Iterable<string> | Iterable<R> = []): boolean[] {
|
|
272
|
+
const ans: boolean[] = [];
|
|
273
|
+
for (const word of words) {
|
|
274
|
+
if (this.toElementFn) {
|
|
275
|
+
ans.push(this.add(this.toElementFn(word as R)));
|
|
276
|
+
} else {
|
|
277
|
+
ans.push(this.add(word as string));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return ans;
|
|
281
|
+
}
|
|
282
|
+
|
|
178
283
|
/**
|
|
179
284
|
* Time Complexity: O(l), where l is the length of the input word.
|
|
180
285
|
* Space Complexity: O(1) - Constant space.
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BinaryTree, BinaryTreeNode } from '../../../data-structures';
|
|
2
2
|
import { IterationType, OptValue } from '../../common';
|
|
3
|
-
import { DFSOperation } from '../../../
|
|
3
|
+
import { DFSOperation } from '../../../common';
|
|
4
4
|
|
|
5
5
|
export type BinaryTreeNodeNested<K, V> = BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
6
6
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { BST, BSTNode } from '../../../data-structures';
|
|
2
2
|
import type { BinaryTreeOptions } from './binary-tree';
|
|
3
|
-
import {
|
|
3
|
+
import { Comparable } from '../../utils';
|
|
4
4
|
|
|
5
5
|
export type BSTNodeNested<K, V> = BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
6
6
|
|
|
7
7
|
export type BSTNested<K, V, R, NODE extends BSTNode<K, V, NODE>> = BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
8
8
|
|
|
9
9
|
export type BSTOptions<K, V, R> = BinaryTreeOptions<K, V, R> & {
|
|
10
|
-
|
|
10
|
+
extractComparable?: (key: K) => Comparable
|
|
11
|
+
isReverse?: boolean;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export type BSTNOptKey<K> = K | undefined;
|
|
@@ -7,4 +7,4 @@ export type RedBlackTreeNodeNested<K, V> = RedBlackTreeNode<K, V, RedBlackTreeNo
|
|
|
7
7
|
|
|
8
8
|
export type RedBlackTreeNested<K, V, R, NODE extends RedBlackTreeNode<K, V, NODE>> = RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
|
9
9
|
|
|
10
|
-
export type RBTreeOptions<K, V, R> = BSTOptions<K, V, R> & {};
|
|
10
|
+
export type RBTreeOptions<K, V, R> = Omit<BSTOptions<K, V, R>, 'isReverse'> & {};
|
package/src/types/utils/utils.ts
CHANGED
|
@@ -7,17 +7,23 @@ export type SpecifyOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T,
|
|
|
7
7
|
|
|
8
8
|
export type Any = string | number | bigint | boolean | symbol | undefined | object;
|
|
9
9
|
|
|
10
|
+
export type Arithmetic = number | bigint;
|
|
11
|
+
|
|
10
12
|
export type ComparablePrimitive = number | bigint | string | boolean;
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
export interface BaseComparableObject {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ValueComparableObject extends BaseComparableObject {
|
|
19
|
+
valueOf: () => ComparablePrimitive | ValueComparableObject;
|
|
20
|
+
toString?: () => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface StringComparableObject extends BaseComparableObject {
|
|
24
|
+
toString: () => string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type ComparableObject = ValueComparableObject | StringComparableObject;
|
|
22
28
|
|
|
23
29
|
export type Comparable = ComparablePrimitive | Date | ComparableObject;
|
package/src/utils/utils.ts
CHANGED
|
@@ -226,7 +226,8 @@ export const roundFixed = (num: number, digit: number = 10) => {
|
|
|
226
226
|
*/
|
|
227
227
|
function isPrimitiveComparable(value: unknown): value is ComparablePrimitive {
|
|
228
228
|
const valueType = typeof value;
|
|
229
|
-
if (valueType === 'number') return
|
|
229
|
+
if (valueType === 'number') return true;
|
|
230
|
+
// if (valueType === 'number') return !Number.isNaN(value);
|
|
230
231
|
return valueType === 'bigint' || valueType === 'string' || valueType === 'boolean';
|
|
231
232
|
}
|
|
232
233
|
|
|
@@ -274,7 +275,8 @@ export function isComparable(value: unknown, isForceObjectComparable = false): v
|
|
|
274
275
|
if (isPrimitiveComparable(value)) return true;
|
|
275
276
|
|
|
276
277
|
if (typeof value !== 'object') return false;
|
|
277
|
-
if (value instanceof Date) return
|
|
278
|
+
if (value instanceof Date) return true;
|
|
279
|
+
// if (value instanceof Date) return !Number.isNaN(value.getTime());
|
|
278
280
|
if (isForceObjectComparable) return true;
|
|
279
281
|
const comparableValue = tryObjectToPrimitive(value);
|
|
280
282
|
if (comparableValue === null || comparableValue === undefined) return false;
|
|
@@ -19,6 +19,9 @@ suite
|
|
|
19
19
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
20
20
|
for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]);
|
|
21
21
|
})
|
|
22
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
|
23
|
+
for (let i = 0; i < randomArray.length; i++) avlTree.getNode(randomArray[i]);
|
|
24
|
+
})
|
|
22
25
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} iterator`, () => {
|
|
23
26
|
const entries = [...avlTree];
|
|
24
27
|
return entries.length === HUNDRED_THOUSAND;
|
|
@@ -23,9 +23,12 @@ suite
|
|
|
23
23
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
24
24
|
for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]);
|
|
25
25
|
})
|
|
26
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} getNode`, () => {
|
|
27
|
+
for (let i = 0; i < randomArray.length; i++) rbTree.getNode(randomArray[i]);
|
|
28
|
+
})
|
|
26
29
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode add randomly`, () => {
|
|
27
30
|
rbTreeNodeMode.clear();
|
|
28
|
-
for (let i = 0; i < randomArray.length; i++)
|
|
31
|
+
for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.add(randomArray[i]);
|
|
29
32
|
})
|
|
30
33
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} node mode get`, () => {
|
|
31
34
|
for (let i = 0; i < randomArray.length; i++) rbTreeNodeMode.get(randomArray[i]);
|
|
@@ -8,6 +8,22 @@ import { PerformanceTest } from './types';
|
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
|
|
10
10
|
const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor;
|
|
11
|
+
const isOnlyOrdered = true;
|
|
12
|
+
const runOrder = [
|
|
13
|
+
'heap',
|
|
14
|
+
'avl-tree',
|
|
15
|
+
'rb-tree',
|
|
16
|
+
'doubly-linked-list',
|
|
17
|
+
'directed-graph',
|
|
18
|
+
'queue',
|
|
19
|
+
'deque',
|
|
20
|
+
'hash-map',
|
|
21
|
+
'trie',
|
|
22
|
+
'stack'
|
|
23
|
+
// 'singly-linked-list',
|
|
24
|
+
// 'priority-queue',
|
|
25
|
+
// 'binary-tree-overall'
|
|
26
|
+
];
|
|
11
27
|
|
|
12
28
|
const getRelativePath = (file: string) => {
|
|
13
29
|
return path.relative(__dirname, file);
|
|
@@ -80,7 +96,7 @@ const composeReport = () => {
|
|
|
80
96
|
#json-to-html {
|
|
81
97
|
padding: 0 10px 20px;
|
|
82
98
|
}
|
|
83
|
-
|
|
99
|
+
|
|
84
100
|
.json-to-html-label {
|
|
85
101
|
font-size: 2rem;
|
|
86
102
|
margin: 2rem 0 0 3px;
|
|
@@ -92,19 +108,19 @@ const composeReport = () => {
|
|
|
92
108
|
margin-top: 10px;
|
|
93
109
|
font-size: 16px;
|
|
94
110
|
}
|
|
95
|
-
|
|
111
|
+
|
|
96
112
|
.content table th,
|
|
97
113
|
.content table td {
|
|
98
114
|
padding: 8px 12px;
|
|
99
115
|
text-align: left;
|
|
100
116
|
border: 1px solid #ddd;
|
|
101
117
|
}
|
|
102
|
-
|
|
118
|
+
|
|
103
119
|
.content table th {
|
|
104
120
|
background-color: #f2f2f2;
|
|
105
121
|
font-weight: bold;
|
|
106
122
|
}
|
|
107
|
-
|
|
123
|
+
|
|
108
124
|
.content table tr:nth-child(odd) {
|
|
109
125
|
background-color: #ffffff;
|
|
110
126
|
}
|
|
@@ -188,46 +204,35 @@ function replaceMarkdownContent(startMarker: string, endMarker: string, newText:
|
|
|
188
204
|
});
|
|
189
205
|
}
|
|
190
206
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
'hash-map',
|
|
197
|
-
'trie',
|
|
198
|
-
'avl-tree',
|
|
199
|
-
'binary-tree-overall',
|
|
200
|
-
'directed-graph',
|
|
201
|
-
'doubly-linked-list',
|
|
202
|
-
'singly-linked-list',
|
|
203
|
-
'priority-queue',
|
|
204
|
-
'stack'
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
const sortedPerformanceTests = [...performanceTests].sort((a, b) => {
|
|
208
|
-
const indexA = order.indexOf(a.testName);
|
|
209
|
-
const indexB = order.indexOf(b.testName);
|
|
207
|
+
const sortedPerformanceTests = (
|
|
208
|
+
isOnlyOrdered ? [...performanceTests].filter(test => runOrder.includes(test.testName)) : [...performanceTests]
|
|
209
|
+
).sort((a, b) => {
|
|
210
|
+
const indexA = runOrder.indexOf(a.testName);
|
|
211
|
+
const indexB = runOrder.indexOf(b.testName);
|
|
210
212
|
|
|
211
|
-
// If both a and b are in the
|
|
213
|
+
// If both a and b are in the runOrder, sort them according to their indices in the runOrder.
|
|
212
214
|
if (indexA !== -1 && indexB !== -1) {
|
|
213
215
|
return indexA - indexB;
|
|
214
216
|
}
|
|
215
217
|
|
|
216
|
-
// If there is only 'a' in the
|
|
218
|
+
// If there is only 'a' in the runOrder, then place 'b' in front.
|
|
217
219
|
if (indexA !== -1) {
|
|
218
220
|
return 1;
|
|
219
221
|
}
|
|
220
222
|
|
|
221
|
-
// If only b is in the
|
|
223
|
+
// If only b is in the runOrder, then a should be placed before it.
|
|
222
224
|
if (indexB !== -1) {
|
|
223
225
|
return -1;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
// If neither a nor b are in
|
|
228
|
+
// If neither a nor b are in runOrder, keep their original runOrder
|
|
227
229
|
return 0;
|
|
228
230
|
});
|
|
229
231
|
|
|
230
|
-
console.log(`${GREEN} Found tests${END}: ${
|
|
232
|
+
console.log(`${GREEN} Found tests (${performanceTests.length})${END}: ${performanceTests.map(test => test.testName)}`);
|
|
233
|
+
console.log(
|
|
234
|
+
`${GREEN} Running tests (${sortedPerformanceTests.length})${END}: ${sortedPerformanceTests.map(test => test.testName)}`
|
|
235
|
+
);
|
|
231
236
|
|
|
232
237
|
sortedPerformanceTests.forEach(item => {
|
|
233
238
|
const { suite, testName, file } = item;
|
|
@@ -245,22 +250,22 @@ sortedPerformanceTests.forEach(item => {
|
|
|
245
250
|
return {
|
|
246
251
|
'test name': benchmark.name,
|
|
247
252
|
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
|
248
|
-
'executions per sec': numberFix(benchmark.hz, 2),
|
|
253
|
+
// 'executions per sec': numberFix(benchmark.hz, 2),
|
|
249
254
|
// 'executed times': numberFix(benchmark.count, 0),
|
|
250
|
-
|
|
255
|
+
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
|
251
256
|
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
|
252
257
|
};
|
|
253
258
|
});
|
|
254
259
|
|
|
255
260
|
report[testName].testName = testName;
|
|
256
|
-
const isDone = completedCount ===
|
|
261
|
+
const isDone = completedCount === sortedPerformanceTests.length;
|
|
257
262
|
runTime = Number(runTime.toFixed(2));
|
|
258
263
|
const isTimeWarn = runTime > 120;
|
|
259
264
|
console.log(
|
|
260
265
|
// `Files: ${GREEN}${testFileCount}${END} `,
|
|
261
266
|
// `Suites: ${GREEN}${performanceTests.length}${END} `,
|
|
262
|
-
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${
|
|
263
|
-
`Time: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
|
267
|
+
`Suites Progress: ${isDone ? GREEN : YELLOW}${completedCount}${END}/${isDone ? GREEN : YELLOW}${sortedPerformanceTests.length}${END}`,
|
|
268
|
+
`Time Costs: ${isTimeWarn ? YELLOW : GREEN}${runTime}s${END}`
|
|
264
269
|
);
|
|
265
270
|
if (isDone) {
|
|
266
271
|
composeReport();
|
|
@@ -736,7 +736,7 @@ describe('AVLTree toEntryFn', () => {
|
|
|
736
736
|
{ obj: { id: 5 } }
|
|
737
737
|
])
|
|
738
738
|
).toThrowError(
|
|
739
|
-
`When comparing object types, a custom
|
|
739
|
+
`When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.`
|
|
740
740
|
);
|
|
741
741
|
});
|
|
742
742
|
|
|
@@ -744,7 +744,7 @@ describe('AVLTree toEntryFn', () => {
|
|
|
744
744
|
const tree = new AVLTreeMultiMap<{ obj: { id: number } }, number>(
|
|
745
745
|
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
|
746
746
|
{
|
|
747
|
-
|
|
747
|
+
extractComparable: key => key.obj.id
|
|
748
748
|
}
|
|
749
749
|
);
|
|
750
750
|
|
|
@@ -103,9 +103,9 @@ describe('BinaryTree addMany', () => {
|
|
|
103
103
|
[undefined, 22, 44, 33]
|
|
104
104
|
);
|
|
105
105
|
expect(tree.get(2)).toBe(22);
|
|
106
|
-
expect(tree.get(tree.
|
|
107
|
-
expect(tree.get(tree.
|
|
108
|
-
expect(tree.get(tree.
|
|
106
|
+
expect(tree.get(tree.getNode(3))).toBe(33);
|
|
107
|
+
expect(tree.get(tree.getNode(4))).toBe(44);
|
|
108
|
+
expect(tree.get(tree.getNode(1))).toBe(1);
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
it('should addMany undefined and null', () => {
|
|
@@ -349,7 +349,7 @@ describe('BinaryTree', () => {
|
|
|
349
349
|
expect(tree.isBST(tree.getNode(4), 'ITERATIVE')).toBe(true);
|
|
350
350
|
expect(tree.getNodes(2, false, null)).toEqual([]);
|
|
351
351
|
expect(tree.getNodes(undefined)).toEqual([]);
|
|
352
|
-
expect(tree.getNodes(tree.
|
|
352
|
+
expect(tree.getNodes(tree.getNode(2), false, tree.root)).toEqual([tree.getNode(2)]);
|
|
353
353
|
});
|
|
354
354
|
|
|
355
355
|
describe('should isKey', () => {
|
|
@@ -362,9 +362,9 @@ describe('BinaryTree', () => {
|
|
|
362
362
|
expect(tree.isKey(-Infinity)).toBe(true);
|
|
363
363
|
});
|
|
364
364
|
|
|
365
|
-
it('NaN should not be a key', () => {
|
|
366
|
-
|
|
367
|
-
});
|
|
365
|
+
// it('NaN should not be a key', () => {
|
|
366
|
+
// expect(tree.isKey(NaN)).toBe(false);
|
|
367
|
+
// });
|
|
368
368
|
|
|
369
369
|
it('strings should be a key', () => {
|
|
370
370
|
expect(tree.isKey('hello')).toBe(true);
|
|
@@ -400,9 +400,9 @@ describe('BinaryTree', () => {
|
|
|
400
400
|
expect(tree.isKey(new Date('2024-01-01'))).toBe(true);
|
|
401
401
|
});
|
|
402
402
|
|
|
403
|
-
it('invalid Date objects should not be a key', () => {
|
|
404
|
-
|
|
405
|
-
});
|
|
403
|
+
// it('invalid Date objects should not be a key', () => {
|
|
404
|
+
// expect(tree.isKey(new Date('invalid'))).toBe(false);
|
|
405
|
+
// });
|
|
406
406
|
});
|
|
407
407
|
|
|
408
408
|
describe('arrays', () => {
|
|
@@ -1155,8 +1155,8 @@ describe('BinaryTree', () => {
|
|
|
1155
1155
|
tree.add([3, 'B']);
|
|
1156
1156
|
tree.add([7, 'C']);
|
|
1157
1157
|
|
|
1158
|
-
expect(tree.getPathToRoot(
|
|
1159
|
-
expect(tree.getPathToRoot(
|
|
1158
|
+
expect(tree.getPathToRoot(7)).toEqual([7, 5]);
|
|
1159
|
+
expect(tree.getPathToRoot(1)).toEqual([]);
|
|
1160
1160
|
});
|
|
1161
1161
|
|
|
1162
1162
|
it('should check if the tree is perfectly balanced', () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BinaryTreeNode, BST, BSTNode } from '../../../../src';
|
|
1
|
+
import { BinaryTreeNode, BST, BSTNode, Range } from '../../../../src';
|
|
2
2
|
import { isDebugTest, isTestStackOverflow, SYSTEM_MAX_CALL_STACK } from '../../../config';
|
|
3
3
|
|
|
4
4
|
const isDebug = isDebugTest;
|
|
@@ -974,7 +974,7 @@ describe('BST operations test recursively', () => {
|
|
|
974
974
|
|
|
975
975
|
if (isTestStackOverflow) {
|
|
976
976
|
it('should getLeftMost', () => {
|
|
977
|
-
const bst = new BST<number>([], {
|
|
977
|
+
const bst = new BST<number>([], { extractComparable: key => key });
|
|
978
978
|
for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i);
|
|
979
979
|
|
|
980
980
|
expect(() => {
|
|
@@ -1009,7 +1009,7 @@ describe('BST isBST', function () {
|
|
|
1009
1009
|
|
|
1010
1010
|
it('isBST when variant is Max', () => {
|
|
1011
1011
|
const bst = new BST<number, number>([1, 2, 3, 9, 8, 5, 6, 7, 4], {
|
|
1012
|
-
|
|
1012
|
+
isReverse: true
|
|
1013
1013
|
});
|
|
1014
1014
|
bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]);
|
|
1015
1015
|
expect(bst.isBST()).toBe(true);
|
|
@@ -1529,3 +1529,79 @@ describe('BST iterative methods not map mode test', () => {
|
|
|
1529
1529
|
expect(balanced.leaves(node => balanced.get(node?.key))).toEqual(['a', 'f', 'd', 'i']);
|
|
1530
1530
|
});
|
|
1531
1531
|
});
|
|
1532
|
+
|
|
1533
|
+
describe('classic use', () => {
|
|
1534
|
+
// Test case for finding the kth smallest element
|
|
1535
|
+
it('@example Find kth smallest element', () => {
|
|
1536
|
+
// Create a BST with some elements
|
|
1537
|
+
const bst = new BST<number>([5, 3, 7, 1, 4, 6, 8]);
|
|
1538
|
+
const sortedKeys = bst.dfs(node => node.key, 'IN');
|
|
1539
|
+
|
|
1540
|
+
// Helper function to find kth smallest
|
|
1541
|
+
const findKthSmallest = (k: number): number | undefined => {
|
|
1542
|
+
return sortedKeys[k - 1];
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
// Assertions
|
|
1546
|
+
expect(findKthSmallest(1)).toBe(1);
|
|
1547
|
+
expect(findKthSmallest(3)).toBe(4);
|
|
1548
|
+
expect(findKthSmallest(7)).toBe(8);
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
// Test case for finding elements in a given range
|
|
1552
|
+
it('@example Find elements in a range', () => {
|
|
1553
|
+
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
|
1554
|
+
expect(bst.search(new Range(5, 10))).toEqual([10, 5, 7]);
|
|
1555
|
+
expect(bst.search(new Range(4, 12))).toEqual([10, 12, 5, 7]);
|
|
1556
|
+
expect(bst.search(new Range(4, 12, true, false))).toEqual([10, 5, 7]);
|
|
1557
|
+
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
|
|
1558
|
+
expect(bst.search(new Range(15, 20, false))).toEqual([18]);
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
// Test case for Huffman coding simulation
|
|
1562
|
+
it('Huffman coding frequency simulation', () => {
|
|
1563
|
+
// Create a BST to simulate Huffman tree
|
|
1564
|
+
const frequencyBST = new BST<string, number>([
|
|
1565
|
+
['a', 5],
|
|
1566
|
+
['b', 9],
|
|
1567
|
+
['c', 12],
|
|
1568
|
+
['d', 13],
|
|
1569
|
+
['e', 16],
|
|
1570
|
+
['f', 45]
|
|
1571
|
+
]);
|
|
1572
|
+
|
|
1573
|
+
// Sort nodes by frequency
|
|
1574
|
+
const sortedFrequencies = frequencyBST.dfs(node => ({ char: node.key, freq: node.value }), 'IN');
|
|
1575
|
+
|
|
1576
|
+
// Build Huffman tree simulation
|
|
1577
|
+
expect(sortedFrequencies[0].char).toBe('a');
|
|
1578
|
+
expect(sortedFrequencies[5].char).toBe('f');
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
// Test case for Lowest Common Ancestor (LCA)
|
|
1582
|
+
it('@example Find lowest common ancestor', () => {
|
|
1583
|
+
const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
|
1584
|
+
|
|
1585
|
+
function findFirstCommon(arr1: number[], arr2: number[]): number | undefined {
|
|
1586
|
+
for (const num of arr1) {
|
|
1587
|
+
if (arr2.indexOf(num) !== -1) {
|
|
1588
|
+
return num;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
return undefined;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// LCA helper function
|
|
1595
|
+
const findLCA = (num1: number, num2: number): number | undefined => {
|
|
1596
|
+
const path1 = bst.getPathToRoot(num1);
|
|
1597
|
+
const path2 = bst.getPathToRoot(num2);
|
|
1598
|
+
// Find the first common ancestor
|
|
1599
|
+
return findFirstCommon(path1, path2);
|
|
1600
|
+
};
|
|
1601
|
+
|
|
1602
|
+
// Assertions
|
|
1603
|
+
expect(findLCA(3, 10)).toBe(7);
|
|
1604
|
+
expect(findLCA(5, 35)).toBe(15);
|
|
1605
|
+
expect(findLCA(20, 30)).toBe(25);
|
|
1606
|
+
});
|
|
1607
|
+
});
|