Trie树

Trie树,又称字典树,前缀树,单词查找树。是字符串算法中一个比较基础的结构。在字符串查找方面有着线性时间的查找速度,是因为查找时间与Trie中的数据总量无关,只与待查找的字符串的长度有关。

字典树可以应用在多数字符串查找问题上,
比如说,给定一个非常大的文本,文本中每一行是一个单词,然后查询文本中是否包含某个单词,或者询问某个单词出现的次数。
再比如Trie + KMP算法就构成了AC自动机,可以实现多模式匹配问题。
当然首先,需要学会如何构建一棵Trie树。

Trie树的思想是利用词的公共前缀进行存储,这也正是树结构天生自带的优势,两个词具有公共前缀就意味着具有相同的父节点,而公共前缀就是从根节点到父节点这条路径所表示的内容。
Trie树的构建有array和linked-list两种,本文只介绍利用数组构建的方法。

Trie树节点

要构建一棵Trie树,首先应该解决的问题是,如何定义树节点。
假设Trie树只存储英文单词,只由26个小写英文字母组成,那么从一个节点出发,就有26种可能,也就是每个节点都有26个孩子节点(如果某个节点表示的是字符a,那么紧接着它的字符可能是a,b,c,d,e,…,z中的任何一个,因为英文单词有很多种)。

注:但是因为开辟的大小是事先规定好的,所以能够存储的范围非常有限。如果想要存储中文字符,可以使用《双数组Trie数》。

如图,每个节点表示一个英文字母,同时每个节点又有26个孩子节点,这些孩子节点分别表示从a到z的英文字母。这样,当从根节点沿着一条路径走下来后,将所有经过的节点表示的字母连接起来,就是一个完整的英文单词。

另外需要注意的是,构造Trie树时是每次插入一个完整的单词,所以只有当沿着某条路径走到特定节点后,连接起来的单词才是完整的单词,所以在每个节点中需要有一个bool型变量记录从根节点到当前结点这条路径表示的单词是否存在。

这样就可以为每一个树节点都开辟一个数组来存储孩子节点指针,像这样:

const size_t LETTER_SIZE = 26;
class TrieNode
{
public:TrieNode(const char& pAlpha = '\0'):m_pAlpha(pAlpha),m_pExist(false),m_pString(""){m_pNext = new TrieNode*[LETTER_SIZE];for(size_t i = 0; i < LETTER_SIZE; ++i)m_pNext[i] = NULL;}char m_pAlpha;bool m_pExist;  //记录从根节点到当前结点所构成的字符串是词典中的单词string m_pString;   //记录从根节点到当前结点所构成的字符串(单词),只有当m_pExist为true时该变量才有实际意义TrieNode **m_pNext;  //存储孩子节点指针的数组
}

在实际应用中,还可以根据需要为TrieNode增加多个成员变量,比如说

size_t m_pNextSize; //记录next数组中有多少个非空指针,即有多少个孩子不是NULL
size_t m_pCount; //在统计某个单词出现次数时使用

定义中将m_pNext数组中的每一元素都设置成NULL,表示这个节点没有孩子节点,也就是没有其它英文字母在它的后面。而且为了简便,可以认为表示a到z的孩子节点在next数组中是按顺序存储的,这样因为小写字母a到z的ASCII码是从97开始的,所以在m_pNext数组内想要确定哪个单词存在,就可以直接判断m_pNext[pAlpha - 97]是否是NULL即可。pAlpha可以是从a到z的任意字符。
像这样:

//想要判断pNode节点后面是否还有表示字符b的节点
if(pNode->m_pNext['b' - 97] != NULL)return true;
elsereturn false;

注:对于上面的第二张图,根节点的左孩子节点表示字符a,它的26个孩子节点中只有表示字符b,d,f的节点存在。所以很显然利用数组表示的Trie会存在大量的空间浪费。
另外可能也注意到了Trie树的根节点root,它不表示任何字符,只用来分出不同的字符。

