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 e31cc62

Browse files
feat(trie): feature complete
1 parent a81f6e1 commit e31cc62

File tree

4 files changed

+159
-24
lines changed

4 files changed

+159
-24
lines changed

‎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: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,31 @@ class Trie {
2727
* @returns {boolean}
2828
*/
2929
remove(word) {
30-
return this.removeHelper(word);
31-
}
30+
let curr = this;
31+
// let lastWordToKeep = 0;
32+
const stack = [curr];
3233

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;
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);
4640
}
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)];
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;
5452
}
55-
return found;
53+
54+
return true;
5655
}
5756

5857
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe('Trie', () => {
137137
});
138138
});
139139

140-
describe('remove', () => {
140+
fdescribe('remove', () => {
141141
it('should remove a word', () => {
142142
trie = new Trie();
143143
trie.insert('a');

‎src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const BinaryTreeNode = require('./data-structures/trees/binary-tree-node');
1414
const AvlTree = require('./data-structures/trees/avl-tree');
1515
const RedBlackTree = require('./data-structures/trees/red-black-tree');
1616
const LRUCache = require('./data-structures/custom/lru-cache');
17+
const Trie = require('./data-structures/trees/trie');
18+
1719
// algorithms
1820
const bubbleSort = require('./algorithms/sorting/bubble-sort');
1921
const insertionSort = require('./algorithms/sorting/insertion-sort');
@@ -37,6 +39,7 @@ module.exports = {
3739
AvlTree,
3840
RedBlackTree,
3941
LRUCache,
42+
Trie,
4043
bubbleSort,
4144
insertionSort,
4245
selectionSort,

0 commit comments

Comments
(0)

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