本文用尽量简洁的语言介绍一种树形数据结构 —— Trie树。

一、什么是Trie树

Trie树,又叫字典树前缀树(Prefix Tree)单词查找树键树,是一种多叉树结构。如下图:

上图是一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。从上图可以归纳出Trie树的基本性质:

  1. 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  2. 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
  3. 每个节点的所有子节点包含的字符互不相同。

通常在实现的时候,会在节点结构中设置一个标志,用来标记该结点处是否构成一个单词(关键字)。

可以看出,Trie树的关键字一般都是字符串,而且Trie树把每个关键字保存在一条路径上,而不是一个结点中。另外,两个有公共前缀的关键字,在Trie树中前缀部分的路径相同,所以Trie树又叫做前缀树(Prefix Tree)

二、Trie树的优缺点

Trie树的核心思想是空间换时间,利用字符串的公共前缀来减少无谓的字符串比较以达到提高查询效率的目的。

优点

  1. 插入和查询的效率很高,都为O(m),其中 m 是待插入/查询的字符串的长度。

    • 关于查询,会有人说 hash 表时间复杂度是O(1)不是更快?但是,哈希搜索的效率通常取决于 hash 函数的好坏,若一个坏的 hash 函数导致很多的冲突,效率并不一定比Trie树高。
  2. Trie树中不同的关键字不会产生冲突。

  3. Trie树只有在允许一个关键字关联多个值的情况下才有类似hash碰撞发生。

  4. Trie树不用求 hash 值,对短字符串有更快的速度。通常,求hash值也是需要遍历字符串的。

  5. Trie树可以对关键字按字典序排序。

缺点

  1. 当 hash 函数很好时,Trie树的查找效率会低于哈希搜索。

  2. 空间消耗比较大。

三、Trie树的应用

1、字符串检索

检索/查询功能是Trie树最原始的功能。思路就是从根节点开始一个一个字符进行比较:

  • 如果沿路比较,发现不同的字符,则表示该字符串在集合中不存在。
  • 如果所有的字符全部比较完并且全部相同,还需判断最后一个节点的标志位(标记该节点是否代表一个关键字)。
struct trie_node
{
bool isKey; // 标记该节点是否代表一个关键字
trie_node *children[26]; // 各个子节点
};

2、词频统计

Trie树常被搜索引擎系统用于文本词频统计 。

struct trie_node
{
int count; // 记录该节点代表的单词的个数
trie_node *children[26]; // 各个子节点
};

思路:为了实现词频统计,我们修改了节点结构,用一个整型变量count来计数。对每一个关键字执行插入操作,若已存在,计数加1,若不存在,插入后count置1。

注意:第一、第二种应用也都可以用 hash table 来做。

3、字符串排序

Trie树可以对大量字符串按字典序进行排序,思路也很简单:遍历一次所有关键字,将它们全部插入trie树,树的每个结点的所有儿子很显然地按照字母表排序,然后先序遍历输出Trie树中所有关键字即可。

4、前缀匹配

例如:找出一个字符串集合中所有以ab开头的字符串。我们只需要用所有字符串构造一个trie树,然后输出以a->b->开头的路径上的关键字即可。

trie树前缀匹配常用于搜索提示。如当输入一个网址,可以自动搜索出可能的选择。当没有完全匹配的搜索结果,可以返回前缀最相似的可能。

5、作为其他数据结构和算法的辅助结构

如后缀树,AC自动机等。

四、Trie树的实现

这里为了方便,我们假设所有的关键字都由 a-z 的字母组成。下面是 trie 树的一种典型实现:

#include <iostream>
#include <string>
using namespace std; #define ALPHABET_SIZE 26 typedef struct trie_node
{
int count; // 记录该节点代表的单词的个数
trie_node *children[ALPHABET_SIZE]; // 各个子节点
}*trie; trie_node* create_trie_node()
{
trie_node* pNode = new trie_node();
pNode->count = 0;
for(int i=0; i<ALPHABET_SIZE; ++i)
pNode->children[i] = NULL;
return pNode;
} void trie_insert(trie root, char* key)
{
trie_node* node = root;
char* p = key;
while(*p)
{
if(node->children[*p-'a'] == NULL)
{
node->children[*p-'a'] = create_trie_node();
}
node = node->children[*p-'a'];
++p;
}
node->count += 1;
} /**
* 查询:不存在返回0,存在返回出现的次数
*/
int trie_search(trie root, char* key)
{
trie_node* node = root;
char* p = key;
while(*p && node!=NULL)
{
node = node->children[*p-'a'];
++p;
} if(node == NULL)
return 0;
else
return node->count;
} int main()
{
// 关键字集合
char keys[][8] = {"the", "a", "there", "answer", "any", "by", "bye", "their"};
trie root = create_trie_node(); // 创建trie树
for(int i = 0; i < 8; i++)
trie_insert(root, keys[i]); // 检索字符串
char s[][32] = {"Present in trie", "Not present in trie"};
printf("%s --- %s\n", "the", trie_search(root, "the")>0?s[0]:s[1]);
printf("%s --- %s\n", "these", trie_search(root, "these")>0?s[0]:s[1]);
printf("%s --- %s\n", "their", trie_search(root, "their")>0?s[0]:s[1]);
printf("%s --- %s\n", "thaw", trie_search(root, "thaw")>0?s[0]:s[1]); return 0;
}

