字典树(Trie树):顾名思义是一种树形结构,属于哈希树的一种。应用于统计、排序、查找单词、统计单词出现的频率等。它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。
字典树的结构特点:根节点不代表任何字符。其他节点从当前节点回溯到根节点可以得到它代表的字符串。当然可以再节点上加一个记录该节点所代表单词的属性。字典树的形状如图:
字典树节点的结构:
节点包含26个指针,每个指针代表一个英文字符节点。26个英文字母那就需要26个指针。节点其他的属性可根据需要添加。一般包含一个记录该节点所代表字符出现次数的属性。
一种节点定义形式如下:
#define max 26
struct TrieNode
{
int count;//单词出现的次数
char* word;//节点表示那个单词
TrieNode* next[max];//指向26个字符节点的指针。
TrieNode():count(0),word(0)
{
for(int i=0;i<max;i++)
next[i]=NULL;
}
};
在TrieNode除了26个指针外还有一个记录当前节点所代表单词的属性,和一个记录该单词出现次数的属性。
字典树一般有插入和查找两个操作,很少用到删除某个节点的操作。
字典树实现代码:
class TrieTree
{
public:
TrieTree(void)
{
pRoot=new TrieNode();
}
~TrieTree(void)
{
ReleaseTrie(pRoot);
}
TrieTree& Insert(const char* s)
{
TrieInsert(pRoot,s);
return *this;
}
int Search(const char* s,int& count)
{
return TrieSearch(pRoot,s,count);
}
void PrintWordNum()
{
WordNum(pRoot);
}
private:
//插入一个新的单词
void TrieInsert(TrieNode* root,const char* s);
//查找某个单词是否存在
int TrieSearch(TrieNode* root,const char* s,int& count);
//释放字典树
void ReleaseTrie(TrieNode* root);
void WordNum(TrieNode* root);
private:
TrieNode* pRoot;
};
插入一个单词的代码:
现在是对单词的统计,所以仅对构成单词的节点,计数加1,当一个节点不为空,但计数为0,是仅代表它是某个单词的前缀。
void TrieTree::TrieInsert(TrieNode* root,const char* s)
{
if(s==NULL)
return ;
TrieNode* p=root;
int i=0;
while(s[i]!=0)
{
int index=tolower(s[i])-'a';
if(index<0||index>max)
return ;
if(p->next[index]==NULL)
{
TrieNode* newNode=new TrieNode();
p->next[index]=newNode;
}
p=p->next[index];
i++;
}
p->word=new char[strlen(s)+1];
strcpy(p->word,s);
p->count+=1;
}
查找一个单词出现次数的代码:
返回1表示查找成功,count获得单词出现次数。
int TrieTree::TrieSearch(TrieNode* root,const char* s,int& count)
{
if(s==NULL||root==NULL)
return 0;
TrieNode* p=root;
int i=0;
while(s[i]!=0&&p!=NULL)
{
int index=tolower(s[i])-'a';
if(index<0||index>max)
return 0;
p=p->next[index];
i++;
}
if(s[i]==0&&p&&p->count!=0)
{
count=p->count;
return 1;
}
else
{
count=0;
return 0;
}
}
释放字典树空间的代码:
void TrieTree::ReleaseTrie(TrieNode* root)
{
if(root==NULL)
return ;
for(int i=0;i<max;i++)
ReleaseTrie(root->next[i]);
if(root->word)
delete root->word;
delete root;
}
在对大量的单词使用Insert插入构建好字典树后,遍历整个树,就能的到单词出现的次数了。
void TrieTree::WordNum(TrieNode* root)
{
if(root==NULL)
return ;
if(root->word!=NULL)
{
cout<<root->word<<" :"<<root->count<<endl;
}
for(int i=0;i<max;i++)
WordNum(root->next[i]);
}