什么是Trie树:
Trie树又叫字典树、单词查找树。
Trie树的作用:
快速存储和查找字符串集合的数据结构
存储:
类比为建树,先从根节点往下走,看有没有字符串开头的子节点,没有就创建一个子节点,有就往下走。当子节点跟字符串中字母不一样时,就创建一个新的子节点(字符串中每个字母按顺序进行存储,同样找子节点,有就往下走,没有就创建一个),当字符串中所有字母全部走完之后,在最后一个字母下标进行标记,表示是以这个字母结尾的字符串。
查找:
从根节点开始,一个一个子节点进行查找,如果字符串字母查找到最后一个字母都有子节点与之匹配并且字符串最后一个字母在当前线路上有结束标记,就可以查找到记录的树中有这个字符串;否则,不存在这个字符串。
下图方便理解:
存储:
查找:
详解实现:
存储操作怎么实现:
思路:
从根节点开始,用idx记录当前序号位置,如果子节点和当前字母一样,就继续找子节点;如果不一样,就创建这个字母的子节点,直到走到字符串最后一个字母,字符串出现次数cnt[p]++。
代码实现:
public static void insert(char[] str){
int p = 0;
for (int i = 0; i < str.length; i++) {
int u = str[i] - 'a';
if(son[p][u] == 0){
son[p][u] = ++idx;
}
p = son[p][u];
}
cnt[p]++;
}
查询操作怎么实现:
思路:
从根节点开始,如果子节点序号位置为0,证明没有这个子节点,直接返回0;如果当前字母的子节点的序号位置不为0,证明这个字母的子节点存在,继续向下找,直到最后一个字母,返回字符串出现的次数。
代码实现:
public static int query(char str[]){
int p = 0;
for (int i = 0; i < str.length; i++) {
int u = str[i] - 'a';
if(son[p][u] == 0){
return 0;
}
p = son[p][u];
}
return cnt[p];
}
题目
题解完整代码(Java)
import java.util.Scanner;
public class Main {
static int N = 100010;
static int[][] son = new int[N][26];//由于出现字母范围是小写字母,一个结点最多可向外扩展26条边,26个分支(子节点).son[][] 存的是下标编号idx
static int[] cnt = new int[N];//以当前点结尾的单词有多少个
static int idx;//当前用到了哪个下标,下标是0的点,既是根节点,又是空节点
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
while (n-- > 0){
String s = sc.next();
if(s.equals("I")){
char[] str = sc.next().toCharArray();
insert(str);
}else {
char[] str = sc.next().toCharArray();
System.out.println(query(str));
}
}
}
//存储/插入操作
public static void insert(char[] str){
int p = 0;//从根节点开始遍历
for (int i = 0; i < str.length; i++) {//对于字符串中每一个字母进行遍历
int u = str[i] - 'a';//每一个子节点(字母)对应的数字编号【a~z ——> 0~25】
if(son[p][u] == 0){//由于idx初始化为0,而我们添加的时候++idx表示编号从1开始,所以son[][] 为0,表示当前线路没有这个字母的子节点,就创建子节点
son[p][u] = ++idx;//子节点son[][]存的是下标编号
}
p = son[p][u];//向下一行推进
}
cnt[p]++;//所有字母遍历完成之后,这个字符串的最后一个字母下标进行标记并记录这个字符串的出现次数
}
//查询操作
public static int query(char str[]){
int p = 0;//从根节点开始遍历
for (int i = 0; i < str.length; i++) {
int u = str[i] - 'a';//每一个子节点(字母)对应的数字编号【a~z ——> 0~25】
if(son[p][u] == 0){//如果子节点中下标编号不存在的话,表示这个字符串从当前编号开始就不存在了,不需要继续进行查找了,return 0结束查找
return 0;
}
p = son[p][u];//向下一行进行推进
}
return cnt[p];//这个字符串的所有字母都按组成顺序被查找到了,返回这个字符串存储的次数
}
}
Trie树的学习要掌握模型的特征,并实现存储和查找功能,详解可看题解代码的注释。