对于Trie树,我们一般只实现插入和搜索操作。这段代码可以用来检索单词和统计词频。

个人站点:http://songlee24.github.com

Trie树(Prefix Tree)介绍的更多相关文章

  1. 分形树Fractal tree介绍——具体如何结合TokuDB还没有太懂,先记住其和LSM都是一样的适合写密集

    在目前的Mysql数据库中,使用最广泛的是innodb存储引擎.innodb确实是个很不错的存储引擎,就连高性能Mysql里都说了,如果不是有什么很特别的要求,innodb就是最好的选择.当然,这偏文 ...

  2. B树,B+树,红黑树应用场景AVL树,红黑树,B树,B+树,Trie树

    B B+运用在file system database这类持续存储结构,同样能保持lon(n)的插入与查询,也需要额外的平衡调节.像mysql的数据库定义是可以指定B+ 索引还是hash索引. C++ ...

  3. AVL树,红黑树,B-B+树,Trie树原理和应用

    前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作 ...

  4. [BinaryTree] AVL树、红黑树、B/B+树和Trie树的比较

    转自:AVL树.红黑树.B/B+树和Trie树的比较 AVL树 最早的平衡二叉树之一.AVL是一种高度平衡的二叉树,所以通常的结果是,维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应 ...

  5. Trie树(代码),后缀树(代码)

    Trie树系列 Trie字典树 压缩的Trie 后缀树Suffix tree 后缀树--ukkonen算法 Trie是通过对字符串进行预先处理,达到加快搜索速度的算法.即把文本中的字符串转换为树结构, ...

  6. 基于trie树的具有联想功能的文本编辑器

    之前的软件设计与开发实践课程中,自己构思的大作业题目.做的具有核心功能,但是还欠缺边边角角的小功能和持久化数据结构,先放出来,有机会一点点改.github:https://github.com/chu ...

  7. Prefix tree

    Prefix tree The trie, or prefix tree, is a data structure for storing strings or other sequences in ...

  8. BZOJ 3261 最大异或和 可持久化Trie树

    题目大意:给定一个序列,提供下列操作: 1.在数组结尾插入一个数 2.给定l,r,x,求一个l<=p<=r,使x^a[p]^a[p+1]^...^a[n]最大 首先我们能够维护前缀和 然后 ...

  9. Trie树(小)总结 By cellur925

    关于\(Trie\)树的详细介绍,还请移步这篇深度好文 基本操作 插入 void insert() { int p=0; int len=strlen(tmp+1); for(int i=1;i< ...

随机推荐

  1. 怎样在nexus 中 搜索到远程maven仓库中的jar 文件

    怎样在nexus 中 搜索到远程maven仓库中的jar 文件 url: http://www.oschina.net/question/95712_21999 点击Administration菜单下 ...

  2. viewer && ImageFlow 图片滚动组件 图片点击放大 可以滚轮放大缩小 viewer

    ImageFlow https://finnrudolph.com/products/imageflow https://github.com/countzero/ImageFlow http://w ...

  3. 06C#类

    C#类 1.2      类的继承 在1.3节,定义了一个描述个人情况的类Person,如果我们需要定义一个雇员类,当然可以从头开始定义雇员类Employee.但这样不能利用Person类中已定义的函 ...

  4. 指针函数(Pointer Function)和函数指针(Pointer to Function或Function Pointer)

    一.指针函数 1.解释:指针函数很好理解:简单来说,就是一个返回指针的函数,本质是一个函数.如: int fun(int x,int y);    //这是一个普通函数的声明,返回值是一个int类型, ...

  5. 零基础入门学习Python(14)--字符串:各种奇葩的内置方法

    前言 这节课我们回过头来,再谈一下字符串,或许我们现在再来谈字符串,有些朋友可能觉得没必要了,甚至有些朋友就会觉得,不就是字符串吗,哥闭着眼也能写出来,那其实关于字符串还有很多你不知道的秘密哦.由于字 ...

  6. Kali Linux 下载、引导、安装

    下载卡莉 Linux 官方镜像: https://www.kali.org/downloads/ 官方虚拟机镜像: https://www.offensive-security.com/kali-li ...

  7. token 的生成杂谈

    背景 很多时候我们需要用 token 来作为一些标识, 比如: 一个用户登录后的认证标识. 实现方式 md5 的方式: $v = 1; // 自己定义的 需要hash 的value 值 $key = ...

  8. day21 05 员工信息表

    day21 05 员工信息表 假设有一个员工信息表,里面有每个员工的名字,id,年龄,电话,还有他们所作的工作,而有时候我们并不需要所有的信息,而想根据某些条件,寻找符合条件即可,即筛选, 比如想要筛 ...

  9. stark组件之注册与路由系统(三)

    在文章stark组件前戏中已经提到过,django的注册功能是通过AdminSite的单例进行组册的,所以在这里也可以进行单例模式. class AdminSite(object): def __in ...

  10. Oracle创建 表空间 用户 给用户授权命令

    //创建表空间 create tablespace ACHARTSdatafile 'D:\oradata\orcl\ACHARTS01.DBF' size 800mautoextend on nex ...