对于Trie树,构建操作主要就是将词典中的单词拆成一个个字符,每个字符申请一个节点,后一个字符作为前一个字符的孩子。当申请完一个单词的所有节点后,需要把最后一个节点的m_pExist设置成true,表示从根到目前节点所表示的字符串在词典中存在。

插入函数

在Trie树的类中,使用insert()函数实现上述对每个单词处理然后添加的操作

class Trie
{
public:Trie();~Trie();void insert(const string& pKey); bool exist(const string& pKey);
private:TrieNode *m_pRoot;
}

插入函数的思路如下:
1.先判断是否已经申请了前缀部分的节点,因为具有公共前缀的单词只有一份前缀。例如上面的”abc”,”ada”和”af”,三者具有相同的前缀’a’,所以在添加”abc”后添加”ada”时,就不需要再为第一个字符’a’申请节点了,直接移动到字符’a’,为表示字符’a’的节点申请表示字符’d’的孩子。”af”也是如此。
2.不断遍历,如果存在当前要申请的节点,则不用再次申请内存,直接移动到那个位置。
3.为最后一个节点的exist变量赋值为true。

代码实现如下:

void Trie::insert(const string& pKey)
{TrieNode* pNode = m_pRoot; //从根节点开始寻找是否已经申请了某些节点for(size_t i = 0; i < pKey.length(); ++i){char pAlpha = pKey.at(i);//如果当前结点没有表示pAlpha的孩子节点时,申请节点if(pNode->m_pNext[pAlpha - 97] == NULL){TrieNode *ppNode = new TrieNode(pAlpha);pNode->m_pNext[pAlpha - 97] = ppNode;}//移动到表示pAlpha的节点,继续申请pNode = pNode->m_pNext[pAlpha - 97];}//将最后一个节点的m_pExist变量设置为true,表示单词存在pNode->m_pExist = true;pNode->m_pString = pKey;
}

判断某个字符串是否出现在词典中

insert函数是用于构造Trie树,而exist函数则是为了解决给定文本文件,每行为一个单词,然后给定一个单词问是否在其中出现的问题。首先需要将文本文件读入,将每一行的单词使用insert函数插入到Trie树中,然后进行查询。因为使用的是数组,而某个字符是否存在只需要判断当前结点是否有表示这个字符的孩子节点即可,所以查询的速度非常快,也只有要查询的字符串的长度有关。

bool Trie::exist(const string& pKey)
{TrieNode *pNode = m_pRoot;for(size_t i = 0; i < pKey.length(); ++i){char pAlpha = pKey.at(i);if(pNode->m_pNext[pAlpha - 97] != NULL){pNode = pNode->m_pNext[pAlpha - 97];}else{return false;}}return true;
}

问题

由于每个节点的next数组不可能都有元素存在,而更可能的情况是每个节点的next数组的元素都比较少,造成了大量的空间浪费,要想解决这一问题,可以考虑用链表将孩子节点连接起来,但是在查询的过程中就会比较耗时。
另一种解决办法是采用双数组Trie树,仅用两个数组描述的字典树,同时可以处理各种字符,比较实用。

注:
图一来自于http://www.cnblogs.com/en-heng/p/6265256.html

