208. 实现 Trie (前缀树)
https://leetcode-cn.com/problems/implement-trie-prefix-tree/submissions/
这题用Java来实现,其实会比较繁琐一点,如果你平时有做二叉树的题目的话,这题应该会比较容易理解一点,因为这种树的关键点在于建造树的节点的类型,知道如何构建,基本上这题就好解决了。其实熟悉字典树的朋友肯定一眼就看出来了,这题就是要用字典树来完成,
先来看看我构造的节点的数据类:
class TrieNode {
char value;
boolean isWord = false;
TrieNode[] children = new TrieNode[26];
TrieNode() {
}
TrieNode(char value) {
this.value = value;
}
}
用属性『value』来表示当前节点存储的节点的值,『isWord』来表示这个节点是否单词的最后一个字母,如果是话,那么的『isWord』就要设置为true,否则就是默认的false。『children 』表示这个节点的子节点的数组,因为题目中表示只有小写字母,所以我们就初始化26个大小了。
插入步骤:
- 逐个遍历单词
- 每次都在子节点的数组中,根据字母的在响应的位置插入新的节点
我们先来看看错误的代码:
class Trie {
class TrieNode {
char value;
boolean isWord = false;
TrieNode[] children = new TrieNode[26];
TrieNode() {
}
TrieNode(char value) {
this.value = value;
}
}
private TrieNode root;
/** Initialize your data structure here. */
public Trie() {
root = new TrieNode(' ');
}
/** Inserts a word into the trie. */
public void insert(String word) {
TrieNode node = root;
for (int i = 0, len = word.length(); i < len; ++i) {
char temp = word.charAt(i);
node.children[temp - 'a'] = new TrieNode(temp);
node = node.children[temp - 'a'];
}
node.isWord = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
TrieNode node = root;
for (int i = 0, len = word.length(); i < len; ++i) {
char temp = word.charAt(i);
if (node.children[temp - 'a'] == null) {
return false;
}
if (node.children[temp - 'a'].value != temp) {
return false;
}
node = node.children[temp - 'a'];
}
return node.isWord;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
TrieNode node = root;
for (int i = 0, len = prefix.length(); i < len; ++i) {
char temp = prefix.charAt(i);
if (node.children[temp - 'a'] == null) {
return false;
}
if (temp != node.children[temp - 'a'].value) {
return false;
}
node = node.children[temp - 'a'];
}
return true;
}
}
上面的代码会比较冗余,但是最大的问题不是冗余,而是有条件判断错了,其实这里最重要的就是插入部分的代码,为啥有问题?因为在逐个字母插入的时候,我们每次都去新建了一个节点,而新建的节点里的『isWord』的值是false,也就是说,如果你有个字母覆盖了前面的一个单词的所有的字母,那么这个单词背查询出来就是false的。因为那个标识是否是单词的属性已变成“false”了。
Show the code:
class Trie {
class TrieNode {
char value;
boolean isWord = false;
TrieNode[] children = new TrieNode[26];
TrieNode() {
}
TrieNode(char value) {
this.value = value;
}
}
private TrieNode root;
/** Initialize your data structure here. */
public Trie() {
root = new TrieNode(' ');
}
/** Inserts a word into the trie. */
public void insert(String word) {
TrieNode node = root;
for (int i = 0, len = word.length(); i < len; ++i) {
char temp = word.charAt(i);
// 这里就是最重要的代码了,只有对应的子节点的为null的才去新建。
if (node.children[temp - 'a'] == null) {
node.children[temp - 'a'] = new TrieNode(temp);
}
// node等于他的子节点。
node = node.children[temp - 'a'];
}
node.isWord = true;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
TrieNode node = root;
for (int i = 0, len = word.length(); i < len; ++i) {
char temp = word.charAt(i);
if (node.children[temp - 'a'] == null) {
return false;
}
node = node.children[temp - 'a'];
}
return node.isWord;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
TrieNode node = root;
for (int i = 0, len = prefix.length(); i < len; ++i) {
char temp = prefix.charAt(i);
if (node.children[temp - 'a'] == null) {
return false;
}
node = node.children[temp - 'a'];
}
return true;
}
}