核心思想:
利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的
举个例子
上图是由
- am
- as
- tea
- too
- tooth
- two
构成的字典树。每个节点代表的单词是从根遍历到他的路径,标黄的是当前节点存在单词
代码实现:
struct Trie{ Trie *son[26]; bool w;//记录当前节点是否是单词; Trie(){ memset(son,0,sizeof(son)),w=0; } }root;
基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 每个节点的所有子节点包含的字符都不相同。
查询和插入
对于一个单词,我只要按照它的每个字母从根走到对应的节点,再看这个节点是否被标记为黄色就可以知道它是否出现过了。
如果没被标记,把这个节点标记为红色,就相当于插入了这个单词,这样查找和插入就可以一起实现。
代码实现:(这里字典树只支持大写字母)
int sum,word; void search(char s[]){//查找和插入 Trie *x=&root; for(int i=0;s[i];i++){ if(x->son[s[i]-'A']==NULL){//判断之前是否出现过 x->son[s[i]-'A']=new Trie;//创建新的节点 sum++;//sum总结点数+1 } x=x->son[s[i]-'A']; } if(!x->w)word,x->w=1;//word总单词数+1 }
因为空间是动态分配的,所以用完要释放
void node_free(Trie* x){//节点释放 for(int i=0;i<26;i++){ if(x->son[i]!=NULL)node_free(x->son[i]);//先释放子节点 } delete x; } void trie_free(){//空间释放 for(int i=0;i<26;i++){ if(root.son[i]!=NULL)node_free(root.son[i]),root.son[i]=NULL; } }
例题
代码
#include<cstdio> #include<cstring> using namespace std; struct Trie{ Trie *son[26]; bool w;//记录当前节点是否是单词; Trie(){ memset(son,0,sizeof(son)),w=0; } }root; int sum,word; void search(char s[]){//查找和插入 Trie *x=&root; for(int i=0;s[i];i++){ if(x->son[s[i]-'A']==NULL){//判断之前是否出现过 x->son[s[i]-'A']=new Trie;//创建新的节点 sum++; } x=x->son[s[i]-'A']; } if(!x->w)word,x->w=1;//sum总结点数+1 } void node_free(Trie* x){//节点释放 for(int i=0;i<26;i++){ if(x->son[i]!=NULL)node_free(x->son[i]);//先释放子节点 } delete x; } void trie_free(){//空间释放 for(int i=0;i<26;i++){ if(root.son[i]!=NULL)node_free(root.son[i]); } } char s[64]; int main(){ freopen("trie.in","r",stdin); freopen("trie.out","w",stdout); while(~scanf("%s",s)){ search(s); } trie_free(); printf("%d",sum+1); return 0; }