题解
前缀树
(只保存小写字符的)「前缀树」是一种特殊的多叉树,它的 TrieNode 中 chidren 是一个大小为 26 的一维数组,分别对应了26个英文字符 ‘a’ ~ ‘z’,也就是说形成了一棵 26叉树。
前缀树的结构可以定义为下面这样。
里面存储了两个信息:
- isWord 表示从根节点到当前节点为止,该路径是否形成了一个有效的字符串
- children 是该节点的所有子节点。
构建
在构建前缀树的时候,按照下面的方法:
- 根节点不保存任何信息;
- 关键词放到「前缀树」时,需要把它拆成各个字符,每个字符按照其在 ‘a’ ~ ‘z’ 的序号,放在对应的 chidren 里面。下一个字符是当前字符的子节点。
- 一个输入字符串构建「前缀树」结束的时候,需要把该节点的 isWord 标记为 true,说明从根节点到当前节点的路径,构成了一个关键词。
下面是一棵「前缀树」,其中保存了 {“am”, “an”, “as”, “b”, “c”, “cv”} 这些关键词。图中红色表示 isWord 为 true。
看下面这个图的时候需要注意:
- 所有以相同字符开头的字符串,会聚合到同一个子树上。比如 {“am”, “an”, “as”} ;
- 并不一定是到达叶子节点才形成了一个关键词,只要 isWord 为true,那么从根节点到当前节点的路径就是关键词。比如 {“c”, “cv”} ;
查询
在判断一个关键词是否在「前缀树」中时,需要依次遍历该关键词所有字符,在前缀树中找出这条路径。可能出现三种情况:
- 在寻找路径的过程中,发现到某个位置路径断了。比如在上面的前缀树图中寻找 “d” 或者 “ar” 或者 “any” ,由于树中没有构建对应的节点,那么就查找不到这些关键词;
- 找到了这条路径,但是最后一个节点的 isWord 为 false。这也说明没有该关键词。比如在上面的前缀树图中寻找 “a” ;
- 找到了这条路径,并且最后一个节点的 isWord 为 true。这说明前缀树存储了这个关键词,比如上面前缀树图中的 “am” , “cv” 等。
应用
上面说了这么多前缀树,那前缀树有什么用呢?
其实我们生活中就有应用。比如我们常见的电话拨号键盘,当我们输入一些数字的时候,后面会自动提示以我们的输入数字为开头的所有号码。
比如我们的英文输入法,当我们输入半个单词的时候,输入法上面会自动联想和补全后面可能的单词。
代码
class Trie {
//类似于二叉树的孩子节点,不过孩子节点可以有26个
private Trie[] children;
private boolean isEnd;
public Trie() {
children = new Trie[26];
isEnd = false;
}
public void insert(String word) {
//?????
Trie node = this;
for(int i=0;i<word.length();i++){
char ch = word.charAt(i);
int index = ch - 'a';
//******
if(node.children[index]==null){
node.children[index] = new Trie();
}
//下一个子节点
node = node.children[index];
}
node.isEnd = true;
}
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;
for(int i=0;i<prefix.length();i++){
char ch = prefix.charAt(i);
int index = ch - 'a';
if(node.children[index]==null){
return null;
}
//下一个子节点
node = node.children[index];
}
return node;
}
}