class TrieNode{
//定义子节点数组, 因为我们的trie树是一个多叉树, 我们的子节点有多个, 所以我们直接定义一个子节点数组即可
TrieNode [] children;
//表示当前位置的字符串是否为一个单词
boolean isWord;
//提供无参构造, 给children数组初始化, 这个时候我们使用的是普通数组作为子节点数组, 所以这个时候的数组长度我们直接定义为26即可
public TrieNode(){
/*
在创建一个节点的时候我们就要完成对子节点数组的初始化操作, 使用的是数组动态初始化方式, 声明了一个长度为26的TireNode[], 初始的时候
数组中的所有元素默认赋值为null
*/
this.children = new TrieNode[26];
}
}
public class Trie {
//定义根节点
TrieNode root;
//定义一个无参构造, 在其中完成根节点的初始化, 也就是创建一个Trie树
public Trie(){
this.root = new TrieNode();
}
//编写在Trie树中添加单词的方法
public void insert(String word){
//因为我们要遍历Trie树, 一旦需要遍历, 我们就要定义一个临时变量, 使用这个临时变量来代替root引用来遍历trie树, 因为我们的root引用的指向是不能改变的
TrieNode node = root;
//进行一个循环, 进行char[]的遍历以及trie数中元素的添加
for(char c : word.toCharArray()){
//如果trie中对应子节点数组中没有改字母, 则我们我们就要添加该字母到trie树中的对应位置
/*
这个时候注意: 我们这里标识某个位置有没有指定字母只需要判断children数组中对应位置有没有结点即可, 因为我们每个索引位置都对应的一个字母, 只要这个位置是有结点的
这个时候就是表示有对应的字母, 所以我们添加指定的某个字母的时候其实就是在对应位置添加一个节点即可
*/
if(node.children[c - 'a'] == null) node.children[c - 'a'] = new TrieNode();
//指针下移 (这一步操作就和我们的二叉树中的node = node.left 或者是node = node.right的功能还是一样的)
node = node.children[c - 'a'];
}
//最终遍历完数组之后退出for循环之后node是指向了最后一个结点的, 我们这个节点位置表示的是一个单词(因为此时我们的操作就是添加单词), 所以这个时候我们一定要将这个结点的isWord属性修改为true
node.isWord = true;
}
//查询单词的方法, 如果存在目标单词就返回true, 如果不存在目标单词就返回一个false
public boolean search(String word){
//因为我们查询单词的时候要遍历trie树, 所以我们需要定义一个临时变量用于遍历
TrieNode node = root;
for(char c : word.toCharArray()){
if(node.children[c - 'a'] == null) return false;
//指针下移
node = node.children[c - 'a'];
}
//即使这些字母都存在于trie数中, 这个时候我们也不能直接返回true, 因为这个时候我们是判断是否存在指定单词, 我们最终要判断这个查询的字符串是否是一个单词, 如果真的是单词再返回true
return node.isWord;
}
//查询前缀的方法, 如果存在目标前缀就返回true
public boolean startsWith(String prefix){
//因为我们查询单词的时候要遍历trie树, 所以我们需要定义一个临时变量用于遍历
TrieNode node = root;
for(char c : prefix.toCharArray()){
if(node.children[c - 'a'] == null) return false;
//指针下移
node = node.children[c - 'a'];
}
//这个时候我们是查找指定的前缀, 这个时候前缀不是一个单词, 只要对应的字符都按照顺序出现在我们的trie树中, 那么我们就返回一个true
return true;
}
}
//测试代码
public static void main(String[] args) {
Trie trie = new Trie();
trie.insert("hello");
boolean he = trie.startsWith("he");
System.out.println(he);
boolean hello = trie.search("hello");
System.out.println(hello);
}
字典树每个结点都需要用一个数组来存储子节点的指针, 即便某个结点只有两三个子节点, 但依然需要一个完整大小的数组, 所以, 字典树是比较消耗内存的, 也就是空间复杂度较高
将末尾一些只有一个子节点的结点, 可以进行合并, 但是增加了编码的难度, 如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQWXa7nI-1669121402687)(E:\非凡英才\数据结构(java)]\数据结构图解\Trie的缩点优化(空间复杂度优化).png)