import HashTable from '../hash-table/HashTable';
export default class TrieNode {
constructor(character, isCompleteWord = false) {
this.character = character;
this.isCompleteWord = isCompleteWord;
this.children = new HashTable();
}
getChild(character) {
return this.children.get(character);
}
addChild(character, isCompleteWord = false) {
if (!this.children.has(character)) {
this.children.set(character, new TrieNode(character, isCompleteWord));
}
const childNode = this.children.get(character);
// ex) "carpet" 뒤에 "car"를 추가하는 경우 "r" 문자를 완성으로 표시해야 함
childNode.isCompleteWord = childNode.isCompleteWord || isCompleteWord;
return childNode;
}
removeChild(character) {
const childNode = this.getChild(character);
// childNode에 children이 없고
// && childNode가 CompleteWord가 아닐 경우에만 삭제
if (
childNode
&& !childNode.isCompleteWord
&& !childNode.hasChildren()
) {
this.children.delete(character);
}
return this;
}
hasChild(character) {
return this.children.has(character);
}
hasChildren() {
return this.children.getKeys().length !== 0;
}
suggestChildren() {
return [...this.children.getKeys()];
}
toString() {
let childrenAsString = this.suggestChildren().toString();
childrenAsString = childrenAsString ? `:${childrenAsString}` : '';
const isCompleteString = this.isCompleteWord ? '*' : '';
return `${this.character}${isCompleteString}${childrenAsString}`;
}
}
import TrieNode from './TrieNode';
// 트라이 트리의 루트에 사용할 문자
const HEAD_CHARACTER = '*';
export default class Trie {
constructor() {
this.head = new TrieNode(HEAD_CHARACTER);
}
addWord(word) {
const characters = Array.from(word);
let currentNode = this.head;
for (let charIndex = 0; charIndex < characters.length; charIndex += 1) {
const isComplete = charIndex === characters.length - 1;
currentNode = currentNode.addChild(characters[charIndex], isComplete);
}
return this;
}
deleteWord(word) {
const depthFirstDelete = (currentNode, charIndex = 0) => {
if (charIndex >= word.length) {
return; // 단어의 범위를 벗어난 문자를 삭제하려는 경우 반환
}
const character = word[charIndex];
const nextNode = currentNode.getChild(character);
if (nextNode == null) {
return; // 트리에 추가되지 않은 단어를 삭제하려는 경우 반환
}
// Go deeper.
depthFirstDelete(nextNode, charIndex + 1);
// 마지막 character의 isCompleteWord flag는 false로 설정
if (charIndex === (word.length - 1)) {
nextNode.isCompleteWord = false;
}
// childNode에 children이 없고
// && childNode가 CompleteWord가 아닐 경우에만 삭제
currentNode.removeChild(character);
};
// Start depth-first deletion from the head node.
depthFirstDelete(this.head);
return this;
}
suggestNextCharacters(word) {
const lastCharacter = this.getLastCharacterNode(word);
if (!lastCharacter) {
return null;
}
return lastCharacter.suggestChildren();
}
// Check if complete word exists in Trie.
doesWordExist(word) {
const lastCharacter = this.getLastCharacterNode(word);
return !!lastCharacter && lastCharacter.isCompleteWord;
}
getLastCharacterNode(word) {
const characters = Array.from(word);
let currentNode = this.head;
for (let charIndex = 0; charIndex < characters.length; charIndex += 1) {
if (!currentNode.hasChild(characters[charIndex])) {
return null;
}
currentNode = currentNode.getChild(characters[charIndex]);
}
return currentNode;
}
}
Github | tech-interview-for-developer
Github | Interview_Question_for_Beginner
Github | javascript-algorithms | trekhleb
Photo by Alain Pham on Unsplash