树和树结构(3) : Trie树(代码原创)
2015-12-27 13:00
190 查看
Trie树的名字有很多,比如字典树,前缀树等等。
Trie是词频分析和字典序排序的利器
一:概念
下面我们有and,as,at,cn,com这些关键词,那么如何构建trie树呢?
![](http://pic002.cnblogs.com/images/2012/214741/2012112521092438.png)
从上面的图中,我们或多或少的可以发现一些好玩的特性。
第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。
第三:每个单词的公共前缀作为一个字符节点保存。
二:使用范围
既然学Trie树,我们肯定要知道这玩意是用来干嘛的。
第一:词频统计。
可能有人要说了,词频统计简单啊,一个hash或者一个堆就可以打完收工,但问题来了,如果内存有限呢?还能这么玩吗?所以这里我们就可以用trie树来压缩下空间,因为公共前缀都是用一个节点保存的。
第二: 前缀匹配
就拿上面的图来说吧,如果我想获取所有以”a”开头的字符串,从图中可以很明显的看到是:and,as,at,如果不用trie树,你该怎么做呢?很显然朴素的做法时间复杂度为O(N^2) ,那么用Trie树就不一样了,它可以做到h,h为你检索单词的长度,可以说这是秒杀的效果。
举个例子:现有一个编号为1的字符串”and“,我们要插入到trie树中,采用动态规划的思想,将编号”1“计入到每个途径的节点中,那么以后我们要找”a“,”an“,”and”为前缀的字符串的编号将会轻而易举。
三:实际操作
到现在为止,我想大家已经对trie树有了大概的掌握,下面我们看看如何来实现。
1:定义trie树节点
为了方便,我也采用纯英文字母,我们知道字母有26个,那么我们构建的trie树就是一个26叉树,每个节点包含26个子节点。
2: 插入操作
插入操作可以充分体现c指针的简洁性,这是其他语言(如java)无法比拟的。
3: 查找操作
查找几乎和插入一样,不再赘述
4: 删除树
很简单,看代码
有了trie这个大杀器,我们就可以干很多事了。事实上,许多搜索引擎使用trie来做词频分析。只需要加入一个times就可以计算出现的次数。
这个完整文件是一个简单包装的trie类以及简单应用。
trie.cpp
Trie是词频分析和字典序排序的利器
一:概念
下面我们有and,as,at,cn,com这些关键词,那么如何构建trie树呢?
![](http://pic002.cnblogs.com/images/2012/214741/2012112521092438.png)
从上面的图中,我们或多或少的可以发现一些好玩的特性。
第一:根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
第二:从根节点到某一节点,路径上经过的字符连接起来,就是该节点对应的字符串。
第三:每个单词的公共前缀作为一个字符节点保存。
二:使用范围
既然学Trie树,我们肯定要知道这玩意是用来干嘛的。
第一:词频统计。
可能有人要说了,词频统计简单啊,一个hash或者一个堆就可以打完收工,但问题来了,如果内存有限呢?还能这么玩吗?所以这里我们就可以用trie树来压缩下空间,因为公共前缀都是用一个节点保存的。
第二: 前缀匹配
就拿上面的图来说吧,如果我想获取所有以”a”开头的字符串,从图中可以很明显的看到是:and,as,at,如果不用trie树,你该怎么做呢?很显然朴素的做法时间复杂度为O(N^2) ,那么用Trie树就不一样了,它可以做到h,h为你检索单词的长度,可以说这是秒杀的效果。
举个例子:现有一个编号为1的字符串”and“,我们要插入到trie树中,采用动态规划的思想,将编号”1“计入到每个途径的节点中,那么以后我们要找”a“,”an“,”and”为前缀的字符串的编号将会轻而易举。
三:实际操作
到现在为止,我想大家已经对trie树有了大概的掌握,下面我们看看如何来实现。
1:定义trie树节点
为了方便,我也采用纯英文字母,我们知道字母有26个,那么我们构建的trie树就是一个26叉树,每个节点包含26个子节点。
struct trie_node { trie_node *children[26]; bool data=false; //如果为true 说明有一个单词在这里结束 //低版本编译器可能需要写构造函数完成初值 };
2: 插入操作
插入操作可以充分体现c指针的简洁性,这是其他语言(如java)无法比拟的。
void insert (trie_node* &node, const char *str) { if (!node) node = new trie_node; if (*str=='\0') { //字符串结束 node->data = true; return; //插入 } insert(node->children[(*str)-'a'], str+1); //递归插入下一个字符 }
3: 查找操作
查找几乎和插入一样,不再赘述
bool find (trie_node* &node, const char *str) { if (!node) return 0; //节点为空一定找不到 if (*str=='\0') { if (node->data) return 1; else return 0; //字符串结束,查看是否有一个mark表示是一整个单词 //而不是一个单词的子串 } return find(node->children[(*str)-'a'], str+1); }
4: 删除树
很简单,看代码
void del (trie_node* &node) { if (node) { for (int i=0; i<26; i++) del (node->children[i]); delete node; } }
有了trie这个大杀器,我们就可以干很多事了。事实上,许多搜索引擎使用trie来做词频分析。只需要加入一个times就可以计算出现的次数。
这个完整文件是一个简单包装的trie类以及简单应用。
trie.cpp
相关文章推荐
- Java基础学习总结(6)——面向对象
- Java基础学习总结(6)——面向对象
- Java基础学习总结(6)——面向对象
- Java WebService 简单实例
- C++中string、int常见类型转换(含代码演示)
- Java基础学习总结(5)——多态
- Java基础学习总结(5)——多态
- Java基础学习总结(5)——多态
- Java基础学习总结(4)——对象转型
- Java基础学习总结(4)——对象转型
- Java基础学习总结(4)——对象转型
- Java基础学习总结(3)——抽象类
- Java基础学习总结(3)——抽象类
- Java基础学习总结(3)——抽象类
- C#_List<T>的使用
- Java基础学习总结(2)——接口
- Java基础学习总结(2)——接口
- Java基础学习总结(2)——接口
- Java多线程知识点大集合
- Struts2 服务器端验证流程