Skip to content

Commit de861fd

Browse files
committed
trie 구현 연습
1 parent 863c8a2 commit de861fd

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

허현빈/8주차/trie-구현-1.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// @ts-nocheck
2+
3+
class TrieNode {
4+
constructor() {
5+
this.children = new Map();
6+
this.pass = 0;
7+
this.end = 0;
8+
}
9+
}
10+
11+
class Trie {
12+
constructor() {
13+
this.root = new TrieNode();
14+
this.totalWorlds = 0;
15+
}
16+
insert(word) {
17+
let node = this.root;
18+
node.pass++;
19+
20+
for (let i = 0; i < word.length; i++) {
21+
const ch = word[i];
22+
if (!node.children.has(ch)) {
23+
node.children.set(ch, new TrieNode());
24+
}
25+
node = node.children.get(ch);
26+
node.pass++;
27+
}
28+
node.end++;
29+
this.totalWorlds = word.length;
30+
}
31+
32+
#walk(s) {
33+
let node = this.root;
34+
for (let i = 0; i < s.length; i++) {
35+
const ch = s[i];
36+
const next = node.children.get(ch);
37+
if (!next) return null;
38+
39+
node = next;
40+
}
41+
return node;
42+
}
43+
44+
search(s) {
45+
const node = this.#walk(s);
46+
return !!node && node.end > 0;
47+
}
48+
49+
countWordsEqualTo(s) {
50+
const node = this.#walk(s);
51+
return node ? node.end : 0;
52+
}
53+
54+
startsWith(s) {
55+
const node = this.#walk(s);
56+
return node ? node.pass : 0;
57+
}
58+
59+
delete(s) {
60+
if (!this.search(s)) return false;
61+
let node = this.root;
62+
node.pass--;
63+
const stack = [];
64+
for (let i = 0; i < s.length; i++) {
65+
const ch = s[i];
66+
stack.push([node, ch]);
67+
node = node.children.get(ch);
68+
node.pass--;
69+
}
70+
node.end--;
71+
this.totalWorlds--;
72+
for (let i = stack.length - 1; i >= 0; i--) {
73+
const [par, ch] = stack[i];
74+
const child = par.children.get(ch);
75+
if (child.pass === 0) par.children.delete(ch);
76+
else break;
77+
}
78+
return true;
79+
}
80+
81+
words() {
82+
const res = [];
83+
const path = [];
84+
85+
const dfs = (node) => {
86+
if (node.end > 0) {
87+
res.push(path.join(""));
88+
}
89+
for (const [ch, next] of node.children) {
90+
path.push(ch);
91+
dfs(next);
92+
path.pop();
93+
}
94+
};
95+
96+
dfs(this.root);
97+
return res;
98+
}
99+
}
100+
const trie = new Trie();
101+
102+
trie.insert("flower");
103+
trie.insert("flow");
104+
trie.insert("flight");
105+
trie.insert("flow"); // duplicate
106+
107+
console.log(trie.search("flow")); // true
108+
console.log(trie.countWordsEqualTo("flow")); // 2
109+
console.log(trie.startsWith("fl")); // true
110+
111+
trie.delete("flow");
112+
console.log(trie.countWordsEqualTo("flow")); // 1
113+
console.log(trie.words()); // unique words: ["flower","flow","flight"] (again order may vary)

허현빈/8주차/trie-구현.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// @ts-nocheck
2+
3+
class TrieNode {
4+
constructor() {
5+
this.children = new Map();
6+
this.pass = 0;
7+
this.end = 0;
8+
}
9+
}
10+
11+
class Trie {
12+
constructor() {
13+
this.root = new TrieNode();
14+
this.totalWords = 0;
15+
}
16+
17+
insert(word) {
18+
if (typeof word !== "string") throw new TypeError("word must be a string");
19+
let node = this.root;
20+
node.pass++;
21+
22+
for (const ch of word) {
23+
if (!node.children.has(ch)) node.children.set(ch, new TrieNode());
24+
node = node.children.get(ch);
25+
node.pass++;
26+
}
27+
node.end++;
28+
this.totalWords++;
29+
}
30+
31+
search(word) {
32+
const node = this.#walk(word);
33+
return !!node && node.end > 0;
34+
}
35+
36+
startsWith(prefix) {
37+
return !!this.#walk(prefix);
38+
}
39+
40+
countWordsEqualTo(word) {
41+
const node = this.#walk(word);
42+
return node ? node.end : 0;
43+
}
44+
45+
countWordsStartingWith(prefix) {
46+
const node = this.#walk(prefix);
47+
return node ? node.pass : 0;
48+
}
49+
50+
delete(word) {
51+
if (!this.search(word)) return false;
52+
53+
let node = this.root;
54+
node.pass--;
55+
56+
const stack = [];
57+
for (const ch of word) {
58+
stack.push([node, ch]);
59+
node = node.children.get(ch);
60+
node.pass--;
61+
}
62+
63+
node.end--;
64+
this.totalWords--;
65+
66+
for (let i = stack.length - 1; i >= 0; i--) {
67+
const [parent, ch] = stack[i];
68+
const child = parent.children.get(ch);
69+
if (child.pass === 0) parent.children.delete(ch);
70+
else break;
71+
}
72+
return true;
73+
}
74+
75+
words() {
76+
const res = [];
77+
const path = [];
78+
79+
const dfs = (node) => {
80+
if (node.end > 0) res.push(path.join(""));
81+
for (const [ch, nxt] of node.children) {
82+
path.push(ch);
83+
dfs(nxt);
84+
path.pop();
85+
}
86+
};
87+
88+
dfs(this.root);
89+
return res;
90+
}
91+
92+
longestCommonPrefix() {
93+
if (this.totalWords === 0) return "";
94+
95+
let node = this.root;
96+
let prefix = "";
97+
98+
while (node.children.size === 1 && node.end === 0) {
99+
const [[ch, next]] = node.children.entries();
100+
prefix += ch;
101+
node = next;
102+
}
103+
return prefix;
104+
}
105+
106+
#walk(s) {
107+
let node = this.root;
108+
for (const ch of s) {
109+
const nxt = node.children.get(ch);
110+
if (!nxt) return null;
111+
node = nxt;
112+
}
113+
return node;
114+
}
115+
}
116+
117+
const trie = new Trie();
118+
119+
trie.insert("flower");
120+
trie.insert("flow");
121+
trie.insert("flight");
122+
trie.insert("flow"); // duplicate
123+
124+
console.log(trie.search("flow")); // true
125+
console.log(trie.countWordsEqualTo("flow")); // 2
126+
console.log(trie.startsWith("fl")); // true
127+
console.log(trie.countWordsStartingWith("flo")); // 3 ("flower","flow","flow")
128+
129+
console.log(trie.autocomplete("fl", { limit: 10 })); // e.g. ["flight","flow","flower"] (order depends on insertion/Map order)
130+
console.log(trie.searchWithWildcard("fl..")); // true ("flow")
131+
console.log(trie.longestCommonPrefix()); // "fl"
132+
133+
trie.delete("flow");
134+
console.log(trie.countWordsEqualTo("flow")); // 1
135+
console.log(trie.words()); // unique words: ["flower","flow","flight"] (again order may vary)

0 commit comments

Comments
 (0)