法一
/**
* 前缀树
* (1)Trie树其实就是维护有公共前缀子串的树
* (2)利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较
* 复杂度
* (1)时间复杂度:初始化为O(1),插入或查找为O(N),N为插入或查找的字符串的长度
* (2)空间复杂度:O(26*N),N表示Trie结点数量
*/
public class Solution208 {
/**
* 前缀树根节点
*/
private TrieNode root;
/**
* 前缀树节点静态内部类
*/
private static class TrieNode {
boolean isEnd; // 标记每个单词的结尾
TrieNode[] children;
private TrieNode() {
isEnd = false;
children = new TrieNode[26]; // 每个节点最多有26个不同的小写字母
}
}
/**
* 初始化前缀树
*/
public Solution208() {
root = new TrieNode(); // 先构造出一个空的根节点
}
/**
* 向前缀树插入单词word
*
* @param word
*/
public void insert(String word) {
TrieNode cur = root; // 从根节点开始,一直向下走
for (int i = 0; i < word.length(); i++) { // 单词每个字母正着插
int c = word.charAt(i) - 'a'; // 将字母用数字表示出来,并作为下标
if (cur.children[c] == null) {
cur.children[c] = new TrieNode();
}
cur = cur.children[c];
}
cur.isEnd = true; // 单词插入完毕,此时cur指向的节点即为该单词的结尾
}
/**
* 判断一个单词word是否完整存在于前缀树中(非递归)
*
* @param word
* @return
*/
public boolean search(String word) {
TrieNode cur = root;
for (int i = 0; i < word.length(); i++) {
int c = word.charAt(i) - 'a';
if (cur.children[c] == null) { // 走到了null,说明word不是前缀树的任何一条路径,返回false
return false;
}
cur = cur.children[c];
}
return cur.isEnd; // 若cur指向单词末尾,返回true,否则说明word仅仅是一个前缀,并不完整,返回false
}
/**
* 根据word和start得到此时的字符,然后看该字符是否与此时的节点node配对
*
* @param word
* @param node
* @param start
* @return
*/
private boolean match(String word, TrieNode node, int start) {
if (start == word.length()) {
return node.isEnd;
}
int c = word.charAt(start) - 'a';
return node.children[c] != null && match(word, node.children[c], start + 1);
}
/**
* 判断一个单词word是否完整存在于前缀树中(递归)
*
* @param word
* @return
*/
public boolean search_2(String word) {
return match(word, root, 0);
}
/**
* 判断一个单词word是否是前缀树中的前缀
*
* @param prefix
* @return
*/
public boolean startsWith(String prefix) {
TrieNode cur = root;
for (int i = 0; i < prefix.length(); i++) {
int c = prefix.charAt(i) - 'a';
if (cur.children[c] == null) {
return false;
}
cur = cur.children[c];
}
return true; // 不关心此时cur是不是指向单词末尾,如果prefix安全走完了,直接返回true即可
}
}
本地测试
/**
* 208. 实现 Trie (前缀树)
*/
lay.showTitle(208);
Solution208 sol208 = new Solution208();
sol208.insert("apple");
System.out.println(sol208.search("apple")); // 返回 True
System.out.println(sol208.search("app")); // 返回 False
System.out.println(sol208.startsWith("app")); // 返回 True
sol208.insert("app");
System.out.println(sol208.search_2("app")); // 返回 True