Skip to content

Commit f029666

Browse files
committed
chapter 12: tries
1 parent bb8742d commit f029666

File tree

6 files changed

+382
-0
lines changed

6 files changed

+382
-0
lines changed

src/12-trie/01-spell-checker.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// src/12-trie/01-spell-checker.js
2+
3+
const Trie = require('./trie');
4+
5+
class SpellChecker {
6+
#trie = new Trie();
7+
8+
buildDictionary(words) {
9+
for (let word of words) {
10+
this.#trie.insert(word);
11+
}
12+
}
13+
14+
isWordInDictionary(word) {
15+
return this.#trie.search(word);
16+
}
17+
18+
getSuggestions(word) {
19+
const suggestions = [];
20+
const wordArray = word.split('');
21+
for (let i = 0; i < wordArray.length; i++) {
22+
const temp = wordArray[i];
23+
wordArray[i] = '';
24+
const newWord = wordArray.join('');
25+
if (this.#trie.startsWith(newWord)) {
26+
suggestions.push(newWord);
27+
}
28+
wordArray[i] = temp;
29+
}
30+
return suggestions;
31+
}
32+
33+
removeWord(word) {
34+
return this.#trie.remove(word);
35+
}
36+
}
37+
38+
const spellChecker = new SpellChecker();
39+
spellChecker.buildDictionary(['cat', 'bat', 'rat', 'drat', 'dart', 'date']);
40+
console.log(spellChecker.isWordInDictionary('bat')); // true
41+
console.log(spellChecker.isWordInDictionary('drate')); // false
42+
console.log(spellChecker.getSuggestions('drate')); // [ 'date', 'drat' ]
43+
44+
45+
const trie = new Trie();
46+
trie.insert('she');
47+
trie.insert('sells');
48+
trie.insert('sea');
49+
trie.insert('shells');
50+
trie.insert('by');
51+
trie.insert('the');
52+
53+
// to see the output of this file use the command: node src/12-trie/01-spell-checker.js
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// https://leetcode.com/problems/design-add-and-search-words-data-structure/description/
2+
// 211. Design Add and Search Words Data Structure
3+
4+
// @ts-ignore
5+
class TrieNode {
6+
children: Map<string, TrieNode>;
7+
isEndOfWord: boolean;
8+
9+
constructor() {
10+
this.children = new Map<string, TrieNode>();
11+
this.isEndOfWord = false;
12+
}
13+
}
14+
15+
class WordDictionary {
16+
private root: TrieNode;
17+
18+
constructor() {
19+
this.root = new TrieNode();
20+
}
21+
22+
addWord(word: string): void {
23+
let node = this.root;
24+
for (let char of word) {
25+
if (!node.children.has(char)) {
26+
node.children.set(char, new TrieNode());
27+
}
28+
node = node.children.get(char)!;
29+
}
30+
node.isEndOfWord = true;
31+
}
32+
33+
search(word: string): boolean {
34+
return this.searchFromNode(word, this.root);
35+
}
36+
37+
private searchFromNode(word: string, node: TrieNode): boolean {
38+
for (let i = 0; i < word.length; i++) {
39+
const char = word[i];
40+
if (char === '.') {
41+
for (let child of node.children.values()) {
42+
if (this.searchFromNode(word.slice(i + 1), child)) {
43+
return true;
44+
}
45+
}
46+
return false;
47+
} else if (!node.children.has(char)) {
48+
return false;
49+
}
50+
node = node.children.get(char)!;
51+
}
52+
return node.isEndOfWord;
53+
}
54+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// https://leetcode.com/problems/implement-trie-prefix-tree/
2+
// 208. Implement Trie (Prefix Tree)
3+
4+
5+
class TrieNode {
6+
children: Map<string, TrieNode>;
7+
isEndOfWord: boolean;
8+
9+
constructor() {
10+
this.children = new Map<string, TrieNode>();
11+
this.isEndOfWord = false;
12+
}
13+
}
14+
15+
class Trie {
16+
private root: TrieNode;
17+
18+
constructor() {
19+
this.root = new TrieNode();
20+
}
21+
22+
insert(word: string): void {
23+
let node = this.root;
24+
for (let char of word) {
25+
if (!node.children.has(char)) {
26+
node.children.set(char, new TrieNode());
27+
}
28+
node = node.children.get(char)!;
29+
}
30+
node.isEndOfWord = true;
31+
}
32+
33+
search(word: string): boolean {
34+
let node = this.root;
35+
for (let char of word) {
36+
if (!node.children.has(char)) {
37+
return false;
38+
}
39+
node = node.children.get(char)!;
40+
}
41+
return node.isEndOfWord;
42+
}
43+
44+
startsWith(prefix: string): boolean {
45+
let node = this.root;
46+
for (let char of prefix) {
47+
if (!node.children.has(char)) {
48+
return false;
49+
}
50+
node = node.children.get(char)!;
51+
}
52+
return true;
53+
}
54+
}
55+
56+
/**
57+
* Your Trie object will be instantiated and called as such:
58+
* var obj = new Trie()
59+
* obj.insert(word)
60+
* var param_2 = obj.search(word)
61+
* var param_3 = obj.startsWith(prefix)
62+
*/
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// https://leetcode.com/problems/longest-common-prefix/description/
2+
// 14. Longest Common Prefix
3+
4+
function longestCommonPrefix(strs: string[]): string {
5+
if (strs.length === 0) { return ''; }
6+
let prefix = strs[0];
7+
for (let i = 1; i < strs.length; i++) {
8+
while (strs[i].indexOf(prefix) !== 0) {
9+
prefix = prefix.substring(0, prefix.length - 1);
10+
}
11+
}
12+
return prefix;
13+
}
14+
15+
// @ts-ignore
16+
class TrieNode {
17+
children: Map<string, TrieNodeMap> = new Map();
18+
isEndOfWord: boolean = false;
19+
}
20+
21+
// @ts-ignore
22+
class Trie {
23+
private root: TrieNode;
24+
25+
constructor() {
26+
this.root = new TrieNode();
27+
}
28+
29+
insert(word: string): void {
30+
let node = this.root;
31+
for (let char of word) {
32+
if (!node.children.has(char)) {
33+
node.children.set(char, new TrieNode());
34+
}
35+
node = node.children.get(char);
36+
}
37+
node.isEndOfWord = true;
38+
}
39+
40+
findLongestCommonPrefix(): string {
41+
let node = this.root;
42+
let prefix = '';
43+
while (node.children.size === 1 && !node.isEndOfWord) {
44+
const char = Array.from(node.children.keys())[0];
45+
prefix += char;
46+
node = node.children.get(char);
47+
}
48+
return prefix;
49+
}
50+
}
51+
52+
function longestCommonPrefixTrie(strs: string[]): string {
53+
if (strs.length === 0) {
54+
return '';
55+
}
56+
57+
let trie = new Trie();
58+
for (let str of strs) {
59+
trie.insert(str);
60+
}
61+
62+
return trie.findLongestCommonPrefix();
63+
}
64+
65+
console.log(longestCommonPrefix(['flower', 'flow', 'flight'])); // 'fl'
66+
console.log(longestCommonPrefix(['dog', 'racecar', 'car'])); // ''

src/12-trie/trie.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
class TrieNode {
2+
constructor() {
3+
this.children = new Map();
4+
this.isEndOfWord = false;
5+
}
6+
}
7+
8+
class Trie {
9+
#root = new TrieNode();
10+
11+
insert(word) {
12+
let node = this.#root;
13+
for (let char of word) {
14+
if (!node.children.has(char)) {
15+
node.children.set(char, new TrieNode());
16+
}
17+
node = node.children.get(char);
18+
}
19+
node.isEndOfWord = true;
20+
}
21+
22+
search(word) {
23+
let node = this.#root;
24+
for (let char of word) {
25+
if (!node.children.has(char)) {
26+
return false;
27+
}
28+
node = node.children.get(char);
29+
}
30+
return node.isEndOfWord;
31+
}
32+
33+
startsWith(prefix) {
34+
let node = this.#root;
35+
for (let char of prefix) {
36+
if (!node.children.has(char)) {
37+
return false;
38+
}
39+
node = node.children.get(char);
40+
}
41+
return true;
42+
}
43+
44+
remove(word) {
45+
return this.#removeWord(this.#root, word, 0);
46+
}
47+
48+
#removeWord(node, word, index) {
49+
if (index === word.length) {
50+
if (!node.isEndOfWord) return false;
51+
node.isEndOfWord = false;
52+
return node.children.size === 0;
53+
}
54+
55+
const char = word[index];
56+
if (!node.children.has(char)) {
57+
return false;
58+
}
59+
60+
const shouldDeleteCurrentNode = this.#removeWord(node.children.get(char), word, index + 1);
61+
if (shouldDeleteCurrentNode) {
62+
node.children.delete(char);
63+
return node.children.size === 0;
64+
}
65+
66+
return false;
67+
}
68+
}
69+
70+
module.exports = Trie;

