data-structure-typed 2.2.1 → 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.
Files changed (83) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/README.md +440 -1190
  3. package/README_CN.md +509 -0
  4. package/SECURITY.md +962 -11
  5. package/SECURITY.zh-CN.md +966 -0
  6. package/SPECIFICATION.md +689 -30
  7. package/SPECIFICATION.zh-CN.md +715 -0
  8. package/SPONSOR.zh-CN.md +62 -0
  9. package/SPONSOR_POLISHED.md +62 -0
  10. package/benchmark/report.html +1 -1
  11. package/benchmark/report.json +215 -172
  12. package/dist/cjs/index.cjs +163 -0
  13. package/dist/cjs/index.cjs.map +1 -1
  14. package/dist/cjs-legacy/index.cjs +164 -0
  15. package/dist/cjs-legacy/index.cjs.map +1 -1
  16. package/dist/esm/index.mjs +163 -0
  17. package/dist/esm/index.mjs.map +1 -1
  18. package/dist/esm-legacy/index.mjs +164 -0
  19. package/dist/esm-legacy/index.mjs.map +1 -1
  20. package/dist/types/data-structures/binary-tree/avl-tree.d.ts +96 -2
  21. package/dist/types/data-structures/binary-tree/binary-tree.d.ts +103 -7
  22. package/dist/types/data-structures/binary-tree/bst.d.ts +156 -13
  23. package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +84 -35
  24. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +2 -2
  25. package/dist/types/data-structures/graph/directed-graph.d.ts +126 -1
  26. package/dist/types/data-structures/graph/undirected-graph.d.ts +160 -1
  27. package/dist/types/data-structures/hash/hash-map.d.ts +110 -27
  28. package/dist/types/data-structures/heap/heap.d.ts +107 -58
  29. package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +72 -404
  30. package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +121 -5
  31. package/dist/types/data-structures/queue/deque.d.ts +95 -67
  32. package/dist/types/data-structures/queue/queue.d.ts +90 -34
  33. package/dist/types/data-structures/stack/stack.d.ts +58 -40
  34. package/dist/types/data-structures/trie/trie.d.ts +109 -47
  35. package/dist/types/interfaces/binary-tree.d.ts +1 -0
  36. package/dist/umd/data-structure-typed.js +164 -0
  37. package/dist/umd/data-structure-typed.js.map +1 -1
  38. package/dist/umd/data-structure-typed.min.js +3 -3
  39. package/dist/umd/data-structure-typed.min.js.map +1 -1
  40. package/package.json +3 -2
  41. package/src/data-structures/binary-tree/avl-tree.ts +96 -2
  42. package/src/data-structures/binary-tree/binary-tree.ts +117 -7
  43. package/src/data-structures/binary-tree/bst.ts +322 -13
  44. package/src/data-structures/binary-tree/red-black-tree.ts +84 -35
  45. package/src/data-structures/binary-tree/tree-multi-map.ts +2 -2
  46. package/src/data-structures/graph/directed-graph.ts +126 -1
  47. package/src/data-structures/graph/undirected-graph.ts +160 -1
  48. package/src/data-structures/hash/hash-map.ts +110 -27
  49. package/src/data-structures/heap/heap.ts +107 -58
  50. package/src/data-structures/linked-list/doubly-linked-list.ts +72 -404
  51. package/src/data-structures/linked-list/singly-linked-list.ts +121 -5
  52. package/src/data-structures/queue/deque.ts +95 -67
  53. package/src/data-structures/queue/queue.ts +90 -34
  54. package/src/data-structures/stack/stack.ts +58 -40
  55. package/src/data-structures/trie/trie.ts +109 -47
  56. package/src/interfaces/binary-tree.ts +2 -0
  57. package/test/performance/benchmark-runner.ts +14 -11
  58. package/test/performance/data-structures/binary-tree/avl-tree.test.ts +8 -8
  59. package/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +8 -8
  60. package/test/performance/data-structures/binary-tree/binary-tree.test.ts +6 -6
  61. package/test/performance/data-structures/binary-tree/bst.test.ts +5 -5
  62. package/test/performance/data-structures/binary-tree/red-black-tree.test.ts +10 -10
  63. package/test/performance/reportor.ts +2 -1
  64. package/test/performance/single-suite-runner.ts +7 -4
  65. package/test/unit/data-structures/binary-tree/avl-tree.test.ts +117 -0
  66. package/test/unit/data-structures/binary-tree/binary-tree.test.ts +166 -0
  67. package/test/unit/data-structures/binary-tree/bst.test.ts +766 -8
  68. package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +89 -37
  69. package/test/unit/data-structures/graph/directed-graph.test.ts +133 -0
  70. package/test/unit/data-structures/graph/undirected-graph.test.ts +167 -0
  71. package/test/unit/data-structures/hash/hash-map.test.ts +149 -3
  72. package/test/unit/data-structures/heap/heap.test.ts +182 -47
  73. package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +118 -14
  74. package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +121 -0
  75. package/test/unit/data-structures/queue/deque.test.ts +98 -67
  76. package/test/unit/data-structures/queue/queue.test.ts +85 -51
  77. package/test/unit/data-structures/stack/stack.test.ts +142 -33
  78. package/test/unit/data-structures/trie/trie.test.ts +135 -39
  79. package/tsup.leetcode.config.js +99 -0
  80. package/typedoc.json +2 -1
  81. package/POSTS_zh-CN.md +0 -54
  82. package/README_zh-CN.md +0 -1208
  83. 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
