逐步解析力扣208. 实现 Trie (前缀树/字典树)
没看答案前随便写了下发现能过,但这完全是直接调轮子乱写
class Trie {
Set<String> a = new HashSet<>();
public Trie() {
}
public void insert(String word) {
a.add(word);
}
public boolean search(String word) {
return a.contains(word);
}
public boolean startsWith(String prefix) {
for (String s:a) {
if (s.startsWith(prefix)) return true;
}
return false;
}
}
主要目的是要学习前缀树(字典树)
前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
时间复杂度:初始化为 O(1)
,其余操作为 O(∣S∣)
,其中 |S|
是每次插入或查询的字符串的长度
比用哈希表快了不少
其实看了代码之后就很好理解
class Trie {
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;
}
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;
}
public boolean startsWith(String prefix) {
return searchPrefix(prefix) !=null;
}
}
首先以数组形式声明一个n个子节点(基本以26个英文字母为最大长度)
private Trie[] children;
和一个字符串结尾
private boolean isEnd;
构造的时候声明26个英文字母和为false的结尾
开始新增
把当前类作为node节点,开始遍历传进来的word字符串,字符-‘a’获取他的数组位置
int index = ch - 'a';
然后记录在 children 数组的对应位置上
if (node.children[index]==null) node.children[index] = new Trie();
node跳到子节点继续遍历
node = node.children[index];
走完循环也就到头了,把isEnd置true
这时候就插入完成,一个完整的字典树就建好了
多个不同字符串也能进来,例如包含了三个单词 “sea”,“sells”,“she” 的 Trie结构如图
红色节点的isEnd就是true
新增没问题了,然后是search方法,他其实和startsWith方法类似,不同点在于node.isEnd的判断
看一下共同调用的方法searchPrefix,和insert方法几乎一样
区别在于遍历匹配节点的时候,如果不匹配,子节点肯定为null,因此返回null;否则返回完整的树结构。
之后走出该方法,判断null来区分该字符串到底是不是前缀了,不等于null表示匹配成功,等于null gg