data-structure-typed 2.2.2 → 2.2.4
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 +3 -1
- package/README.md +355 -1672
- package/README_CN.md +509 -0
- package/SECURITY.md +962 -11
- package/SECURITY.zh-CN.md +966 -0
- package/SPECIFICATION.md +689 -30
- package/SPECIFICATION.zh-CN.md +715 -0
- package/SPONSOR.zh-CN.md +62 -0
- package/SPONSOR_POLISHED.md +62 -0
- package/benchmark/report.html +1 -1
- package/benchmark/report.json +215 -172
- package/dist/cjs/index.cjs +245 -72
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +246 -72
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +245 -72
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +246 -72
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree-counter.d.ts +2 -2
- package/dist/types/data-structures/binary-tree/avl-tree-multi-map.d.ts +5 -5
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +98 -5
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
- package/dist/types/data-structures/binary-tree/bst.d.ts +202 -39
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +86 -37
- package/dist/types/data-structures/binary-tree/tree-counter.d.ts +4 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +7 -7
- 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/types/types/data-structures/binary-tree/bst.d.ts +5 -5
- package/dist/umd/data-structure-typed.js +246 -72
- package/dist/umd/data-structure-typed.js.map +1 -1
- package/dist/umd/data-structure-typed.min.js +3 -3
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +3 -2
- package/src/data-structures/binary-tree/avl-tree-counter.ts +1 -2
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +7 -8
- package/src/data-structures/binary-tree/avl-tree.ts +100 -7
- package/src/data-structures/binary-tree/binary-tree.ts +117 -7
- package/src/data-structures/binary-tree/bst.ts +431 -93
- package/src/data-structures/binary-tree/red-black-tree.ts +85 -37
- package/src/data-structures/binary-tree/tree-counter.ts +5 -7
- package/src/data-structures/binary-tree/tree-multi-map.ts +9 -10
- 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/src/types/data-structures/binary-tree/bst.ts +5 -5
- package/test/performance/benchmark-runner.ts +14 -11
- package/test/performance/data-structures/binary-tree/avl-tree.test.ts +8 -8
- package/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +8 -8
- package/test/performance/data-structures/binary-tree/binary-tree.test.ts +6 -6
- package/test/performance/data-structures/binary-tree/bst.test.ts +5 -5
- package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +10 -10
- package/test/performance/reportor.ts +2 -1
- package/test/performance/single-suite-runner.ts +7 -4
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +117 -0
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +166 -0
- package/test/unit/data-structures/binary-tree/bst.test.ts +771 -16
- package/test/unit/data-structures/binary-tree/overall.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +90 -38
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +2 -2
- package/test/unit/data-structures/graph/directed-graph.test.ts +133 -0
- package/test/unit/data-structures/graph/undirected-graph.test.ts +167 -0
- package/test/unit/data-structures/hash/hash-map.test.ts +149 -3
- package/test/unit/data-structures/heap/heap.test.ts +182 -47
- package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +118 -14
- package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +121 -0
- package/test/unit/data-structures/queue/deque.test.ts +98 -67
- package/test/unit/data-structures/queue/queue.test.ts +85 -51
- package/test/unit/data-structures/stack/stack.test.ts +142 -33
- package/test/unit/data-structures/trie/trie.test.ts +135 -39
- package/tsup.leetcode.config.js +99 -0
- package/typedoc.json +2 -1
- package/POSTS_zh-CN.md +0 -54
- package/README_zh-CN.md +0 -1208
- package/SPECIFICATION_zh-CN.md +0 -81
|
@@ -112,53 +112,99 @@ export class TrieNode {
|
|
|
112
112
|
* 10. IP Routing: Used in certain types of IP routing algorithms.
|
|
113
113
|
* 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data.
|
|
114
114
|
* @example
|
|
115
|
-
* //
|
|
116
|
-
*
|
|
115
|
+
* // basic Trie creation and add words
|
|
116
|
+
* // Create a simple Trie with initial words
|
|
117
|
+
* const trie = new Trie(['apple', 'app', 'apply']);
|
|
117
118
|
*
|
|
118
|
-
* //
|
|
119
|
-
*
|
|
120
|
-
*
|
|
119
|
+
* // Verify size
|
|
120
|
+
* console.log(trie.size); // 3;
|
|
121
|
+
*
|
|
122
|
+
* // Check if words exist
|
|
123
|
+
* console.log(trie.has('apple')); // true;
|
|
124
|
+
* console.log(trie.has('app')); // true;
|
|
125
|
+
*
|
|
126
|
+
* // Add a new word
|
|
127
|
+
* trie.add('application');
|
|
128
|
+
* console.log(trie.size); // 4;
|
|
121
129
|
* @example
|
|
122
|
-
* //
|
|
123
|
-
*
|
|
124
|
-
* '/home/user/documents/file1.txt',
|
|
125
|
-
* '/home/user/documents/file2.txt',
|
|
126
|
-
* '/home/user/pictures/photo.jpg',
|
|
127
|
-
* '/home/user/pictures/vacation/',
|
|
128
|
-
* '/home/user/downloads'
|
|
129
|
-
* ]);
|
|
130
|
+
* // Trie getWords and prefix search
|
|
131
|
+
* const trie = new Trie(['apple', 'app', 'apply', 'application', 'apricot']);
|
|
130
132
|
*
|
|
131
|
-
* //
|
|
132
|
-
*
|
|
133
|
+
* // Get all words with prefix 'app'
|
|
134
|
+
* const appWords = trie.getWords('app');
|
|
135
|
+
* console.log(appWords); // contains 'app';
|
|
136
|
+
* console.log(appWords); // contains 'apple';
|
|
137
|
+
* console.log(appWords); // contains 'apply';
|
|
138
|
+
* console.log(appWords); // contains 'application';
|
|
139
|
+
* expect(appWords).not.toContain('apricot');
|
|
140
|
+
* @example
|
|
141
|
+
* // Trie isPrefix and isAbsolutePrefix checks
|
|
142
|
+
* const trie = new Trie(['tree', 'trial', 'trick', 'trip', 'trie']);
|
|
133
143
|
*
|
|
134
|
-
* //
|
|
135
|
-
*
|
|
136
|
-
* console.log(
|
|
144
|
+
* // Check if string is a prefix of any word
|
|
145
|
+
* console.log(trie.hasPrefix('tri')); // true;
|
|
146
|
+
* console.log(trie.hasPrefix('tr')); // true;
|
|
147
|
+
* console.log(trie.hasPrefix('xyz')); // false;
|
|
148
|
+
*
|
|
149
|
+
* // Check if string is an absolute prefix (not a complete word)
|
|
150
|
+
* console.log(trie.hasPurePrefix('tri')); // true;
|
|
151
|
+
* console.log(trie.hasPurePrefix('tree')); // false; // 'tree' is a complete word
|
|
152
|
+
*
|
|
153
|
+
* // Verify size
|
|
154
|
+
* console.log(trie.size); // 5;
|
|
137
155
|
* @example
|
|
138
|
-
* //
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* 'closure',
|
|
148
|
-
* 'const',
|
|
149
|
-
* 'constructor'
|
|
150
|
-
* ]);
|
|
156
|
+
* // Trie delete and iteration
|
|
157
|
+
* const trie = new Trie(['car', 'card', 'care', 'careful', 'can', 'cat']);
|
|
158
|
+
*
|
|
159
|
+
* // Delete a word
|
|
160
|
+
* trie.delete('card');
|
|
161
|
+
* console.log(trie.has('card')); // false;
|
|
162
|
+
*
|
|
163
|
+
* // Word with same prefix still exists
|
|
164
|
+
* console.log(trie.has('care')); // true;
|
|
151
165
|
*
|
|
152
|
-
* //
|
|
153
|
-
* console.log(
|
|
154
|
-
* console.log(autocomplete.getWords('cla')); // ['classes', 'classical', 'class']
|
|
155
|
-
* console.log(autocomplete.getWords('con')); // ['constructor', 'const']
|
|
166
|
+
* // Size decreased
|
|
167
|
+
* console.log(trie.size); // 5;
|
|
156
168
|
*
|
|
157
|
-
* //
|
|
158
|
-
*
|
|
169
|
+
* // Iterate through all words
|
|
170
|
+
* const allWords = [...trie];
|
|
171
|
+
* console.log(allWords.length); // 5;
|
|
172
|
+
* @example
|
|
173
|
+
* // Trie for autocomplete search index
|
|
174
|
+
* // Trie is perfect for autocomplete: O(m + k) where m is prefix length, k is results
|
|
175
|
+
* const searchIndex = new Trie(['typescript', 'javascript', 'python', 'java', 'rust', 'ruby', 'golang', 'kotlin']);
|
|
176
|
+
*
|
|
177
|
+
* // User types 'j' - get all suggestions
|
|
178
|
+
* const jResults = searchIndex.getWords('j');
|
|
179
|
+
* console.log(jResults); // contains 'javascript';
|
|
180
|
+
* console.log(jResults); // contains 'java';
|
|
181
|
+
* console.log(jResults.length); // 2;
|
|
182
|
+
*
|
|
183
|
+
* // User types 'ja' - get more specific suggestions
|
|
184
|
+
* const jaResults = searchIndex.getWords('ja');
|
|
185
|
+
* console.log(jaResults); // contains 'javascript';
|
|
186
|
+
* console.log(jaResults); // contains 'java';
|
|
187
|
+
* console.log(jaResults.length); // 2;
|
|
188
|
+
*
|
|
189
|
+
* // User types 'jav' - even more specific
|
|
190
|
+
* const javResults = searchIndex.getWords('jav');
|
|
191
|
+
* console.log(javResults); // contains 'javascript';
|
|
192
|
+
* console.log(javResults); // contains 'java';
|
|
193
|
+
* console.log(javResults.length); // 2;
|
|
194
|
+
*
|
|
195
|
+
* // Check for common prefix
|
|
196
|
+
*
|
|
197
|
+
* console.log(searchIndex.hasCommonPrefix('ja')); // false; // Not all words start with 'ja'
|
|
198
|
+
*
|
|
199
|
+
* // Total words in index
|
|
200
|
+
* console.log(searchIndex.size); // 8;
|
|
201
|
+
*
|
|
202
|
+
* // Get height (depth of tree)
|
|
203
|
+
* const height = searchIndex.getHeight();
|
|
204
|
+
* console.log(typeof height); // 'number';
|
|
159
205
|
* @example
|
|
160
206
|
* // Dictionary: Case-insensitive word lookup
|
|
161
|
-
*
|
|
207
|
+
* // Create a case-insensitive dictionary
|
|
162
208
|
* const dictionary = new Trie<string>([], { caseSensitive: false });
|
|
163
209
|
*
|
|
164
210
|
* // Add words with mixed casing
|
|
@@ -167,14 +213,30 @@ export class TrieNode {
|
|
|
167
213
|
* dictionary.add('JavaScript');
|
|
168
214
|
*
|
|
169
215
|
* // Test lookups with different casings
|
|
170
|
-
* console.log(dictionary.has('hello')); // true
|
|
171
|
-
* console.log(dictionary.has('HELLO')); // true
|
|
172
|
-
* console.log(dictionary.has('Hello')); // true
|
|
173
|
-
* console.log(dictionary.has('javascript')); // true
|
|
174
|
-
* console.log(dictionary.has('JAVASCRIPT')); // true
|
|
216
|
+
* console.log(dictionary.has('hello')); // true;
|
|
217
|
+
* console.log(dictionary.has('HELLO')); // true;
|
|
218
|
+
* console.log(dictionary.has('Hello')); // true;
|
|
219
|
+
* console.log(dictionary.has('javascript')); // true;
|
|
220
|
+
* console.log(dictionary.has('JAVASCRIPT')); // true;
|
|
221
|
+
* @example
|
|
222
|
+
* // File System Path Operations
|
|
223
|
+
* const fileSystem = new Trie<string>([
|
|
224
|
+
* '/home/user/documents/file1.txt',
|
|
225
|
+
* '/home/user/documents/file2.txt',
|
|
226
|
+
* '/home/user/pictures/photo.jpg',
|
|
227
|
+
* '/home/user/pictures/vacation/',
|
|
228
|
+
* '/home/user/downloads'
|
|
229
|
+
* ]);
|
|
230
|
+
*
|
|
231
|
+
* // Find common directory prefix
|
|
232
|
+
* console.log(fileSystem.getLongestCommonPrefix()); // '/home/user/';
|
|
233
|
+
*
|
|
234
|
+
* // List all files in a directory
|
|
235
|
+
* const documentsFiles = fileSystem.getWords('/home/user/documents/');
|
|
236
|
+
* console.log(documentsFiles); // ['/home/user/documents/file1.txt', '/home/user/documents/file2.txt'];
|
|
175
237
|
* @example
|
|
176
238
|
* // IP Address Routing Table
|
|
177
|
-
*
|
|
239
|
+
* // Add IP address prefixes and their corresponding routes
|
|
178
240
|
* const routes = {
|
|
179
241
|
* '192.168.1': 'LAN_SUBNET_1',
|
|
180
242
|
* '192.168.2': 'LAN_SUBNET_2',
|
|
@@ -185,13 +247,13 @@ export class TrieNode {
|
|
|
185
247
|
* const ipRoutingTable = new Trie<string>(Object.keys(routes));
|
|
186
248
|
*
|
|
187
249
|
* // Check IP address prefix matching
|
|
188
|
-
* console.log(ipRoutingTable.hasPrefix('192.168.1')); // true
|
|
189
|
-
* console.log(ipRoutingTable.hasPrefix('192.168.2')); // true
|
|
250
|
+
* console.log(ipRoutingTable.hasPrefix('192.168.1')); // true;
|
|
251
|
+
* console.log(ipRoutingTable.hasPrefix('192.168.2')); // true;
|
|
190
252
|
*
|
|
191
253
|
* // Validate IP address belongs to subnet
|
|
192
254
|
* const ip = '192.168.1.100';
|
|
193
255
|
* const subnet = ip.split('.').slice(0, 3).join('.');
|
|
194
|
-
* console.log(ipRoutingTable.hasPrefix(subnet)); // true
|
|
256
|
+
* console.log(ipRoutingTable.hasPrefix(subnet)); // true;
|
|
195
257
|
*/
|
|
196
258
|
export class Trie<R = any> extends IterableElementBase<string, R> {
|
|
197
259
|
/**
|
|
@@ -38,6 +38,8 @@ export interface IBinaryTree<K = any, V = any, R = any> {
|
|
|
38
38
|
|
|
39
39
|
add(keyOrNodeOrEntryOrRawElement: BTNRep<K, V, BinaryTreeNode<K, V>>, value?: V, count?: number): boolean;
|
|
40
40
|
|
|
41
|
+
set(keyOrNodeOrEntryOrRawElement: BTNRep<K, V, BinaryTreeNode<K, V>>, value?: V, count?: number): boolean;
|
|
42
|
+
|
|
41
43
|
// Accept raw R as well (when toEntryFn is configured)
|
|
42
44
|
addMany(
|
|
43
45
|
keysNodesEntriesOrRaws: Iterable<
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { BinaryTreeOptions } from './binary-tree';
|
|
2
|
-
import {
|
|
3
|
-
import { OptValue } from '../../common';
|
|
2
|
+
import type { Comparator, OptValue } from '../../common';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
type BSTBaseOptions<K, V, R> = Omit<BinaryTreeOptions<K, V, R>, 'isDuplicate'>;
|
|
5
|
+
|
|
6
|
+
export type BSTOptions<K, V, R> = BSTBaseOptions<K, V, R> & {
|
|
7
|
+
comparator?: Comparator<K>;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export type BSTNOptKey<K> = K | undefined;
|
|
@@ -4,6 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as fastGlob from 'fast-glob';
|
|
5
5
|
import { fork } from 'child_process';
|
|
6
6
|
import { ConsoleColor, numberFix } from '../utils';
|
|
7
|
+
import * as console from 'node:console';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Optimized benchmark runner
|
|
@@ -279,7 +280,7 @@ testFiles = sortByOrder(testFiles);
|
|
|
279
280
|
if (flags.shuffle) shuffle(testFiles);
|
|
280
281
|
|
|
281
282
|
const plannedCount = testFiles.length;
|
|
282
|
-
const isIndividual = filters.length > 0;
|
|
283
|
+
// const isIndividual = filters.length > 0;
|
|
283
284
|
|
|
284
285
|
// ---------------- report utils (kept from original) ----------------
|
|
285
286
|
const report: { [key: string]: any } = {};
|
|
@@ -309,19 +310,20 @@ function writeReportHTMLAndJSON(htmlTables: string) {
|
|
|
309
310
|
${htmlTables}
|
|
310
311
|
</div></body></html>`;
|
|
311
312
|
|
|
312
|
-
if (!isIndividual) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
313
|
+
// if (!isIndividual) {
|
|
314
|
+
replaceMarkdownContent(
|
|
315
|
+
'[//]: # (No deletion!!! Start of Replace Section)',
|
|
316
|
+
'[//]: # (No deletion!!! End of Replace Section)',
|
|
317
|
+
htmlTables
|
|
318
|
+
);
|
|
319
|
+
// }
|
|
319
320
|
fs.writeFileSync(htmlFilePath, html);
|
|
320
321
|
console.log(`Performance ${BOLD}${GREEN}report${END} file generated in file://${BOLD}${GREEN}${htmlFilePath}${END}`);
|
|
321
322
|
}
|
|
322
323
|
|
|
323
324
|
function replaceMarkdownContent(startMarker: string, endMarker: string, newText: string) {
|
|
324
|
-
const filePath = path.join(parentDirectory, '
|
|
325
|
+
const filePath = path.join(parentDirectory, '/docs/PERFORMANCE.md');
|
|
326
|
+
|
|
325
327
|
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
326
328
|
if (err) {
|
|
327
329
|
console.error(`Unable to read ${filePath}:`, err);
|
|
@@ -330,10 +332,11 @@ function replaceMarkdownContent(startMarker: string, endMarker: string, newText:
|
|
|
330
332
|
const startIndex = data.indexOf(startMarker);
|
|
331
333
|
const endIndex = data.indexOf(endMarker);
|
|
332
334
|
if (startIndex === -1 || endIndex === -1 || startIndex >= endIndex) {
|
|
333
|
-
console.warn('Markers not found or invalid range; skip
|
|
335
|
+
console.warn('Markers not found or invalid range; skip PERFORMANCE injection.');
|
|
334
336
|
return;
|
|
335
337
|
}
|
|
336
|
-
const updatedMarkdown =
|
|
338
|
+
const updatedMarkdown =
|
|
339
|
+
data.slice(0, startIndex + startMarker.length) + '\n\n' + newText + '\n\n' + data.slice(endIndex);
|
|
337
340
|
fs.writeFile(filePath, updatedMarkdown, 'utf8', err2 => {
|
|
338
341
|
if (err2) console.error(`Unable to write to ${filePath}:`, err2);
|
|
339
342
|
else console.log(`The content has been successfully replaced in file://${BOLD}${GREEN}${filePath}${END}`);
|
|
@@ -8,13 +8,13 @@ const { HUNDRED_THOUSAND } = magnitude;
|
|
|
8
8
|
const randomArray = getRandomIntArray(HUNDRED_THOUSAND, 0, HUNDRED_THOUSAND - 1, true);
|
|
9
9
|
|
|
10
10
|
suite
|
|
11
|
-
.add(`${HUNDRED_THOUSAND.toLocaleString()}
|
|
11
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} set randomly`, () => {
|
|
12
12
|
avlTree.clear();
|
|
13
|
-
for (let i = 0; i < randomArray.length; i++) avlTree.
|
|
13
|
+
for (let i = 0; i < randomArray.length; i++) avlTree.set(randomArray[i]);
|
|
14
14
|
})
|
|
15
|
-
.add(`${HUNDRED_THOUSAND.toLocaleString()}
|
|
15
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} set`, () => {
|
|
16
16
|
avlTree.clear();
|
|
17
|
-
for (let i = 0; i < randomArray.length; i++) avlTree.
|
|
17
|
+
for (let i = 0; i < randomArray.length; i++) avlTree.set(i);
|
|
18
18
|
})
|
|
19
19
|
.add(`${HUNDRED_THOUSAND.toLocaleString()} get`, () => {
|
|
20
20
|
for (let i = 0; i < randomArray.length; i++) avlTree.get(randomArray[i]);
|
|
@@ -26,14 +26,14 @@ suite
|
|
|
26
26
|
const entries = [...avlTree];
|
|
27
27
|
return entries.length === HUNDRED_THOUSAND;
|
|
28
28
|
})
|
|
29
|
-
.add(`${HUNDRED_THOUSAND.toLocaleString()}
|
|
29
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} set & delete orderly`, () => {
|
|
30
30
|
avlTree.clear();
|
|
31
|
-
for (let i = 0; i < randomArray.length; i++) avlTree.
|
|
31
|
+
for (let i = 0; i < randomArray.length; i++) avlTree.set(i);
|
|
32
32
|
for (let i = 0; i < randomArray.length; i++) avlTree.delete(i);
|
|
33
33
|
})
|
|
34
|
-
.add(`${HUNDRED_THOUSAND.toLocaleString()}
|
|
34
|
+
.add(`${HUNDRED_THOUSAND.toLocaleString()} set & delete randomly`, () => {
|
|
35
35
|
avlTree.clear();
|
|
36
|
-
for (let i = 0; i < randomArray.length; i++) avlTree.
|
|
36
|
+
for (let i = 0; i < randomArray.length; i++) avlTree.set(randomArray[i]);
|
|
37
37
|
for (let i = 0; i < randomArray.length; i++) avlTree.delete(randomArray[i]);
|
|
38
38
|
});
|
|
39
39
|
|
|
@@ -9,28 +9,28 @@ const { TEN_THOUSAND } = magnitude;
|
|
|
9
9
|
const arr = getRandomIntArray(TEN_THOUSAND, 0, TEN_THOUSAND - 1, true);
|
|
10
10
|
|
|
11
11
|
suite
|
|
12
|
-
.add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree
|
|
12
|
+
.add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree set randomly`, () => {
|
|
13
13
|
rbTree.clear();
|
|
14
|
-
for (let i = 0; i < arr.length; i++) rbTree.
|
|
14
|
+
for (let i = 0; i < arr.length; i++) rbTree.set(arr[i]);
|
|
15
15
|
})
|
|
16
16
|
.add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree get randomly`, () => {
|
|
17
17
|
for (let i = 0; i < arr.length; i++) rbTree.get(arr[i]);
|
|
18
18
|
})
|
|
19
|
-
.add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree
|
|
19
|
+
.add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree set & delete randomly`, () => {
|
|
20
20
|
rbTree.clear();
|
|
21
|
-
for (let i = 0; i < arr.length; i++) rbTree.
|
|
21
|
+
for (let i = 0; i < arr.length; i++) rbTree.set(arr[i]);
|
|
22
22
|
for (let i = 0; i < arr.length; i++) rbTree.delete(arr[i]);
|
|
23
23
|
})
|
|
24
|
-
.add(`${TEN_THOUSAND.toLocaleString()} AVLTree
|
|
24
|
+
.add(`${TEN_THOUSAND.toLocaleString()} AVLTree set randomly`, () => {
|
|
25
25
|
avlTree.clear();
|
|
26
|
-
for (let i = 0; i < arr.length; i++) avlTree.
|
|
26
|
+
for (let i = 0; i < arr.length; i++) avlTree.set(arr[i]);
|
|
27
27
|
})
|
|
28
28
|
.add(`${TEN_THOUSAND.toLocaleString()} AVLTree get randomly`, () => {
|
|
29
29
|
for (let i = 0; i < arr.length; i++) avlTree.get(arr[i]);
|
|
30
30
|
})
|
|
31
|
-
.add(`${TEN_THOUSAND.toLocaleString()} AVLTree
|
|
31
|
+
.add(`${TEN_THOUSAND.toLocaleString()} AVLTree set & delete randomly`, () => {
|
|
32
32
|
avlTree.clear();
|
|
33
|
-
for (let i = 0; i < arr.length; i++) avlTree.
|
|
33
|
+
for (let i = 0; i < arr.length; i++) avlTree.set(arr[i]);
|
|
34
34
|
for (let i = 0; i < arr.length; i++) avlTree.delete(arr[i]);
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -8,18 +8,18 @@ const { THOUSAND } = magnitude;
|
|
|
8
8
|
const arr = getRandomIntArray(THOUSAND, 0, THOUSAND, true);
|
|
9
9
|
|
|
10
10
|
suite
|
|
11
|
-
.add(`${THOUSAND.toLocaleString()}
|
|
11
|
+
.add(`${THOUSAND.toLocaleString()} set randomly`, () => {
|
|
12
12
|
biTree.clear();
|
|
13
|
-
for (let i = 0; i < arr.length; i++) biTree.
|
|
13
|
+
for (let i = 0; i < arr.length; i++) biTree.set(arr[i]);
|
|
14
14
|
})
|
|
15
|
-
.add(`${THOUSAND.toLocaleString()}
|
|
15
|
+
.add(`${THOUSAND.toLocaleString()} set & delete randomly`, () => {
|
|
16
16
|
biTree.clear();
|
|
17
|
-
for (let i = 0; i < arr.length; i++) biTree.
|
|
17
|
+
for (let i = 0; i < arr.length; i++) biTree.set(arr[i]);
|
|
18
18
|
for (let i = 0; i < arr.length; i++) biTree.delete(arr[i]);
|
|
19
19
|
})
|
|
20
|
-
.add(`${THOUSAND.toLocaleString()}
|
|
20
|
+
.add(`${THOUSAND.toLocaleString()} setMany`, () => {
|
|
21
21
|
biTree.clear();
|
|
22
|
-
biTree.
|
|
22
|
+
biTree.setMany(arr);
|
|
23
23
|
})
|
|
24
24
|
.add(`${THOUSAND.toLocaleString()} get`, () => {
|
|
25
25
|
for (let i = 0; i < arr.length; i++) biTree.get(arr[i]);
|
|
@@ -8,18 +8,18 @@ const { TEN_THOUSAND } = magnitude;
|
|
|
8
8
|
const arr = getRandomIntArray(TEN_THOUSAND, 0, TEN_THOUSAND, true);
|
|
9
9
|
|
|
10
10
|
suite
|
|
11
|
-
.add(`${TEN_THOUSAND.toLocaleString()}
|
|
11
|
+
.add(`${TEN_THOUSAND.toLocaleString()} set randomly`, () => {
|
|
12
12
|
bst.clear();
|
|
13
|
-
for (let i = 0; i < arr.length; i++) bst.
|
|
13
|
+
for (let i = 0; i < arr.length; i++) bst.set(arr[i]);
|
|
14
14
|
})
|
|
15
|
-
.add(`${TEN_THOUSAND.toLocaleString()}
|
|
15
|
+
.add(`${TEN_THOUSAND.toLocaleString()} set & delete randomly`, () => {
|
|
16
16
|
bst.clear();
|
|
17
17
|
for (let i = 0; i < arr.length; i++) bst.add(arr[i]);
|
|
18
18
|
for (let i = 0; i < arr.length; i++) bst.delete(arr[i]);
|
|
19
19
|
})
|
|
20
|
-
.add(`${TEN_THOUSAND.toLocaleString()}
|
|
20
|
+
.add(`${TEN_THOUSAND.toLocaleString()} setMany`, () => {
|
|
21
21
|
bst.clear();
|
|
22
|
-
bst.
|
|
22
|
+
bst.setMany(arr);
|
|
23
23
|
})
|
|
24
24
|
.add(`${TEN_THOUSAND.toLocaleString()} get`, () => {
|
|
25
25
|
for (let i = 0; i < arr.length; i++) bst.get(arr[i]);
|
|
@@ -12,13 +12,13 @@ const randomArray = getRandomIntArray(MILLION, 0, MILLION - 1, true);
|
|
|
12
12
|
const cOrderedMap = new OrderedMap<number, number>();
|
|
13
13
|
|
|
14
14
|
suite
|
|
15
|
-
// .add(`${MILLION.toLocaleString()}
|
|
15
|
+
// .add(`${MILLION.toLocaleString()} set randomly`, () => {
|
|
16
16
|
// rbTree.clear();
|
|
17
|
-
// for (let i = 0; i < randomArray.length; i++) rbTree.
|
|
17
|
+
// for (let i = 0; i < randomArray.length; i++) rbTree.set(randomArray[i]);
|
|
18
18
|
// })
|
|
19
|
-
.add(`${MILLION.toLocaleString()}
|
|
19
|
+
.add(`${MILLION.toLocaleString()} set`, () => {
|
|
20
20
|
rbTree.clear();
|
|
21
|
-
for (let i = 0; i < randomArray.length; i++) rbTree.
|
|
21
|
+
for (let i = 0; i < randomArray.length; i++) rbTree.set(i);
|
|
22
22
|
})
|
|
23
23
|
.add(`${MILLION.toLocaleString()} get`, () => {
|
|
24
24
|
for (let i = 0; i < randomArray.length; i++) rbTree.get(randomArray[i]);
|
|
@@ -37,23 +37,23 @@ suite
|
|
|
37
37
|
const entries = [...rbTree];
|
|
38
38
|
return entries.length === MILLION;
|
|
39
39
|
});
|
|
40
|
-
// .add(`${MILLION.toLocaleString()}
|
|
40
|
+
// .add(`${MILLION.toLocaleString()} set & delete orderly`, () => {
|
|
41
41
|
// rbTree.clear();
|
|
42
|
-
// for (let i = 0; i < randomArray.length; i++) rbTree.
|
|
42
|
+
// for (let i = 0; i < randomArray.length; i++) rbTree.set(i);
|
|
43
43
|
// for (let i = 0; i < randomArray.length; i++) rbTree.delete(i);
|
|
44
44
|
// })
|
|
45
|
-
// .add(`${MILLION.toLocaleString()}
|
|
45
|
+
// .add(`${MILLION.toLocaleString()} set & delete randomly`, () => {
|
|
46
46
|
// rbTree.clear();
|
|
47
|
-
// for (let i = 0; i < randomArray.length; i++) rbTree.
|
|
47
|
+
// for (let i = 0; i < randomArray.length; i++) rbTree.set(randomArray[i]);
|
|
48
48
|
// for (let i = 0; i < randomArray.length; i++) rbTree.delete(randomArray[i]);
|
|
49
49
|
// });
|
|
50
50
|
|
|
51
51
|
if (true) {
|
|
52
52
|
suite
|
|
53
|
-
.add(`
|
|
53
|
+
.add(`Competitor ${MILLION.toLocaleString()} set`, () => {
|
|
54
54
|
for (let i = 0; i < randomArray.length; i++) cOrderedMap.setElement(randomArray[i], randomArray[i]);
|
|
55
55
|
})
|
|
56
|
-
.add(`
|
|
56
|
+
.add(`Competitor ${MILLION.toLocaleString()} get`, () => {
|
|
57
57
|
for (let i = 0; i < randomArray.length; i++) cOrderedMap.getElementByKey(randomArray[i]);
|
|
58
58
|
});
|
|
59
59
|
}
|
|
@@ -174,7 +174,8 @@ const composeReport = () => {
|
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
function replaceMarkdownContent(startMarker: string, endMarker: string, newText: string) {
|
|
177
|
-
const filePath = path.join(parentDirectory, '
|
|
177
|
+
const filePath = path.join(parentDirectory, '/docs/PERFORMANCE.md'); // Path to PERFORMANCE.md file
|
|
178
|
+
console.log(`Replacing ${filePath}`);
|
|
178
179
|
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
179
180
|
if (err) {
|
|
180
181
|
console.error(`Unable to read ${filePath}:`, err);
|
|
@@ -52,10 +52,13 @@ if (!file) {
|
|
|
52
52
|
const benchmarks = this.map((benchmark: any) => {
|
|
53
53
|
runTime += benchmark.times.elapsed;
|
|
54
54
|
return {
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
58
|
-
'
|
|
55
|
+
'Test Case': benchmark.name,
|
|
56
|
+
'Latency Avg (ms)': numberFix(benchmark.stats.mean * 1000, 2), // Average time
|
|
57
|
+
'Min (ms)': numberFix(Math.min(...benchmark.stats.sample) * 1000, 2),
|
|
58
|
+
'Max (ms)': numberFix(Math.max(...benchmark.stats.sample) * 1000, 2),
|
|
59
|
+
Stability: `±${numberFix(benchmark.stats.rme, 2)}%` // Relative error (better to understand than deviation)
|
|
60
|
+
// 'Samples': benchmark.stats.sample.length, // Number of samples
|
|
61
|
+
// 'Ops/Sec': Math.round(benchmark.hz).toLocaleString(), // Operations per second
|
|
59
62
|
};
|
|
60
63
|
});
|
|
61
64
|
runTime = Number(runTime.toFixed(2));
|
|
@@ -739,7 +739,7 @@ describe('AVLTreeCounter toEntryFn', () => {
|
|
|
739
739
|
{ obj: { id: 5 } }
|
|
740
740
|
])
|
|
741
741
|
).toThrowError(
|
|
742
|
-
`When comparing object
|
|
742
|
+
`When comparing object type keys, a custom comparator must be provided in the constructor's options!`
|
|
743
743
|
);
|
|
744
744
|
});
|
|
745
745
|
|
|
@@ -747,7 +747,7 @@ describe('AVLTreeCounter toEntryFn', () => {
|
|
|
747
747
|
const avlCounter = new AVLTreeCounter<{ obj: { id: number } }, number>(
|
|
748
748
|
[{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }],
|
|
749
749
|
{
|
|
750
|
-
|
|
750
|
+
comparator: (a, b) => a.obj.id - b.obj.id,
|
|
751
751
|
}
|
|
752
752
|
);
|
|
753
753
|
|
|
@@ -544,6 +544,104 @@ describe('AVLTree iterative methods not map mode', () => {
|
|
|
544
544
|
});
|
|
545
545
|
|
|
546
546
|
describe('classic use', () => {
|
|
547
|
+
it('@example basic AVLTree creation and add operation', () => {
|
|
548
|
+
// Create a simple AVLTree with initial values
|
|
549
|
+
const tree = new AVLTree([5, 2, 8, 1, 9]);
|
|
550
|
+
|
|
551
|
+
tree.print();
|
|
552
|
+
// _2___
|
|
553
|
+
// / \
|
|
554
|
+
// 1 _8_
|
|
555
|
+
// / \
|
|
556
|
+
// 5 9
|
|
557
|
+
|
|
558
|
+
// Verify the tree maintains sorted order
|
|
559
|
+
expect([...tree.keys()]).toEqual([1, 2, 5, 8, 9]);
|
|
560
|
+
|
|
561
|
+
// Check size
|
|
562
|
+
expect(tree.size).toBe(5);
|
|
563
|
+
|
|
564
|
+
// Add a new element
|
|
565
|
+
tree.add(3);
|
|
566
|
+
expect(tree.size).toBe(6);
|
|
567
|
+
expect([...tree.keys()]).toEqual([1, 2, 3, 5, 8, 9]);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('@example AVLTree has and get operations', () => {
|
|
571
|
+
const tree = new AVLTree<number>([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
572
|
+
|
|
573
|
+
// Check if element exists
|
|
574
|
+
expect(tree.has(6)).toBe(true);
|
|
575
|
+
expect(tree.has(99)).toBe(false);
|
|
576
|
+
|
|
577
|
+
// Get node by key
|
|
578
|
+
const node = tree.getNode(6);
|
|
579
|
+
expect(node?.key).toBe(6);
|
|
580
|
+
|
|
581
|
+
// Verify tree is balanced
|
|
582
|
+
expect(tree.isAVLBalanced()).toBe(true);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('@example AVLTree delete and balance verification', () => {
|
|
586
|
+
const tree = new AVLTree([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
|
|
587
|
+
|
|
588
|
+
// Delete an element
|
|
589
|
+
tree.delete(10);
|
|
590
|
+
expect(tree.has(10)).toBe(false);
|
|
591
|
+
|
|
592
|
+
// Tree should remain balanced after deletion
|
|
593
|
+
expect(tree.isAVLBalanced()).toBe(true);
|
|
594
|
+
|
|
595
|
+
// Size decreased
|
|
596
|
+
expect(tree.size).toBe(15);
|
|
597
|
+
|
|
598
|
+
// Remaining elements are still sorted
|
|
599
|
+
const keys = [...tree.keys()];
|
|
600
|
+
expect(keys).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16]);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it('@example AVLTree for university ranking system with strict balance', () => {
|
|
604
|
+
interface University {
|
|
605
|
+
name: string;
|
|
606
|
+
rank: number;
|
|
607
|
+
students: number;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// AVLTree provides highest search efficiency with strict balance
|
|
611
|
+
// (every node's left/right subtrees differ by at most 1 in height)
|
|
612
|
+
const universityTree = new AVLTree<number, University>([
|
|
613
|
+
[1, { name: 'MIT', rank: 1, students: 1200 }],
|
|
614
|
+
[5, { name: 'Stanford', rank: 5, students: 1800 }],
|
|
615
|
+
[3, { name: 'Harvard', rank: 3, students: 2300 }],
|
|
616
|
+
[2, { name: 'Caltech', rank: 2, students: 400 }],
|
|
617
|
+
[4, { name: 'CMU', rank: 4, students: 1500 }]
|
|
618
|
+
]);
|
|
619
|
+
|
|
620
|
+
// Quick lookup by rank
|
|
621
|
+
const mit = universityTree.get(1);
|
|
622
|
+
expect(mit?.name).toBe('MIT');
|
|
623
|
+
|
|
624
|
+
const cmulevel = universityTree.getHeight(4);
|
|
625
|
+
expect(typeof cmulevel).toBe('number');
|
|
626
|
+
|
|
627
|
+
// Tree maintains strict balance during insertions and deletions
|
|
628
|
+
expect(universityTree.isAVLBalanced()).toBe(true);
|
|
629
|
+
|
|
630
|
+
// Add more universities
|
|
631
|
+
universityTree.add(6, { name: 'Oxford', rank: 6, students: 2000 });
|
|
632
|
+
expect(universityTree.isAVLBalanced()).toBe(true);
|
|
633
|
+
|
|
634
|
+
// Delete and verify balance is maintained
|
|
635
|
+
universityTree.delete(2);
|
|
636
|
+
expect(universityTree.has(2)).toBe(false);
|
|
637
|
+
expect(universityTree.isAVLBalanced()).toBe(true);
|
|
638
|
+
|
|
639
|
+
// Get all remaining universities in rank order
|
|
640
|
+
const remainingRanks = [...universityTree.keys()];
|
|
641
|
+
expect(remainingRanks).toEqual([1, 3, 4, 5, 6]);
|
|
642
|
+
expect(universityTree.size).toBe(5);
|
|
643
|
+
});
|
|
644
|
+
|
|
547
645
|
// Test case for finding elements in a given range
|
|
548
646
|
it('@example Find elements in a range', () => {
|
|
549
647
|
// 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.
|
|
@@ -609,4 +707,23 @@ describe('classic use', () => {
|
|
|
609
707
|
{ minute: 15, temperature: 58.6 }
|
|
610
708
|
]);
|
|
611
709
|
});
|
|
710
|
+
|
|
711
|
+
it('AVLTree tree height and getLeftMost', () => {
|
|
712
|
+
const tree = new AVLTree([5, 2, 8, 1, 9, 3, 7]);
|
|
713
|
+
|
|
714
|
+
// Get tree height
|
|
715
|
+
const height = tree.getHeight();
|
|
716
|
+
expect(typeof height).toBe('number');
|
|
717
|
+
|
|
718
|
+
// Get height of specific node
|
|
719
|
+
const heightOf5 = tree.getHeight(5);
|
|
720
|
+
expect(typeof heightOf5).toBe('number');
|
|
721
|
+
|
|
722
|
+
// Find leftmost (smallest) element
|
|
723
|
+
const min = tree.getLeftMost();
|
|
724
|
+
expect(min).toBe(1);
|
|
725
|
+
|
|
726
|
+
// Verify tree is balanced
|
|
727
|
+
expect(tree.isAVLBalanced()).toBe(true);
|
|
728
|
+
});
|
|
612
729
|
});
|