字典树的详解与实现

字典树,用来干嘛的?查找方便呗,给你一百万个字符串,然后再给你一个字符串,问你这个字符串在这一百万个出现多少次?怎么破?暴力估计太费时间了,仅仅一次查找比较就要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;
}        


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值