TRIE树
算法简介:
字典树,也叫trie树,是一种比较实用的数据结构,无论是在ACM竞赛的题目中,还是字符串相关的某些实际应用领域内,它都能发挥巨大的作用。
首先来看看字典树的本质是什么。它其实是一棵存储了很多字符串的树,这棵树上的每一条边就是某个或某些字符串中的一个字符,而从根节点到某一个特定节点所经过的一条路径上的所有边组成的就是字典树所保存的某一个字符串。不难看出,字典树就是一颗多叉树,它利用字符串的前缀来建立了这棵树,从而达到了节省存储空间(所有相同前缀都只存储一次),优化查询速度(查询单个单词是否存在的时间复杂度仅为该单词的长度)的目的。
个人理解:
快速的查询单词,统计前缀,但是需要牺牲大量的空间。
字典树的两个难点1、如何表达字典树的多叉属性 2、如何标记字符串的结束。其实这两个问题就是对应字典树的插入和查询操作。
建立的过程中跟普通叉树的建立过程很相似,这里要注意Next节点数组与flag的用法,Next非NULL即表示存在。flag表示代表前缀的个数,例如字典树里边有cbzzzz和 cbfff,则b节点的flag就等于2,有两个字符串公用到了cb这个前缀。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=26;
struct trie{
//代表当前节点可以延伸出来的边,数量可变
trie *Next[maxn];
//标记当前节点是否保存信息的结尾,也可以代表前缀的个数
int flag;
trie(){
flag=1;//初始化以该信息为前缀的信息个数!!!!!!!
//比如说cccsdd cccsvv 则cccs的flag就等于2
memset(Next,NULL,sizeof(Next));
}
}*root;
//1、插入操作
//将某一个信息插入字典树,其实就是依次将该信息的前缀信息保存在对应节点中,并修改相应标记的值即可。
void Insert(string s)
{
int len=s.size();
trie *p=root,*q;
//将s的每一个字符插入到trie树
for(int i=0;i<len;i++){
int id=s[i]-'a';
//如果没有边,则新建一个trie节点,产生一个新的边代表该字符
if(p->Next[id]==NULL){
q=new trie();
p->Next[id]=q;
p=p->Next[id];
}
//如果存在则继续往下走
else{
p=p->Next[id];
(p->flag)++;
}
}
}
//2、查询操作
//查询某个信息是否存在于字典树中,实质上也是将该信息的前缀信息与字典树上存储的对应位置的信息进行匹配,然后判断标记的值即可。
int Query(string s)
{
int len=s.size();
trie *p=root;
//在trie树上顺序搜索s的每一个字符
for(int i=0;i<len;i++){
int id=s[i]-'a';
p=p->Next[id];
//如果是空集,表示不存在以此为前缀的信息
if(p==NULL) return 0;
}
//返回以该信息为前缀的信息的个数
return p->flag;
}
//3、删除操作
//递归释放字典树的每一个节点占用的空间即可。
void Free(trie *t)
{
if(t==NULL) return ;
//释放trie树的每一个节点占用的内存
for(int i=0;i<maxn;i++){
if(t->Next[i]) free(t->Next[i]);
}
delete(t);
}
int main()
{
string s;
root=new trie();
cin>>s;
Insert(s);
Query(s);
free(root);
}