首页 > 代码库 > 树学习 ---------字典树(Trie Tree)

树学习 ---------字典树(Trie Tree)

字典树,又称为字母数,前缀树等等,不仅可以存储字符,还可以存储数字等,

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。


它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。 字典树与字典很相似,当你要查一个单词是不是在字典树中,首先看单词的第一个字母是不是在字典的第一层,如果不在,说明字典树里没有该单词,如果在就在该字母的孩子节点里找是不是有单词的第二个字母,没有说明没有该单词,有的话用同样的方法继续查找.字典树不仅可以用来储存字母,也可以储存数字等其它数据


节点结构:

每个节点对应一个最大可储存字符数组。假设字典只存26个小写英文字母,那么每个节点下应该有一个长度为26的数组。换言说,可存的元素类型越多,单个节点占用内存越大。如果用字典树储存汉字,那么每个节点必须为数千个常用汉字开辟一个数组作为储存空间,占用的内存实在不是一个数量级。不过Trie树就是一种用空间换时间的数据结构,鱼和熊掌往往不可兼得。

建树细节:

  • 取要插入字符串的首个字符,从根节点的孩子节点开始,匹配当前字符是否已有节点,有则把指针指向该节点。无则为该字符创建节点,并把指针指向该新建节点。
  • 迭代。
  • 遇到要插入字符串末尾结束符时停止迭代,并把最后一个非’\0’字符对应的节点设为末端节点。

查找细节:
循环取要插入字符串的首个字符,从根节点的孩子节点开始,匹配当前字符是否已有节点,有则继续循环,无则返回False. 直至匹配到最后一个字符则完成查找。

树结构图:
我们用apps, apply, apple, append, back, basic, backen几英文单词创建树形结构:
trie

上图很容易看出,有相同前缀的英文单词,会合并在同一个节点,Trie树顺着一个个节点进行检索,

代码:

#include <stdio.h>
#include <string.h>
#define MAXN 100000
typedef struct TrieNode{
int nCount;
struct TrieNode *next[26];
}TrieNode;
TrieNode Memory[MAXN];
int allocp = 0;
void Init(TrieNode **pRoot)
{
*pRoot = NULL;
}


TrieNode *Build()
{
int i;
TrieNode *p;
p = &Memory[allocp++];
p -> nCount = 1;
for(int i = 0; i < 26; ++i)
{
p->next[i] = NULL;
}
return p;
}


void Insert(TrieNode **pRoot, char *s)
{
int i, k;
TrieNode *p;
if(!(p = *pRoot))
{
p = *pRoot = Build();
}
i =  0;
while(s[i])
{
k = s[i++] - ‘a‘;
if(!p->next[k])
{
p->next[k] = Build();
}
else
{
p->next[k] -> nCount++;
}
p = p->next[k];
}
}


int Search(TrieNode **pRoot, char *s)
{
int k;
TrieNode *p;
p = *pRoot;
int i = 0;
while(p && s[i])
{
k = s[i++] - ‘a‘;
p = p->next[k];
}
if(!p)
return 0;
else
return p->nCount;
}




int main()
{
char s[11];
TrieNode * Root = NULL;
printf("请输入想存储的字符串:\n");
while(gets(s) && s[0])
{
Insert(&Root, s);
}
printf("请输入查询的前缀:\n");
while(gets(s))
{
printf("%d\n", Search(&Root, s));
}
return 0;
}


树学习 ---------字典树(Trie Tree)