data-structure-typed 1.36.3 → 1.36.5

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.
@@ -5,21 +5,26 @@
5
5
  * @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
+
9
+ /**
10
+ * TrieNode represents a node in the Trie data structure. It holds a character key, a map of children nodes,
11
+ * and a flag indicating whether it's the end of a word.
12
+ */
8
13
  export class TrieNode {
9
- constructor(v: string) {
10
- this._val = v;
14
+ constructor(key: string) {
15
+ this._key = key;
11
16
  this._isEnd = false;
12
17
  this._children = new Map<string, TrieNode>();
13
18
  }
14
19
 
15
- private _val;
20
+ private _key;
16
21
 
17
- get val(): string {
18
- return this._val;
22
+ get key(): string {
23
+ return this._key;
19
24
  }
20
25
 
21
- set val(v: string) {
22
- this._val = v;
26
+ set key(v: string) {
27
+ this._key = v;
23
28
  }
24
29
 
25
30
  protected _children: Map<string, TrieNode>;
@@ -43,9 +48,13 @@ export class TrieNode {
43
48
  }
44
49
  }
45
50
 
51
+ /**
52
+ * Trie represents a Trie data structure. It provides basic Trie operations and additional methods.
53
+ */
46
54
  export class Trie {
47
- constructor(words?: string[]) {
55
+ constructor(words?: string[], caseSensitive = true) {
48
56
  this._root = new TrieNode('');
57
+ this._caseSensitive = caseSensitive;
49
58
  if (words) {
50
59
  for (const i of words) {
51
60
  this.add(i);
@@ -63,8 +72,16 @@ export class Trie {
63
72
  this._root = v;
64
73
  }
65
74
 
75
+ private readonly _caseSensitive: boolean;
76
+
77
+ /**
78
+ * Add a word to the Trie structure.
79
+ * @param {string} word - The word to add.
80
+ * @returns {boolean} True if the word was successfully added.
81
+ */
66
82
  add(word: string): boolean {
67
- let cur = this._root;
83
+ word = this._caseProcess(word);
84
+ let cur = this.root;
68
85
  for (const c of word) {
69
86
  let nodeC = cur.children.get(c);
70
87
  if (!nodeC) {
@@ -77,9 +94,15 @@ export class Trie {
77
94
  return true;
78
95
  }
79
96
 
80
- has(input: string): boolean {
81
- let cur = this._root;
82
- for (const c of input) {
97
+ /**
98
+ * Check if the Trie contains a given word.
99
+ * @param {string} word - The word to check for.
100
+ * @returns {boolean} True if the word is present in the Trie.
101
+ */
102
+ has(word: string): boolean {
103
+ word = this._caseProcess(word);
104
+ let cur = this.root;
105
+ for (const c of word) {
83
106
  const nodeC = cur.children.get(c);
84
107
  if (!nodeC) return false;
85
108
  cur = nodeC;
@@ -87,7 +110,20 @@ export class Trie {
87
110
  return cur.isEnd;
88
111
  }
89
112
 
113
+ private _caseProcess(str: string) {
114
+ if (!this._caseSensitive) {
115
+ str = str.toLowerCase(); // Convert str to lowercase if case-insensitive
116
+ }
117
+ return str;
118
+ }
119
+
120
+ /**
121
+ * Remove a word from the Trie structure.
122
+ * @param{string} word - The word to remove.
123
+ * @returns {boolean} True if the word was successfully removed.
124
+ */
90
125
  remove(word: string) {
126
+ word = this._caseProcess(word);
91
127
  let isDeleted = false;
92
128
  const dfs = (cur: TrieNode, i: number): boolean => {
93
129
  const char = word[i];
@@ -119,14 +155,35 @@ export class Trie {
119
155
  return isDeleted;
120
156
  }
121
157
 
158
+ getHeight() {
159
+ const beginRoot = this.root;
160
+ let maxDepth = 0;
161
+ if (beginRoot) {
162
+ const bfs = (node: TrieNode, level: number) => {
163
+ if (level > maxDepth) {
164
+ maxDepth = level;
165
+ }
166
+ const {children} = node;
167
+ if (children) {
168
+ for (const child of children.entries()) {
169
+ bfs(child[1], level + 1);
170
+ }
171
+ }
172
+ };
173
+ bfs(beginRoot, 0);
174
+ }
175
+ return maxDepth;
176
+ }
177
+
122
178
  // --- start additional methods ---
123
179
  /**
124
- * The function checks if a given input string has an absolute prefix in a tree data structure.Only can present as a prefix, not a word
125
- * @param {string} input - The input parameter is a string that represents the input value for the function.
126
- * @returns a boolean value.
180
+ * Check if a given input string has an absolute prefix in the Trie, meaning it's not a complete word.
181
+ * @param {string} input - The input string to check.
182
+ * @returns {boolean} True if it's an absolute prefix in the Trie.
127
183
  */
128
- isAbsPrefix(input: string): boolean {
129
- let cur = this._root;
184
+ hasPurePrefix(input: string): boolean {
185
+ input = this._caseProcess(input);
186
+ let cur = this.root;
130
187
  for (const c of input) {
131
188
  const nodeC = cur.children.get(c);
132
189
  if (!nodeC) return false;
@@ -136,12 +193,13 @@ export class Trie {
136
193
  }
137
194
 
138
195
  /**
139
- * The function checks if a given input string is a prefix of any existing string in a tree structure.Can present as a abs prefix or word
140
- * @param {string} input - The input parameter is a string that represents the prefix we want to check.
141
- * @returns a boolean value.
196
+ * Check if a given input string is a prefix of any existing word in the Trie, whether as an absolute prefix or a complete word.
197
+ * @param {string} input - The input string representing the prefix to check.
198
+ * @returns {boolean} True if it's a prefix in the Trie.
142
199
  */
143
- isPrefix(input: string): boolean {
144
- let cur = this._root;
200
+ hasPrefix(input: string): boolean {
201
+ input = this._caseProcess(input);
202
+ let cur = this.root;
145
203
  for (const c of input) {
146
204
  const nodeC = cur.children.get(c);
147
205
  if (!nodeC) return false;
@@ -151,50 +209,51 @@ export class Trie {
151
209
  }
152
210
 
153
211
  /**
154
- * The function checks if the input string is a common prefix in a Trie data structure.Check if the input string is the common prefix of all the words
155
- * @param {string} input - The input parameter is a string that represents the common prefix that we want to check for
156
- * in the Trie data structure.
157
- * @returns a boolean value indicating whether the input string is a common prefix in the Trie data structure.
212
+ * Check if the input string is a common prefix in the Trie, meaning it's a prefix shared by all words in the Trie.
213
+ * @param {string} input - The input string representing the common prefix to check for.
214
+ * @returns {boolean} True if it's a common prefix in the Trie.
158
215
  */
159
- isCommonPrefix(input: string): boolean {
216
+ hasCommonPrefix(input: string): boolean {
217
+ input = this._caseProcess(input);
160
218
  let commonPre = '';
161
219
  const dfs = (cur: TrieNode) => {
162
- commonPre += cur.val;
220
+ commonPre += cur.key;
163
221
  if (commonPre === input) return;
164
222
  if (cur.isEnd) return;
165
223
  if (cur && cur.children && cur.children.size === 1) dfs(Array.from(cur.children.values())[0]);
166
224
  else return;
167
225
  };
168
- dfs(this._root);
226
+ dfs(this.root);
169
227
  return commonPre === input;
170
228
  }
171
229
 
172
230
  /**
173
- * The function `getLongestCommonPrefix` returns the longest common prefix among all the words stored in a Trie data
174
- * structure.
175
- * @returns The function `getLongestCommonPrefix` returns a string, which is the longest common prefix found in the
176
- * Trie.
231
+ * Get the longest common prefix among all the words stored in the Trie.
232
+ * @returns {string} The longest common prefix found in the Trie.
177
233
  */
178
234
  getLongestCommonPrefix(): string {
179
235
  let commonPre = '';
180
236
  const dfs = (cur: TrieNode) => {
181
- commonPre += cur.val;
237
+ commonPre += cur.key;
182
238
  if (cur.isEnd) return;
183
239
  if (cur && cur.children && cur.children.size === 1) dfs(Array.from(cur.children.values())[0]);
184
240
  else return;
185
241
  };
186
- dfs(this._root);
242
+ dfs(this.root);
187
243
  return commonPre;
188
244
  }
189
245
 
190
246
  /**
191
247
  * The `getAll` function returns an array of all words in a Trie data structure that start with a given prefix.
192
- * @param [prefix] - The `prefix` parameter is a string that represents the prefix that we want to search for in the
248
+ * @param {string} prefix - The `prefix` parameter is a string that represents the prefix that we want to search for in the
193
249
  * trie. It is an optional parameter, so if no prefix is provided, it will default to an empty string.
194
- * @returns an array of strings.
250
+ * @param {number} max - The max count of words will be found
251
+ * @returns {string[]} an array of strings.
195
252
  */
196
- getAll(prefix = ''): string[] {
253
+ getWords(prefix = '', max = Number.MAX_SAFE_INTEGER): string[] {
254
+ prefix = this._caseProcess(prefix);
197
255
  const words: string[] = [];
256
+ let found = 0;
198
257
 
199
258
  function dfs(node: TrieNode, word: string) {
200
259
  for (const char of node.children.keys()) {
@@ -204,11 +263,13 @@ export class Trie {
204
263
  }
205
264
  }
206
265
  if (node.isEnd) {
266
+ if (found > max - 1) return;
207
267
  words.push(word);
268
+ found++;
208
269
  }
209
270
  }
210
271
 
211
- let startNode = this._root;
272
+ let startNode = this.root;
212
273
 
213
274
  if (prefix) {
214
275
  for (const c of prefix) {
@@ -216,8 +277,8 @@ export class Trie {
216
277
  if (nodeC) startNode = nodeC;
217
278
  }
218
279
  }
280
+ if (startNode !== this.root) dfs(startNode, prefix);
219
281
 
220
- dfs(startNode, prefix);
221
282
  return words;
222
283
  }
223
284
 
@@ -105,6 +105,19 @@ describe('BinaryTree', () => {
105
105
  expect(binaryTree.has(4)).toBe(false);
106
106
  });
107
107
 
108
+ test('should getDepth return correct depth', () => {
109
+ binaryTree.add(1);
110
+ expect(binaryTree.getDepth(1)).toBe(0);
111
+ binaryTree.add(2);
112
+ expect(binaryTree.getDepth(2)).toBe(1);
113
+ binaryTree.add(3);
114
+ expect(binaryTree.getDepth(3, 1)).toBe(1);
115
+ binaryTree.add(4);
116
+ expect(binaryTree.getDepth(4, 1)).toBe(2);
117
+ expect(binaryTree.getDepth(4)).toBe(2);
118
+ expect(binaryTree.getDepth(4, 2)).toBe(1);
119
+ });
120
+
108
121
  test('should traverse in-order', () => {
109
122
  binaryTree.add(4);
110
123
  binaryTree.add(2);
@@ -28,7 +28,6 @@ describe('Heap Operation Test', () => {
28
28
  minHeap.add({key: 0, a: 'a0'});
29
29
 
30
30
  expect(minHeap.peek()).toEqual({a: 'a0', key: 0});
31
- console.log('---', minHeap.toArray());
32
31
  expect(minHeap.toArray().map(item => ({a: item.a}))).toEqual([{a: 'a0'}, {a: 'a1'}, {a: 'a2'}, {a: 'a6'}]);
33
32
  let i = 0;
34
33
  const expectPolled = [{a: 'a0'}, {a: 'a1'}, {a: 'a2'}, {a: 'a6'}];
@@ -29,11 +29,11 @@ describe('TreeNode', () => {
29
29
  child1.addChildren([grandchild1]);
30
30
  child2.addChildren([grandchild2]);
31
31
 
32
- expect(rootNode.getHeight()).toBe(3); // Height of the tree should be 3
32
+ expect(rootNode.getHeight()).toBe(2); // Height of the tree should be 2
33
33
  });
34
34
 
35
35
  it('should handle nodes without children when calculating height', () => {
36
36
  const rootNode = new TreeNode<string>('1', 'Root Node');
37
- expect(rootNode.getHeight()).toBe(1); // Height of a single node should be 1
37
+ expect(rootNode.getHeight()).toBe(0); // Height of a single node should be 0
38
38
  });
39
39
  });