数据结构-----Trie树相关推荐

  1. 字符串匹配数据结构 --Trie树 高效实现搜索词提示 / IDE自动补全

    文章目录 1. 算法背景 2. Trie 树实现原理 2.1 Trie 树的构建 2.2 Trie树的查找 2.3 Trie树的遍历 2.4 Trie树的时间/空间复杂度 2.5 Trie 树 Vs ...

  2. 0x16.基本数据结构 — Trie树(字典树)+ A C 自 动 机

    目录 用TrieTrieTrie树来处理整数异或问题是真的舒服! 一.TrieTrieTrie树 TrieTrieTrie的基本操作 0.初始化 1.插入 2.检索 二.TrieTrieTrie树例题 ...

  3. java单词匹配算法_前端学数据结构与算法(八): 单词前缀匹配神器-Trie树的实现及其应用...

    前言 继二叉树.堆之后,接下来介绍另外一种树型的数据结构-Trie树,也可以叫它前缀树.字典树.例如我们再搜索引擎里输入几个关键字之后,后续的内容会自动续上.此时我们输入的关键词也就是前缀,而后面的就 ...

  4. 字符串匹配算法 -- AC自动机 基于Trie树的高效的敏感词过滤算法

    文章目录 1. 算法背景 2. AC自动机实现原理 2.1 构建失败指针 2.2 依赖失败指针过滤敏感词 3. 复杂度及完整代码 1. 算法背景 之前介绍过单模式串匹配的高效算法:BM和KMP 以及 ...

  5. 数据结构之Trie树

    1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...

  6. 数据结构与算法 / 字符串匹配 / Trie 树

    一.诞生原因 传统字符串比较时,需要将待比较的字符串与字符串集合中每一个串进行比较,结果比较浪费时间. Trie 树的发明就是为了解决上述问题. 二.基本信息 又称字典树,是一种树形结构,是一种哈希树 ...

  7. 数据结构-----基于双数组的Trie树

    Trie树简介 Trie树也称字典树,在字符串的查找中优势比较明显,适用于在海量数据中查找某个数据.因为Trie树的查找时间和数据总量没有关系,只和要查找的数据长度有关.比如搜索引擎中热度词语的统计. ...

  8. trie树查找前缀串_Trie数据结构(前缀树)

    trie树查找前缀串 by Julia Geist Julia·盖斯特(Julia Geist) A Trie, (also known as a prefix tree) is a special ...

  9. 数据结构八-Trie树

    文章出处:极客时间<数据结构和算法之美>-作者:王争.该系列文章是本人的学习笔记. 1 Trie树的使用场景 搜索引擎中的搜索词建议.当你在搜索引擎中输入词,搜索引擎提示给你一个词的列表, ...

最新文章

  1. RedHat Satellite 弃 MongoDB ,全面改用 PostgreSQL
  2. Spring(一)——用Spring IOC容器创建对象
  3. STL-红黑树源码实现
  4. Convolutional Neural Networks for Visual Recognition 8
  5. Error:unsupported class file version 52.0问题的解决
  6. gradle 构建应用流程_使用Gradle构建和应用AST转换
  7. v-for 获取数组key value_Vue之路 | 05von、vif、vfor
  8. Unity+SenseAR教程 | 用手势发射爱心2:加入发射方向【源码】
  9. C++_类和对象_C++多态_纯虚函数和抽象类---C++语言工作笔记072
  10. 关于JQuery的异步注册
  11. 深度学习推荐模型-WideDeep
  12. hmcl启动器怎么联机_hmcl启动器使用教程
  13. 给自动化专业的大学生的终极警钟,单片机、PLC、嵌入式等方向哪个才是香饽饽?
  14. ios、iphone越狱获取系统文件权限
  15. openstack云计算平台 1(认证服务、镜像服务)
  16. Ouino法国学习系统评价它是如何比其他学习软件
  17. C1 驾驶证考试科目二考试心得
  18. 水管工游戏(代码附带注释)2020.10.6
  19. 2022年起重机司机(限桥式起重机)考试试题模拟考试平台操作
  20. 基于Quartus-FPGA制作蜂鸣器的相关教程

热门文章

  1. Express中post请求req.body为空
  2. JDBC中的SPI实现
  3. react当中子组件改变父组件的状态
  4. SVN 如何提交 SO 库文件
  5. BOM的window对象的属性及其方法
  6. 编码 Unicode utf-8
  7. 怎样用java写一个简单的文件复制程序
  8. 解决安装kali 2020.1版本后的中文乱码问题:只需要安装中文字体(而不需要像之前版本那样需要选择locales和编码)。
  9. RxSwift之深入解析map操作符的底层实现
  10. unable to access ‘https://github.com/***.git‘: OpenSSL SSL_read: Connection was reset, errno 10054