题目链接:https://leetcode.com/problems/implement-trie-prefix-tree/
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 true
trie.search("app"); // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");
trie.search("app"); // 返回 true
说明:
你可以假设所有的输入都是由小写字母 a-z 构成的。
保证所有输入均为非空字符串。
思路:
前缀树,又称字典树。
以一张图来看什么是前缀树:
Trie树,又叫字典树、前缀树(Prefix Tree)、单词查找树或键树,是一种多叉树结构。如下图:
上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。从上图可以归纳出Trie树的基本性质:
①根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
②从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
③每个节点的所有子节点包含的字符互不相同。
④从第一字符开始有连续重复的字符只占用一个节点,比如上面的to,和ten,中重复的单词t只占用了一个节点。
前缀树的应用
1、前缀匹配
2、字符串检索
3、词频统计
4、字符串排序
回到此题,由于只包含26个小写字母,于是我们构造一个TrieNode的数据结构,它的域中包含26个字母的TrieNode类型的数组
以及单词是否结束的IsEnd(Boolean类型)。
然后在trie类中定义一个root节点,每次的insert、search。startwith都是从root节点开始的。
插入apple:
最初temp=root;
1 . 先是'a',由于最初初始化的root.children['a'-'a']即root.children[0]==null,所以在children[0]的位置重新定义一个TrieNode
此时把temp更新为root.children[0]即一个新的TrieNode节点,
2 . 再是'p'同理,temp更新为root.children[0].children['p'-'a'],也就是更深层的一个新的TrieNode节点
3 . 再是'p',temp更新为root.children[0].children['p'-'a'].children['p'-'a'],
4 . 'l' ,temp更新为root.children[0].children['p'-'a'].children['p'-'a'].children['l'-'a'],
5 . 'e',temp更新为root.children[0].children['p'-'a'].children['p'-'a'].children['l'-'a'].children['e'-'a'],
此时这个单词就结束了,在最后的temp所指向的节点就是
root.children[0].children['p'-'a'].children['p'-'a'].children['l'-'a'].children['e'-'a'],把它的isEnd域更改为true。
余下的思路同理可以应用到search和startwith操作上面。
AC 73 ms, faster than 98.77% of Java online submissions for Implement Trie (Prefix Tree).
class TrieNode{
TrieNode[] children;
boolean isEnd;
public TrieNode(){
children=new TrieNode[26];
isEnd=false;
}
}
class Trie {
TrieNode root;
/** Initialize your data structure here. */
public Trie() {
root=new TrieNode();
}
/** Inserts a word into the trie. */
public void insert(String word) {
TrieNode temp=root;
for(char c: word.toCharArray()){
if(temp.children[c-'a']==null){
temp.children[c-'a']=new TrieNode();
}
temp=temp.children[c-'a'];
}
temp.isEnd=true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
TrieNode temp=root;
for(char c:word.toCharArray()){
if(temp.children[c-'a']==null)
return false;
else
temp=temp.children[c-'a'];
}
return temp.isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
TrieNode temp=root;
for(char c: prefix.toCharArray()){
if(temp.children[c-'a']==null)
return false;
else
temp=temp.children[c-'a'];
}
return true;
}
}
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* boolean param_2 = obj.search(word);
* boolean param_3 = obj.startsWith(prefix);
*/