Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 965fbd1

Browse files
Merge pull request #43 from amejiarosario/feature/trie
feat(trie): implement trie data structure
2 parents 567c110 + e31cc62 commit 965fbd1

File tree

5 files changed

+579
-0
lines changed

5 files changed

+579
-0
lines changed

‎src/data-structures/trees/trie-1.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
class Trie {
2+
constructor(val) {
3+
this.val = val;
4+
this.children = {};
5+
this.isWord = false;
6+
}
7+
8+
/**
9+
* Insert word into trie and mark last element as such.
10+
* @param {string} word
11+
* @return {undefined}
12+
*/
13+
insert(word) {
14+
let curr = this;
15+
16+
for (const char of word) {
17+
curr.children[char] = curr.children[char] || new Trie(char);
18+
curr = curr.children[char];
19+
}
20+
21+
curr.isWord = true;
22+
}
23+
24+
/**
25+
* Search for complete word (by default) or partial if flag is set.
26+
* @param {string} word - Word to search.
27+
* @param {boolean} options.partial - Whether or not match partial matches.
28+
* @return {boolean}
29+
*/
30+
search(word, { partial } = {}) {
31+
let curr = this;
32+
33+
for (const char of word) {
34+
if (!curr.children[char]) { return false; }
35+
curr = curr.children[char];
36+
}
37+
38+
return partial ? true : curr.isWord;
39+
}
40+
41+
/**
42+
* Return true if any word on the trie starts with the given prefix
43+
* @param {string} prefix - Partial word to search.
44+
* @return {boolean}
45+
*/
46+
startsWith(prefix) {
47+
return this.search(prefix, { partial: true });
48+
}
49+
50+
/**
51+
* Returns all the words from the current `node`.
52+
* Uses backtracking.
53+
*
54+
* @param {string} prefix - The prefix to append to each word.
55+
* @param {string} node - Current node to start backtracking.
56+
* @param {string[]} words - Accumulated words.
57+
* @param {string} string - Current string.
58+
*/
59+
getAllWords(prefix = '', node = this, words = [], string = '') {
60+
if (node.isWord) {
61+
words.push(`${prefix}${string}`);
62+
}
63+
64+
for (const char of Object.keys(node.children)) {
65+
this.getAllWords(prefix, node.children[char], words, `${string}${char}`);
66+
}
67+
68+
return words;
69+
}
70+
71+
/**
72+
* Return true if found the word to be removed, otherwise false.
73+
* Iterative approach
74+
* @param {string} word - The word to remove
75+
* @returns {boolean}
76+
*/
77+
remove(word) {
78+
const stack = [];
79+
let curr = this;
80+
81+
for (const char of word) {
82+
if (!curr.children[char]) { return false; }
83+
stack.push(curr);
84+
curr = curr.children[char];
85+
}
86+
87+
if (!curr.isWord) { return false; }
88+
let node = stack.pop();
89+
90+
do {
91+
node.children = {};
92+
node = stack.pop();
93+
} while (node && !node.isWord);
94+
95+
return true;
96+
}
97+
98+
/**
99+
* Return true if found the word to be removed, otherwise false.
100+
* recursive approach
101+
* @param {string} word - The word to remove
102+
* @returns {boolean}
103+
*/
104+
remove2(word, i = 0, parent = this) {
105+
if (i === word.length - 1) {
106+
return true;
107+
}
108+
const child = parent.children[word.charAt(i)];
109+
if (!child) return false;
110+
111+
const found = this.remove(word, i + 1, child);
112+
113+
if (found) {
114+
delete parent.children[word.charAt(i)];
115+
}
116+
return true;
117+
}
118+
}
119+
120+
// Aliases
121+
Trie.prototype.add = Trie.prototype.insert;
122+
123+
module.exports = Trie;