- * // Autocomplete: Prefix validation and checking
116
- * const autocomplete = new Trie<string>(['gmail.com', 'gmail.co.nz', 'gmail.co.jp', 'yahoo.com', 'outlook.com']);
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
- * // Get all completions for a prefix
119
- * const gmailCompletions = autocomplete.getWords('gmail');
120
- * console.log(gmailCompletions); // ['gmail.com', 'gmail.co.nz', 'gmail.co.jp']
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
- * // File System Path Operations
123
- * const fileSystem = new Trie<string>([
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
- * // Find common directory prefix
132
- * console.log(fileSystem.getLongestCommonPrefix()); // '/home/user/'
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
- * // List all files in a directory
135
- * const documentsFiles = fileSystem.getWords('/home/user/documents/');
136
- * console.log(documentsFiles); // ['/home/user/documents/file1.txt', '/home/user/documents/file2.txt']
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
- * // Autocomplete: Basic word suggestions
139
- * // Create a trie for autocomplete
140
- * const autocomplete = new Trie<string>([
141
- * 'function',
142
- * 'functional',
143
- * 'functions',
144
- * 'class',
145
- * 'classes',
146
- * 'classical',
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
- * // Test autocomplete with different prefixes
153
- * console.log(autocomplete.getWords('fun')); // ['functional', 'functions', 'function']
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
- * // Test with non-matching prefix
158
- * console.log(autocomplete.getWords('xyz')); // []
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
- * // Create a case-insensitive dictionary
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
- * // Add IP address prefixes and their corresponding routes
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<
@@ -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
- replaceMarkdownContent(
314
- '[//]: # (No deletion!!! Start of Replace Section)',
315
- '[//]: # (No deletion!!! End of Replace Section)',
316
- htmlTables
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, 'README.md');
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 README injection.');
335
+ console.warn('Markers not found or invalid range; skip PERFORMANCE injection.');
334
336
  return;
335
337
  }
336
- const updatedMarkdown = data.slice(0, startIndex + startMarker.length) + '\n' + newText + data.slice(endIndex);
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()} add randomly`, () => {
11
+ .add(`${HUNDRED_THOUSAND.toLocaleString()} set randomly`, () => {
12
12
  avlTree.clear();
13
- for (let i = 0; i < randomArray.length; i++) avlTree.add(randomArray[i]);
13
+ for (let i = 0; i < randomArray.length; i++) avlTree.set(randomArray[i]);
14
14
  })
15
- .add(`${HUNDRED_THOUSAND.toLocaleString()} add`, () => {
15
+ .add(`${HUNDRED_THOUSAND.toLocaleString()} set`, () => {
16
16
  avlTree.clear();
17
- for (let i = 0; i < randomArray.length; i++) avlTree.add(i);
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()} add & delete orderly`, () => {
29
+ .add(`${HUNDRED_THOUSAND.toLocaleString()} set & delete orderly`, () => {
30
30
  avlTree.clear();
31
- for (let i = 0; i < randomArray.length; i++) avlTree.add(i);
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()} add & delete randomly`, () => {
34
+ .add(`${HUNDRED_THOUSAND.toLocaleString()} set & delete randomly`, () => {
35
35
  avlTree.clear();
36
- for (let i = 0; i < randomArray.length; i++) avlTree.add(randomArray[i]);
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 add randomly`, () => {
12
+ .add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree set randomly`, () => {
13
13
  rbTree.clear();
14
- for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]);
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 add & delete randomly`, () => {
19
+ .add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree set & delete randomly`, () => {
20
20
  rbTree.clear();
21
- for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]);
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 add randomly`, () => {
24
+ .add(`${TEN_THOUSAND.toLocaleString()} AVLTree set randomly`, () => {
25
25
  avlTree.clear();
26
- for (let i = 0; i < arr.length; i++) avlTree.add(arr[i]);
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 add & delete randomly`, () => {
31
+ .add(`${TEN_THOUSAND.toLocaleString()} AVLTree set & delete randomly`, () => {
32
32
  avlTree.clear();
33
- for (let i = 0; i < arr.length; i++) avlTree.add(arr[i]);
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()} add randomly`, () => {
11
+ .add(`${THOUSAND.toLocaleString()} set randomly`, () => {
12
12
  biTree.clear();
13
- for (let i = 0; i < arr.length; i++) biTree.add(arr[i]);
13
+ for (let i = 0; i < arr.length; i++) biTree.set(arr[i]);
14
14
  })
15
- .add(`${THOUSAND.toLocaleString()} add & delete randomly`, () => {
15
+ .add(`${THOUSAND.toLocaleString()} set & delete randomly`, () => {
16
16
  biTree.clear();
17
- for (let i = 0; i < arr.length; i++) biTree.add(arr[i]);
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()} addMany`, () => {
20
+ .add(`${THOUSAND.toLocaleString()} setMany`, () => {
21
21
  biTree.clear();
22
- biTree.addMany(arr);
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()} add randomly`, () => {
11
+ .add(`${TEN_THOUSAND.toLocaleString()} set randomly`, () => {
12
12
  bst.clear();
13
- for (let i = 0; i < arr.length; i++) bst.add(arr[i]);
13
+ for (let i = 0; i < arr.length; i++) bst.set(arr[i]);
14
14
  })
15
- .add(`${TEN_THOUSAND.toLocaleString()} add & delete randomly`, () => {
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()} addMany`, () => {
20
+ .add(`${TEN_THOUSAND.toLocaleString()} setMany`, () => {
21
21
  bst.clear();
22
- bst.addMany(arr);
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()} add randomly`, () => {
15
+ // .add(`${MILLION.toLocaleString()} set randomly`, () => {
16
16
  // rbTree.clear();
17
- // for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]);
17
+ // for (let i = 0; i < randomArray.length; i++) rbTree.set(randomArray[i]);
18
18
  // })
19
- .add(`${MILLION.toLocaleString()} add`, () => {
19
+ .add(`${MILLION.toLocaleString()} set`, () => {
20
20
  rbTree.clear();
21
- for (let i = 0; i < randomArray.length; i++) rbTree.add(i);
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()} add & delete orderly`, () => {
40
+ // .add(`${MILLION.toLocaleString()} set & delete orderly`, () => {
41
41
  // rbTree.clear();
42
- // for (let i = 0; i < randomArray.length; i++) rbTree.add(i);
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()} add & delete randomly`, () => {
45
+ // .add(`${MILLION.toLocaleString()} set & delete randomly`, () => {
46
46
  // rbTree.clear();
47
- // for (let i = 0; i < randomArray.length; i++) rbTree.add(randomArray[i]);
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(`CPT ${MILLION.toLocaleString()} 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(`CPT ${MILLION.toLocaleString()} 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, 'README.md'); // Path to README.md file
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
- 'test name': benchmark.name,
56
- 'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
57
- 'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
58
- 'sample deviation': numberFix(benchmark.stats.deviation, 2)
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));
@@ -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
  });