前缀树
前缀树,也就是字典树,用来搞单词统计或者匹配前缀时候很有帮助。树的节点可以用来保存一些辅助信息用来做题,而树枝则用来表示字符。
一般在刷题的时候,会遇到单词匹配或者单词统计之类的问题,此时的字符串会被限制为只是字母。这样的话树就更好来表示,就比如对于一个单词表[“a”, “aa”, “ab”, “ab” “b”, “ed”]可以用前缀树表示成下面这样
可以给节点附加pass和end分别表示有多少个字符串经过了该节点,以该节点上方的字符为结尾的字符串的个数。
代码如下:
//前缀树
public class trie_code {
//构造前缀树节点
public static class trieNode {
public int pass; //表示有多少个字符串通过这个节点
public int end; //表示有多少个字符串在这个节点上结束
public trieNode[] nexts; //0-26分别表示a-z,表示路径上的字符
//如果不局限于字母,这里可以用HashMap
public trieNode() {
pass = 0;
end = 0;
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.pass++;
int index = 0;
for (int i = 0; i < chs.length; i++) {
//获得a-z字符对应的0-26
index = chs[i] - 'a';
//之前没有通往这个字符的路,则新添加一个节点,表示到该字符的路径
if (node.nexts[index] == null) {
node.nexts[index] = new trieNode();
}
//切到沿这个字符走到的新节点处进行操作
node = node.nexts[index];
//到该字符的字符串也多了一个
node.pass++;
}
//循环结束后,node停留在字符串结束处,标识字符串在此处结束
node.end++;
}
//查询字符串加入过几次
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) {
trieNode node = root;
//所有的字符串都以null为前缀
if (pre == null) {
return node.pass;
}
char[] chs = pre.toCharArray();
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
//还没把pre走完前缀树里面就已经没有字符了,说明没有字符以pre为前缀
if (node.nexts[index] == null) {
return 0;
}
node = node.nexts[index];
}
return node.pass;
}
//将字符串从前缀树中删除
public void delete(String word) {
//前缀树中没有这个字符串,则无须删除
if (search(word) == 0) {
return;
}
char[] chs = word.toCharArray();
trieNode node = root;
int index = 0;
for (int i = 0; i < chs.length; i++) {
index = chs[i] - 'a';
node.pass--;
//如果通过该节点的字符串只有一个要删除的字符串,则把后面的节点断开
if (node.nexts[index].pass == 1) {
node.nexts[index] = null;
return;
}
node = node.nexts[index];
}
node.end--;
}
}
//测试上面的那个单词表
public static void main(String[] args) {
String[] str = new String[6];
str[0] = "a";
str[1] = "aa";
str[2] = "ab";
str[3] = "ab";
str[4] = "b";
str[5] = "ed";
Trie trie = new Trie();
for(int i=0; i<str.length; i++){
trie.insert(str[i]);
}
System.out.println(trie.search("ab"));
System.out.println(trie.prefixNumber("a"));
trie.delete("ab");
trie.delete(("a"));
System.out.println("-----------------");
System.out.println(trie.search("ab"));
System.out.println(trie.prefixNumber("a"));
}
}

129

被折叠的 条评论
为什么被折叠?



