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.
- package/.github/workflows/ci.yml +2 -20
- package/.travis.yml +3 -4
- package/CHANGELOG.md +6 -1
- package/dist/data-structures/binary-tree/binary-tree.d.ts +3 -2
- package/dist/data-structures/binary-tree/binary-tree.js +10 -4
- package/dist/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/data-structures/tree/tree.js +3 -5
- package/dist/data-structures/tree/tree.js.map +1 -1
- package/dist/data-structures/trie/trie.d.ts +49 -26
- package/dist/data-structures/trie/trie.js +99 -41
- package/dist/data-structures/trie/trie.js.map +1 -1
- package/lib/data-structures/binary-tree/binary-tree.d.ts +3 -2
- package/lib/data-structures/binary-tree/binary-tree.js +10 -4
- package/lib/data-structures/tree/tree.js +3 -5
- package/lib/data-structures/trie/trie.d.ts +49 -26
- package/lib/data-structures/trie/trie.js +97 -39
- package/package.json +5 -5
- package/src/data-structures/binary-tree/binary-tree.ts +9 -5
- package/src/data-structures/tree/tree.ts +3 -5
- package/src/data-structures/trie/trie.ts +101 -40
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +13 -0
- package/test/unit/data-structures/heap/heap.test.ts +0 -1
- package/test/unit/data-structures/tree/tree.test.ts +2 -2
- package/test/unit/data-structures/trie/trie.test.ts +740 -10
- package/umd/bundle.min.js +1 -1
- package/umd/bundle.min.js.map +1 -1
|
@@ -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(
|
|
10
|
-
this.
|
|
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
|
|
20
|
+
private _key;
|
|
16
21
|
|
|
17
|
-
get
|
|
18
|
-
return this.
|
|
22
|
+
get key(): string {
|
|
23
|
+
return this._key;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
set
|
|
22
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
*
|
|
125
|
-
* @param {string} input - The input
|
|
126
|
-
* @returns
|
|
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
|
-
|
|
129
|
-
|
|
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
|
-
*
|
|
140
|
-
* @param {string} input - The input
|
|
141
|
-
* @returns a
|
|
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
|
-
|
|
144
|
-
|
|
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
|
-
*
|
|
155
|
-
* @param {string} input - The input
|
|
156
|
-
* in the Trie
|
|
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
|
-
|
|
216
|
+
hasCommonPrefix(input: string): boolean {
|
|
217
|
+
input = this._caseProcess(input);
|
|
160
218
|
let commonPre = '';
|
|
161
219
|
const dfs = (cur: TrieNode) => {
|
|
162
|
-
commonPre += cur.
|
|
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.
|
|
226
|
+
dfs(this.root);
|
|
169
227
|
return commonPre === input;
|
|
170
228
|
}
|
|
171
229
|
|
|
172
230
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
* @
|
|
250
|
+
* @param {number} max - The max count of words will be found
|
|
251
|
+
* @returns {string[]} an array of strings.
|
|
195
252
|
*/
|
|
196
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
37
|
+
expect(rootNode.getHeight()).toBe(0); // Height of a single node should be 0
|
|
38
38
|
});
|
|
39
39
|
});
|