题目描述
前缀树是一种树形结构,用于高效的存储和检索字符串。请实现字符串,要求如下:
- Trie() 初始化前缀树对象。
- void insert(String word) 向前缀树中插入字符串 word 。
- boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
- boolean startsWith(String prefix) 如果之前已经插入的字符串word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]解释
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
解法思路
构造节点
字符长度是26,使用一个长为26的数组做处理。那么字符'a'所在位置就是0='a'-'a',字符'p'所在位置就是'p'-'a';
考虑方法search(word),这个方法要求word在前缀树中,并且word的最后一个字符也能在前缀树中找到并且也是最后一个字符。这里我参考了其他人的实现,需要增加一个标志符是否是结尾字符。
通过上述信息,可以确定单个节点的数据结构:
class Node {
boolean end;
Node[] nodes;public Node() {
this.nodes = new Node[26];
this.end = false;
}
}
完成insert方法
insert操作时,用户传入word,那么需要处理每个字符。
- 首先,查找这个字符的 int index=word.charAt(i)-'a' ;
- 找到这个字符所在位置,如果不存在下一个节点,则新建一个节点;如果存在下一个节点,则继续执行。
- 最后将最后一个字符对应的节点的属性end设置成true
这样可以得到如下代码:
public void insert(String word) {
Node temp = root;
for (char c : word.toCharArray()) {
if (temp.nodes[c - 'a'] == null) {
temp.nodes[c - 'a'] = new Node();
}
temp = temp.nodes[c - 'a'];
}
temp.end = true;
}
完成search方法
search执行时,用户传入word进行全字符匹配,这个时候参考insert操作;判断条件做出调整:
- 如果还没有遍历完成,有节点为NULL,则直接返回false;
- 如果遍历完成,则判断最后的节点的end是否为true;
这样有代码如下:
public boolean search(String word) {
Node temp = root;
for (char c : word.toCharArray()) {
if (temp.nodes[c - 'a'] == null) {
return false;
}
temp = temp.nodes[c - 'a'];
}
return temp.end;
}
完成startsWith方法
执行startsWith则只需要保证有字符能匹配即可,参考search代码,
- 如果还没有遍历完成,有节点为NULL,则直接返回false;
- 遍历完成,则直接返回true。
那么具体代码实现如下:
public boolean startsWith(String prefix) {
Node temp = root;
for (char c : prefix.toCharArray()) {
if (temp.nodes[c - 'a'] == null) {
return false;
}
temp = temp.nodes[c - 'a'];
}
return true;
}
代码实现
class Trie {
Node root;
public Trie() {
this.root = new Node();
}
public void insert(String word) {
Node temp = root;
for (char c : word.toCharArray()) {
if (temp.nodes[c - 'a'] == null) {
temp.nodes[c - 'a'] = new Node();
}
temp = temp.nodes[c - 'a'];
}
temp.end = true;
}
public boolean search(String word) {
Node temp = root;
for (char c : word.toCharArray()) {
if (temp.nodes[c - 'a'] == null) {
return false;
}
temp = temp.nodes[c - 'a'];
}
return temp.end;
}
public boolean startsWith(String prefix) {
Node temp = root;
for (char c : prefix.toCharArray()) {
if (temp.nodes[c - 'a'] == null) {
return false;
}
temp = temp.nodes[c - 'a'];
}
return true;
}
class Node {
boolean end;
Node[] nodes;
public Node() {
this.nodes = new Node[26];
this.end = false;
}
}
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("apple");
System.out.println(trie.search("apple"));
System.out.println(trie.search("app"));
System.out.println(trie.startsWith("app"));
trie.insert("app");
System.out.println(trie.search("app"));
}
}
总结
这道题是对前缀树的实现,核心是考虑清楚如何构造前缀树的节点。这里我们使用了一个空NODE作为root节点,方便后续的操作。耗时和空间上应该还可以优化,有好的方案欢迎评论,大家一起探讨。