您的位置:首页 > 编程语言 > C语言/C++

字典树的详解与实现

2013-07-14 18:39 405 查看
字典树,用来干嘛的?查找方便呗,给你一百万个字符串,然后再给你一个字符串,问你这个字符串在这一百万个出现多少次?怎么破?暴力估计太费时间了,仅仅一次查找比较就要100万次,现在有这样一个树结构,把出现的每个字母作为节点,建成一棵字典树,这样相同字符串都有公共前缀,这样我们只需要在节点处标记这个字符串出现的次数,然后查找的时候就直接查找出来了。在时间上,map的查找效率为O(logN),而字典树在查找字符串O(m),m为单词的字符个数,因为一般单词的字符个数不会超过30,这样字典树就非常省时间了。因为字典树对相同的字符串前缀至存储一次,而map却要每个都要存储,这样明显比map省内存。

 

(图片来自百度百科)


1,字典树的节点的存储结构

typedef struct Trie  //定义树的节点的存储结构
{
Trie *next[Max];  //Max为单词的个数
int v;            //当前字符串出现的次数
int flag;      //这个根据需求自己定义,我是用来标记当前字符串是出现过
}T;

 

2,初始化创建一个节点

Trie * NewNode()  //创建一个新节点,并把它的指向的下Max个节点初始化为NULL
{
Trie * temp=new Trie;
temp->v=1;
temp->flag=0;
for(int i=0;i<Max;i++)
temp->next[i]=NULL; //把它的子节点都指向空
return temp;
}

 

3,下面就是建树了

void createTrie(char *str)  //建字典树
{
int len=strlen(str);
int id=0;
Trie *p=root;   //定义个指针,指向根节点
for(int i=0;i<len;i++)
{
id=str[i]-'a';
if(p->next[id]==NULL)
p->next[id]=NewNode();
else (p->next[id]->v)++;
p=p->next[id];   //当前的p指的才是当前的插入节点
}
p->flag=1;
p->next[id]=NewNode();
//p->v=-1;
}


 

4,查找字典树

int findTrie(char *str)
{
int id=0;
Trie *p=root;

for(int i=0;i<strlen(str);++i)
{
id=str[i]-'a';
p=p->next[id];
if(p==NULL)
return 0;
}
if(p->flag)   //flag 为真,则这是个完整的单词
return p->v;
else
return 0;
}


 

5,释放树

由于我们是动态建树,开辟了内存,所以必须要释放

int dealTrie(Trie *T)
{
if(T==NULL)
return 0;
for(int i=0;i<Max;i++)
if(T->next[i]!=NULL)
dealTrie(T->next[i]);
free(T);
return 0;
}


讲的这应该差不多了,字典树的删除操作很少见,在这就不做过多阐述了

下面是一道应用题 NYOJ的http://http://acm.nyist.net/JudgeOnline/problem.php?pid=685 

下面是实现代码


//字典树

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>

using namespace std;
#define Max 28
typedef struct Trie //定义树的节点的存储结构
{
Trie *next[Max];
int v;
int flag;
}T;
Trie *root; //声明一个根节点

Trie * NewNode() //创建一个新节点,并把它的指向的下Max个节点初始化为NULL
{
Trie * temp=new Trie;
temp->v=1;
temp->flag=0;
for(int i=0;i<Max;i++)
temp->next[i]=NULL;
return temp;
}
void createTrie(char *str) //建字典树
{
int len=strlen(str);
int id=0;
Trie *p=root; //定义个指针,指向根节点
for(int i=0;i<len;i++)
{
if(str[i]=='+')
id=26;
else if(str[i]=='@')
id=27;
else id=str[i]-'a';
if(p->next[id]==NULL)
p->next[id]=NewNode();
else (p->next[id]->v)++;
p=p->next[id]; //当前的p指的才是当前的插入节点
}
p->flag=1;
p->next[id]=NewNode();
}
// 查找字符串
int findTrie(char *str)
{
int id=0;
Trie *p=root;

for(int i=0;i<strlen(str);++i)
{
if(str[i]=='+')
id=26;
else if(str[i]=='@')
id=27;
else id=str[i]-'a';

p=p->next[id];
if(p==NULL)
return 0;
}
if(p->flag)
return p->v;
else
return 0;
}
//由于是动态建树,所以要释放内存,否则后果不堪设想
int dealTrie(Trie *T) { if(T==NULL) return 0; for(int i=0;i<Max;i++) if(T->next[i]!=NULL) dealTrie(T->next[i]); free(T); return 0; }int main()
{
int T;
int n,m;
scanf("%d",&T);
while(T--)
{
root=NewNode();
scanf("%d %d",&n,&m);
while(n--)
{
char str[15];
scanf("%s",str);
createTrie(str);

}
while(m--)
{
char str1[15];
scanf("%s",str1);
printf("%d\n",findTrie(str1));
}
dealTrie(root);
}
return 0;
}


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息