‎src/data-structures/trees/trie-2.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
class Trie {
2+
constructor(val) {
3+
this.val = val;
4+
this.children = {};
5+
this.isWord = false;
6+
}
7+
8+
/**
9+
* Insert word into trie and mark last element as such.
10+
* @param {string} word
11+
* @return {undefined}
12+
*/
13+
insert(word) {
14+
let curr = this;
15+
16+
for (const char of word) {
17+
curr.children[char] = curr.children[char] || new Trie(char);
18+
curr = curr.children[char];
19+
}
20+
21+
curr.isWord = true;
22+
}
23+
24+
/**
25+
* Return true if found the word to be removed, otherwise false.
26+
* @param {string} word - The word to remove
27+
* @returns {boolean}
28+
*/
29+
remove(word) {
30+
return this.removeHelper(word);
31+
}
32+
33+
/**
34+
* Remove word from trie, return true if found, otherwise false.
35+
* @param {string} word - The word to remove.
36+
* @param {Trie} parent - The parent node.
37+
* @param {number} index - The index.
38+
* @param {number} meta.stop - Keeps track of the last letter that won't be removed.
39+
* @returns {boolean}
40+
*/
41+
removeHelper(word, parent = this, index = 0, meta = { stop: 0 }) {
42+
if (index === word.length) {
43+
parent.isWord = false;
44+
if (Object.keys(parent.children)) { meta.stop = index; }
45+
return true;
46+
}
47+
const child = parent.children[word.charAt(index)];
48+
if (!child) { return false; }
49+
if (parent.isWord) { meta.stop = index; }
50+
const found = this.removeHelper(word, child, index + 1, meta);
51+
// deletes all the nodes beyond `meta.stop`.
52+
if (found && index >= meta.stop) {
53+
delete parent.children[word.charAt(index)];
54+
}
55+
return found;
56+
}
57+
58+
/**
59+
* Retun last node that matches word or prefix or false if not found.
60+
* @param {string} word - Word to search.
61+
* @param {boolean} options.partial - Whether or not match partial matches.
62+
* @return {Trie|false}
63+
*/
64+
searchNode(word) {
65+
let curr = this;
66+
67+
for (const char of word) {
68+
if (!curr.children[char]) { return false; }
69+
curr = curr.children[char];
70+
}
71+
72+
return curr;
73+
}
74+
75+
/**
76+
* Search for complete word (by default) or partial if flag is set.
77+
* @param {string} word - Word to search.
78+
* @param {boolean} options.partial - Whether or not match partial matches.
79+
* @return {boolean}
80+
*/
81+
search(word, { partial } = {}) {
82+
const curr = this.searchNode(word);
83+
if (!curr) { return false; }
84+
return partial ? true : curr.isWord;
85+
}
86+
87+
/**
88+
* Return true if any word on the trie starts with the given prefix
89+
* @param {string} prefix - Partial word to search.
90+
* @return {boolean}
91+
*/
92+
startsWith(prefix) {
93+
return this.search(prefix, { partial: true });
94+
}
95+
96+
/**
97+
* Returns all the words from the current `node`.
98+
* Uses backtracking.
99+
*
100+
* @param {string} prefix - The prefix to append to each word.
101+
* @param {string} node - Current node to start backtracking.
102+
*/
103+
getAllWords(prefix = '', node = this) {
104+
let words = [];
105+
106+
if (!node) { return words; }
107+
if (node.isWord) {
108+
words.push(prefix);
109+
}
110+
111+
for (const char of Object.keys(node.children)) {
112+
const newWords = this.getAllWords(`${prefix}${char}`, node.children[char]);
113+
words = words.concat(newWords);
114+
}
115+
116+
return words;
117+
}
118+
119+
/**
120+
* Return a list of words matching the prefix
121+
* @param {*} prefix - The prefix to match.
122+
* @returns {string[]}
123+
*/
124+
autocomplete(prefix = '') {
125+
const curr = this.searchNode(prefix);
126+
return this.getAllWords(prefix, curr);
127+
}
128+
}
129+
130+
// Aliases
131+
Trie.prototype.add = Trie.prototype.insert;
132+
133+
module.exports = Trie;

