字典树 在计算机科学中, 字典树(trie,中文又被称为”单词查找树“或 ”键树“) , 也称为数字树,有时候也被称为基数树或前缀树(因为它们可以通过前缀搜索),它是一种搜索树–一种已排序的数据结构,通常用于存储动态集或键为字符串的关联数组。
与二叉搜索树不同, 树上没有节点存储与该节点关联的键; 相反,节点在树上的位置定义了与之关联的键。一个节点的全部后代节点都有一个与该节点关联的通用的字符串前缀, 与根节点关联的是空字符串。
值对于字典树中关联的节点来说,不是必需的,相反,值往往和相关的叶子相关,以及与一些键相关的内部节点相关。
TrieNode constructor(character, isCompleteWord = false)初始化方法 1 2 3 4 5 6 7 8 9 10 constructor (character, isCompleteWord = false ) { this .character = character; this .isCompleteWord = isCompleteWord; this .children = new HashTable (); }
getChild(character) 方法 1 2 3 4 5 6 7 8 9 getChild (character ) { return this .children .get (character); }
addChild(character, isCompleteWord = false) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 addChild (character, isCompleteWord = false ) { if (!this .children .has (character)) { this .children .set (character, new TrieNode (character, isCompleteWord)); } const childNode = this .children .get (character); childNode.isCompleteWord = childNode.isCompleteWord || isCompleteWord; return childNode; }
removeChild(character) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 removeChild (character ) { const childNode = this .getChild (character); if ( childNode && !childNode.isCompleteWord && !childNode.hasChildren () ) { this .children .delete (character); } return this ; }
hasChild(character) 方法 1 2 3 4 5 6 7 hasChild (character ) { return this .children .has (character); }
hasChildren() 方法 1 2 3 4 5 6 7 8 9 10 11 hasChildren ( ) { return this .children .getKeys ().length !== 0 ; }
suggestChildren() 方法 1 2 3 4 5 6 7 suggestChildren ( ) { return [...this .children .getKeys ()]; }
toString() 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 toString ( ) { let childrenAsString = this .suggestChildren ().toString (); childrenAsString = childrenAsString ? `:${childrenAsString} ` : '' ; const isCompleteString = this .isCompleteWord ? '*' : '' ; return `${this .character} ${isCompleteString} ${childrenAsString} ` ; }
Trie 类 constructor() 初始化方法 1 2 3 4 constructor ( ) { this .head = new TrieNode (HEAD_CHARACTER ); }
addWord(word) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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 ; } depthFirstDelete (nextNode, charIndex + 1 ); if (charIndex === (word.length - 1 )) { nextNode.isCompleteWord = false ; } currentNode.removeChild (character); }; depthFirstDelete (this .head ); return this ; }
suggestNextCharacters(word) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 suggestNextCharacters (word ) { const lastCharacter = this .getLastCharacterNode (word); if (!lastCharacter) { return null ; } return lastCharacter.suggestChildren (); }
doesWordExist(word) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 doesWordExist (word ) { const lastCharacter = this .getLastCharacterNode (word); return !!lastCharacter && lastCharacter.isCompleteWord ; }
getLastCharacterNode(word) 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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; }