字典树及其应用
字典树定义
Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
字典树有3个基本性质:
1、根节点不包含字符,其余的每个节点都包含一个字符;
2、从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
3、每个节点的所有子节点包含的字符都不相同。
字典树的结构一般如下图所示:
字典树的建立和查询
字典树的建立适合查询同步的。
- #define MAX 26 //字符集的大小
- //结点定义
- typedef struct Node
- {
- struct Node *next[MAX]; //每个节点下面可能有MAX个字符
- int count; //以从根节点到当前节点的字符串为公共前缀的字符串数目
- }TrieNode,*TrieTree;
- //创建一棵仅有根节点的字典树
- TrieTree create_TrieTree()
- {
- TrieTree pTree = (TrieTree)malloc(sizeof(TrieNode));
- pTree->count = 0;
- int i;
- for(i=0;i<MAX;i++)
- pTree->next[i] = NULL;
- return pTree;
- }
- /*
- 插入字符串str到字典树pTree中
- 由于不可能改变根节点,因此这里不需要传入pTree的引用
- */
- void insert_TrieTree(TrieTree pTree,const char *str)//稍作修改可以变为查询
- {
- int i;
- TrieTree pCur = pTree; //当前访问的节点,初始指向根节点
- int len = strlen(str);
- for(i=0;i<len;i++)
- {
- int id = str[i] – ‘a’; //定位到字符应该在的位置
- if(!pCur->next[id])
- { //如果该字符应该在的位置为空,则将字符插入到该位置
- int j;
- TrieNode *pNew = (TrieNode *)malloc(sizeof(TrieNode));
- if(!pNew)
- {
- printf(“pNew malloc fail\n”);
- exit(-1);
- }
- pNew->count = 1;
- for(j=0;j<MAX;j++)
- pNew->next[j] = NULL;
- //指针后移,比较下一个字符
- pCur->next[id] = pNew;
- pCur = pCur->next[id];
- }
- else
- { //如果该字符应该在的位置不空,则继续比较下一个字符
- pCur = pCur->next[id];
- pCur->count++; //每插入一个字符,公共前缀数目就加1
- }
- }
- }
- //查询字典树中以str为公共前缀的字符串的个数
- int count_TrieTree(TrieTree pTree,char *str)
- {
- int i;
- TrieTree pCur = pTree;
- int len = strlen(str);
- for(i=0;i<len;i++)
- {
- int id = str[i] – ‘a’;
- if(!pCur->next[id])
- return 0;
- else
- pCur = pCur->next[id];
- }
- return pCur->count;
- }
字典树的应用
字典树的应用很广泛,下面是摘自百度百科的3个应用介绍:
1、在串快速检错中的应用
字典树的应用很广泛,下面是摘自百度百科的3个应用简介:
1、字典树在串的快速检索中的应用。
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。在这道题中,我们可以用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。
2、字典树在“串”排序方面的应用
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。
3、字典树在最长公共前缀问题的应用
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为最近公共祖先问题。
4、在海量数据处理方面的应用
- 寻找热门查询:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复读比较高,虽然总数是1千万,但是如果去除重复和,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。
(1) 请描述你解决这个问题的思路;
(2) 请给出主要的处理流程,算法,以及算法的复杂度。
复杂度分析
Trie树的平均高度h为len,所以Trie树的查询复杂度为O(h)=O(len)。
建立trie的复杂度为O(n*len)。