ํ๋ก๊ทธ๋๋จธ์ค ํ๋ก ํธ์๋ ๋ฐ๋ธ์ฝ์ค 2์ฃผ์ฐจ ๊ณผ์ ์ธ ํธ๋ผ์ด ์๋ฃ๊ตฌ์กฐ๋ฅผ ์ด์ฉํ ์๋์์ฑ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด์๋ค.
๐ก ์๋์์ฑ ๊ธฐ๋ฅ์ด๋ ?
=> ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒ์์ด๋ก๋ถํฐ ์ถ์ฒ๋ ๊ฒ์์ด ๋ฆฌ์คํธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ฒ
์๋์์ฑ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ , ์ฐ์ ํธ๋ผ์ด ์๋ฃ๊ตฌ์กฐ์ ๋ํด์ ์์๋ณด์.
๋ฌธ์์ด์ ์ ์ฅํ๊ณ ํจ์จ์ ์ผ๋ก ํ์ํ๊ธฐ ์ํ ํธ๋ฆฌ ํํ์ ์๋ฃ ๊ตฌ์กฐ์ด๋ค.
=> ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ฑํ๋ ๋ ธ๋๋ค์ ์ธ์ ๋ฆฌ์คํธ ํํ๋ก ๊ตฌํ
class Node{
constructor(value=""){
this.value = value; // ๋
ธ๋์ ๋ค์ด๊ฐ ๋ฌธ์ ๊ฐ
this.children = new Map(); // ์ฐ๊ฒฐ๋ ๋ค์ ๋
ธ๋๋ค์ ๋ฌธ์๊ฐ ?
//
}
}
๋ฃจํธ ๋
ธ๋์ value๋ ""
์ด๊ณ , children
์ด๋ผ๋ map ์๋ฃ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ์ด children์ ๊ฐ entry๋ค์ key ๊ฐ์ ๊ฐ์ ์ ์๋ฏธํ๊ณ , value ๊ฐ์ด ์ฐ๊ฒฐ๋ ๋
ธ๋๋ฅผ ์๋ฏธํ๋ค.
=> ์ ์์ ๊ทธ๋ฆผ์์ t ๋
ธ๋์ value๋ t
์ด๊ณ , children์ map ์๋ฃ๊ตฌ์กฐ๋ก 2๊ฐ์ entry๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ์ด๋
{"e" : value=te
์ธ node}
{"o" : value=to
์ธ node } ์ด๋ค.
์์ธํ ๊ตฌํ ๋ด์ฉ์ ๋ค์ ๋ชฉ์ฐจ์์ ์ดํด๋ณด์ !
๋ด๊ฐ ์ด๋ฒ์ ๊ตฌํํ๊ณ ์ ํ๋ ์๋์์ฑ์ ๊ธฐ๋ฅ์ ์๋์ ๊ฐ๋ค.
์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๊ฒ์์ด๋ก๋ถํฐ ์์๋ ์ ์๋ ๋ค๋ฅธ ๊ฒ์์ด๋ค์ ๋ชฉ๋ก์ ๊ฒ์์ด์ ๊ธธ์ด ์์ผ๋ก ๋ณด์ฌ์ค๋ค. ์๋ฅผ ๋ค์ด,
plz
,proud
,program
,picnic
,programmers
์ ๊ฒ์์ด๋ค์ด ๋ฑ๋ก๋์ด ์๊ณ ์ฌ์ฉ์๊ฐpro
๋ฅผ ์ ๋ ฅํ๋ค๋ฉด =>proud
,program
,programmers
์ ๋จ์ด๋ค์ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ๊ธฐ๋ฅ์ ํธ๋ผ์ด๋ฅผ ๋ฐํ์ผ๋ก ๊ตฌํํ ๊ณผ์ ์ ์์๋ณด์.
class Queue { // level order traversal์ ์ํ Queue ๊ตฌํ
constructor(){
this.queue = [];
this.front = 0;
this.back = 0;
}
enqueue(value){
this.queue[this.back++] = value;
}
dequeue(){
const value = this.queue[this.front];
delete this.queue[this.front]; //
this.front += 1;
return value;
}
size(){
return this.back - this.front;
}
}
class Node {
constructor(value="", isWord=false){
this.value = value;
this.children = new Map();
this.isWord = isWord; // node์ value๊ฐ์ด ๋ฑ๋ก๋ ๋จ์ด์ธ์ง
}
}
Array๋ฅผ ์ด์ฉํด Queue๋ฅผ ๊ตฌํํ๊ณ , ํธ๋ผ์ด๋ฅผ ๊ตฌ์ฑํ๋ ๋ ธ๋์ ํด๋นํ๋ Node ํด๋์ค๋ ๊ตฌํํด์ฃผ์๋ค.
Node ํด๋์ค๋ฅผ ๊ตฌํํ ๋, node์ ๊ฐ์ ๋ํ๋ด๋ value ํ๋กํผํฐ์ ์์ node์ ์งํฉ์ธ children ํ๋กํผํฐ ๋ฟ๋ง ์๋๋ผ, ํด๋น node์ value๊ฐ ๋ฑ๋ก๋ ๋จ์ด์ธ์ง๋ฅผ ๋ํ๋ด๋ isWord
ํ๋กํผํฐ๋ ํจ๊ป ์ ์ธํด์ฃผ์๋ค.
isWord ํ๋กํผํฐ๋ฅผ ์ ์ธํด์ค ์ด์ ๋ ?
์์ ๊ฐ์ ํธ๋ผ์ด ๊ตฌ์กฐ์์ ์ ์ ๊ฐ ๋ง์ฝ
t
๋ฅผ ์ ๋ ฅํ๋ค๋ฉด ?t์ ์์ ๋ ธ๋๋ค์
to
,te
,tea
,ted
,ten
์ด ์์ง๋ง์ด ์ค์์ ์ค์ ๋จ์ด์ธtea
,ted
,ten
,to
๋ง ์ถ๋ ฅํ๊ณ ,te
๋ ๋จ์ด๋ฅผ ์ ๋ ฅํ๋ ๊ณผ์ ์์ ํธ๋ผ์ด์ ์ถ๊ฐ๋๊ธด ํ์ง๋ง, ํน์ ๋จ์ด์ ์ผ๋ถ๋ถ์ด๊ธฐ ๋๋ฌธ์ ์ถ๋ ฅํ์ง ์์ผ๋ ค๊ณ ํ๋ค.๋ฐ๋ผ์, ๋จ์ด๋ค์ ๋ฑ๋กํ ๋
isWord
๋ฅผtrue
๋ก ์ค์ ํด์ฃผ๊ณisWord
๊ฐtrue
์ธ ๋จ์ด๋ค๋ง ์ถ๋ ฅํด์ฃผ๋ ค๊ณ ํ๋ค.
class Trie {
constructor(value=""){
this.root = new Node(value);
}
insert(string){
let currentNode = this.root;
for(const char of string){
if(!currentNode.children.has(char)){
const charForInsert = currentNode.value + char; // ๋ค์ node์ ์
๋ ฅ๋ value๊ฐ
currentNode.children.set(
char,
new Node(charForInsert, charForInsert === string)
);
}
currentNode = currentNode.children.get(char);
}
}
find(string){ // string์ ํด๋นํ๋ node๋ฅผ ๋ฐํ
let currentNode = this.root;
for(const char of string){
if(!currentNode.children.has(char)){
return null;
}
currentNode = currentNode.children.get(char);
}
return currentNode;
}
}
insert
๋ฉ์๋์ฌ์ฉ์์๊ฒ ๊ฒ์์ด๋ฅผ ์ถ์ฒํด์ฃผ๊ธฐ ์ํ ๋ฆฌ์คํธ์ ์๋ก์ด ๋จ์ด๋ฅผ ์ถ๊ฐํ๋ ๋ฉ์๋์ด๋ค. ๋์ ๊ณผ์ ์ ์ดํดํ๊ธฐ ์ํด ์๋ ๊ทธ๋ฆผ์์ toss
๋ผ๋ ๋จ์ด๋ฅผ ๋ฑ๋กํ๋ ์์๋ฅผ ์๊ฐํด๋ณด์.
insert("toss");
currentNode
๋ฅผ root ๋
ธ๋๋ก ์ค์ ํ๋ค.
์
๋ ฅ๋ ๋ฌธ์์ด toss
์ ๊ฐ ๋ฌธ์ t
, o
, s
, s
๋ฅผ ํ๋์ฉ for๋ฌธ์ผ๋ก ๋ ๊ฒ์ด๋ค.
t
=> ํ์ฌ currentNode
์ children
ํ๋กํผํฐ๊ฐ t
๋ผ๋ key ๊ฐ์ ํด๋นํ๋ value๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๊ฒ์ฌํ๊ณ , ํ์ฌ๋ ์๊ธฐ ๋๋ฌธ์ currentNode
๋ฅผ ํด๋น children
์ ๋ค์ด์๋ node
์ค key ๊ฐ t
์ ํด๋นํ๋ node๋ก ๋ฐ๊ฟ์ฃผ๊ณ , ์ด node
๋ value ๊ฐ์ผ๋ก t
๋ฅผ ๊ฐ์ง ๊ฒ์ด๋ค.
o
=> ํ์ฌ currentNode
์ children
ํ๋กํผํฐ๊ฐ o
๋ผ๋ key ๊ฐ์ ํด๋นํ๋ value๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๊ฒ์ฌํ๊ณ , ํ์ฌ๋ ์๊ธฐ ๋๋ฌธ์ currentNode
๋ฅผ ํด๋น children
์ ๋ค์ด์๋ node ์ค key ๊ฐ o
์ ํด๋นํ๋node
๋ก ๋ฐ๊ฟ์ฃผ๊ณ , ์ด node
๋ value ๊ฐ์ผ๋ก to
๋ฅผ ๊ฐ์ง ๊ฒ์ด๋ค.
s
=> ํ์ฌ currentNode
์ children
ํ๋กํผํฐ๊ฐ s
๋ผ๋ key ๊ฐ์ ํด๋นํ๋ value๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ๊ฒ์ฌํ๊ณ , ํ์ฌ ๋
ธ๋์ธ to
๋ ์์ ๋
ธ๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ํ์ฌ ๋
ธ๋์ children
map ๊ฐ์ฒด์ { key : "s" , value : Node() }
์ธ ์์๋ฅผ ์ถ๊ฐํ๋ค.
์ฌ๊ธฐ์ ์ด node๋ ๋ฑ๋กํ๋ ค๋ ๋จ์ด toss
๊ฐ ์๋๊ธฐ ๋๋ฌธ์ node์ isWord
ํ๋กํผํฐ๋ false
๋ก ์ค์ ํด์ค๋ค. ๋ฑ๋กํ๋ ค๋ ๋จ์ด์ธ์ง ํ๋จํ๊ธฐ ์ํด, ๋ฑ๋กํ๋ ค๋ ๋จ์ด string
๊ณผ ๋
ธ๋์ value
๊ฐ์ด ๋ currentNode.value + char
๊ฐ์ ๋น๊ตํด์คฌ๋ค.
const charForInsert = currentNode.value + char; // ๋ค์ node์ ์
๋ ฅ๋ value ๊ฐ
currentNode.children.set(
char,
new Node(charForInsert, charForInsert === string)
);
s
์ s
์ ๊ฐ์ ๊ณผ์ ์ ๊ฑฐ์น๊ณ , ์ฌ๊ธฐ์๋, isWord = true
์ธ ๊ฐ์ผ๋ก ๋
ธ๋๊ฐ ์ถ๊ฐ๋๋ค.
levelOrder(node){ // level order
const q = new Queue();
q.enqueue(node);
while(q.size()){
const currentNode = q.dequeue();
if(currentNode.isWord){ // ๋ฑ๋ก๋ ๋จ์ด๋ง ์ถ๋ ฅ
console.log(currentNode.value);
}
for(let key of currentNode.children.keys()){
q.enqueue(currentNode.children.get(key));
}
}
}
autoComplete(string){
// string์ ๊ฐ์ง๊ณ ์๋ node๋ฅผ ์ฐพ์์
let node = this.find(string);
// node๋ถํฐ ์ํ ์์
console.log(`๊ฒ์์ด : ${string}`);
this.levelOrder(node);
}
leverOrder
๋ฉ์๋์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋จ์ด๋ก๋ถํฐ ์์ฑ๋ ์ ์๋ ์ถ์ฒ ๊ฒ์์ด๋ค์ ๊ธธ์ด์์ผ๋ก ์ถ๋ ฅํด ์ค ๊ฒ์ด๊ธฐ ๋๋ฌธ์ Queue๋ฅผ ์ด์ฉํ lever order ์ํ๋ฅผ ๊ตฌํํ๋ค.
์ ๋ ฅ๋ node๋ถํฐ ์์ ๋ ธ๋๋ค์ level order๋ก ์ํํ๋ฉด์ ๋ฑ๋ก๋ ๋จ์ด์ผ ๋๋ง ์ถ๋ ฅํด์ฃผ์๋ค.
if(currentNode.isWord){ // ๋ฑ๋ก๋ ๋จ์ด๋ง ์ถ๋ ฅ
console.log(currentNode.value);
}
autoComplete
๋ฉ์๋string
์ ๊ฐ์ง๊ณ ์๋ node๋ฅผ ์ฐพ๊ณ leverOrder
๋ฉ์๋๋ฅผ ์คํํ๋ฉด์๋์์ฑ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค.
const trie = new Trie();
trie.insert("pro"); // ์ถ๋ ฅ x
trie.insert("proto"); // ์ถ๋ ฅ x
trie.insert("prou"); // ์ถ๋ ฅ x
trie.insert("protorype");
trie.insert("programmers");
trie.insert("primary");
trie.insert("proud");
trie.insert("picnic");
trie.insert("plz dm");
trie.autoComplete("pro");
๋ด๊ฐ ๊ธฐ๋ํ๋๋ก ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค !