前缀树(字典树) Trie tree
它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
前缀树的实现:
注意:调用的结构一定要单独写出来,否则吃内存,比如TrieNode
public class Tree {
public static class TrieNode{
//代表通过这个节点的次数
public int path;
//代表以这个节点为终点的次数
public int end;
public TrieNode[] nexts;
//当数量很多时,可以用哈希表,HashMap<Char,Node> nexts,表示字符以及对应的下一个节点
//希望每一条路是有序组织的,可以有序表TreeMap<Char, Node> nexts;
public TrieNode(){
path = 0;
end = 0;
//26位分别代表以a~z开头的路径
nexts = new TrieNode[26];
}
}
public static class Trie{
private TrieNode root;
public Trie(){
//一开始就创建了头节点
root = new TrieNode();
}
//加入
public void insert(String word){
if (word == null){
return;
}
char[] chs = word.toCharArray();
TrieNode node = root;
node.path++;
int index = 0;
//从左到右遍历字符
for (int i = 0; i < chs.length; i++){
//由字符,对应走那条路
index = chs[i] - 'a';
//没有对应的路径头节点,创建一个
if (node.nexts[index] == null){
node.nexts[index] = new TrieNode();//比的是地址
}
//到路径的下一个节点上
node = node.nexts[index];
node.path++;
}
//在哪里结束,节点对应的结束值+1
node.end++;
}
//查找word出现过几次
public int search(String word){
if (word == null){
return 0;
}
char[] chs = word.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++){
index = chs[i] - 'a';
//提前返回了说明就是没有
if (node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
//end值加入过几次就是几次
return node.end;
}
//所有加入的字符串中,有几个是以pre这个字符串作为前缀的
public int prefixNumber(String pre){
if (pre == null){
return 0;
}
char[] chs = pre.toCharArray();
TrieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++){
index = chs[i] - 'a';
if (node.nexts[index] == null){
return 0;
}
node = node.nexts[index];
}
return node.path;
}
//删除
public void delete(String word){
//确定树中确实加入过word,才删除
if (search(word) != 0){
char[] chs = word.toCharArray();
TrieNode node = root;
node.path--;
int index = 0;
for (int i = 0; i < chs.length; i++){
index = chs[i] - 'a';
//java中只要这个值标空,之后的节点都是自动释放的(系统维护)
if (--node.nexts[index].path == 0){
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
}
public static void main(String[] args) {
Trie trie = new Trie();
System.out.println(trie.search("zuo"));
trie.insert("zuo");
System.out.println(trie.search("zuo"));
trie.delete("zuo");
System.out.println(trie.search("zuo"));
trie.insert("zuo");
trie.insert("zuo");
trie.delete("zuo");
System.out.println(trie.search("zuo"));
trie.delete("zuo");
System.out.println(trie.search("zuo"));
trie.insert("zuoa");
trie.insert("zuoac");
trie.insert("zuoab");
trie.insert("zuoad");
trie.delete("zuoa");
System.out.println(trie.search("zuoa"));
System.out.println(trie.prefixNumber("zuo"));
}
}