src/12-trie/trie.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
class TrieNode {
2+
children: Map<string, TrieNode>;
3+
isEndOfWord: boolean;
4+
5+
constructor() {
6+
this.children = new Map<string, TrieNode>();
7+
this.isEndOfWord = false;
8+
}
9+
}
10+
11+
class Trie {
12+
private root: TrieNode;
13+
14+
constructor() {
15+
this.root = new TrieNode();
16+
}
17+
18+
insert(word: string): void {
19+
let node = this.root;
20+
for (let char of word) {
21+
if (!node.children.has(char)) {
22+
node.children.set(char, new TrieNode());
23+
}
24+
node = node.children.get(char)!;
25+
}
26+
node.isEndOfWord = true;
27+
}
28+
29+
search(word: string): boolean {
30+
let node = this.root;
31+
for (let char of word) {
32+
if (!node.children.has(char)) {
33+
return false;
34+
}
35+
node = node.children.get(char)!;
36+
}
37+
return node.isEndOfWord;
38+
}
39+
40+
startsWith(prefix: string): boolean {
41+
let node = this.root;
42+
for (let char of prefix) {
43+
if (!node.children.has(char)) {
44+
return false;
45+
}
46+
node = node.children.get(char)!;
47+
}
48+
return true;
49+
}
50+
51+
remove(word: string): boolean {
52+
return this.#removeWord(this.root, word, 0);
53+
}
54+
55+
#removeWord(node: TrieNode, word: string, index: number): boolean {
56+
if (index === word.length) {
57+
if (!node.isEndOfWord) return false;
58+
node.isEndOfWord = false;
59+
return node.children.size === 0;
60+
}
61+
62+
const char = word[index];
63+
if (!node.children.has(char)) {
64+
return false;
65+
}
66+
67+
const shouldDeleteCurrentNode = this.#removeWord(node.children.get(char)!, word, index + 1);
68+
if (shouldDeleteCurrentNode) {
69+
node.children.delete(char);
70+
return node.children.size === 0;
71+
}
72+
73+
return false;
74+
}
75+
}
76+
77+
export default Trie;

0 commit comments

Comments
 (0)