‎src/data-structures/trees/trie.js

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
class Trie {
2+
constructor(val) {
3+
this.val = val;
4+
this.children = {};
5+
this.isWord = false;
6+
}
7+
8+
/**
9+
* Insert word into trie and mark last element as such.
10+
* @param {string} word
11+
* @return {undefined}
12+
*/
13+
insert(word) {
14+
let curr = this;
15+
16+
for (const char of word) {
17+
curr.children[char] = curr.children[char] || new Trie(char);
18+
curr = curr.children[char];
19+
}
20+
21+
curr.isWord = true;
22+
}
23+
24+
/**
25+
* Return true if found the word to be removed, otherwise false.
26+
* @param {string} word - The word to remove
27+
* @returns {boolean}
28+
*/
29+
remove(word) {
30+
let curr = this;
31+
// let lastWordToKeep = 0;
32+
const stack = [curr];
33+
34+
// find word and stack path
35+
for (const char of word) {
36+
if (!curr.children[char]) { return false; }
37+
// lastWordToKeep += 1;
38+
curr = curr.children[char];
39+
stack.push(curr);
40+
}
41+
42+
let child = stack.pop();
43+
child.isWord = false;
44+
45+
// remove non words without children
46+
while (stack.length) {
47+
const parent = stack.pop();
48+
if (!child.isWord && !Object.keys(child.children).length) {
49+
delete parent.children[child.val];
50+
}
51+
child = parent;
52+
}
53+
54+
return true;
55+
}
56+
57+
/**
58+
* Retun last node that matches word or prefix or false if not found.
59+
* @param {string} word - Word to search.
60+
* @param {boolean} options.partial - Whether or not match partial matches.
61+
* @return {Trie|false}
62+
*/
63+
searchNode(word) {
64+
let curr = this;
65+
66+
for (const char of word) {
67+
if (!curr.children[char]) { return false; }
68+
curr = curr.children[char];
69+
}
70+
71+
return curr;
72+
}
73+
74+
/**
75+
* Search for complete word (by default) or partial if flag is set.
76+
* @param {string} word - Word to search.
77+
* @param {boolean} options.partial - Whether or not match partial matches.
78+
* @return {boolean}
79+
*/
80+
search(word, { partial } = {}) {
81+
const curr = this.searchNode(word);
82+
if (!curr) { return false; }
83+
return partial ? true : curr.isWord;
84+
}
85+
86+
/**
87+
* Return true if any word on the trie starts with the given prefix
88+
* @param {string} prefix - Partial word to search.
89+
* @return {boolean}
90+
*/
91+
startsWith(prefix) {
92+
return this.search(prefix, { partial: true });
93+
}
94+
95+
/**
96+
* Returns all the words from the current `node`.
97+
* Uses backtracking.
98+
*
99+
* @param {string} prefix - The prefix to append to each word.
100+
* @param {string} node - Current node to start backtracking.
101+
*/
102+
getAllWords(prefix = '', node = this) {
103+
let words = [];
104+
105+
if (!node) { return words; }
106+
if (node.isWord) {
107+
words.push(prefix);
108+
}
109+
110+
for (const char of Object.keys(node.children)) {
111+
const newWords = this.getAllWords(`${prefix}${char}`, node.children[char]);
112+
words = words.concat(newWords);
113+
}
114+
115+
return words;
116+
}
117+
118+
/**
119+
* Return a list of words matching the prefix
120+
* @param {*} prefix - The prefix to match.
121+
* @returns {string[]}
122+
*/
123+
autocomplete(prefix = '') {
124+
const curr = this.searchNode(prefix);
125+
return this.getAllWords(prefix, curr);
126+
}
127+
}
128+
129+
// Aliases
130+
Trie.prototype.add = Trie.prototype.insert;
131+
132+
module.exports = Trie;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /