概述

Trie树不同于通常的基于键比较的方法, 直接利用键的数字序列直接定位, 通常用于字符串匹配, 特别对公共前缀查找, 非常有效. 朴素的Trie树使用转移矩阵表示,简单易懂, 查找速度快(O(k), k表示键的长度).唯一的缺点是空间利用效率低下. 于是有两种压缩方案被提出, 一种叫后缀压缩, 把non-branching的后缀统一存储起来; 另一种使用链表来表示转移矩阵, 能有效压缩存储空间, 但由于线性查询, 又会造成了效率低下. 后来, 新的数据结构Triple Array Trie被提出, 既兼顾效率, 又考虑性能. 最后, Double Array Trie作为Triple Array Trie的改进版被提出.

引言

Trie 来源于单词 re**triev**e, 表示信息的获取. 它不同于基于键比较的查找方式, 它利用键的数字或字符序列直接look-at, 速度很快. 比如在字典查找中, 就用到了这个概念, 查找键(bachelor), 直接翻到字母B所在区域, 再进行进一步查找. 把这个概念加以推广就形成了Trie树.

转移矩阵

最简单的表示Trie树的方式莫过于转移矩阵了, 矩阵的行表示Key的字符集合, 矩阵的列表示状态(state), 当从state(1)开始, 通过字符B, 进入到 state(2), 可以给出以下定义

g(s, c) = t (其中s就是state1, c就是字符B, t就是state2)

下面表格中, 展示了4个Key的转移矩阵: bachelorbabybadgejar, 如果要查找bachelor, 可以从state(1)开始, Key的第1个字符是b, 于是进入state(2), Key的第2个字符是a, 于是进入state(4), Key的第3个字符是c, 于是找到bachelor.

Array-Structured Trie

实现中, Trie树可以作为一颗M叉树, 利用数组作为节点, 表示state; 利用字符作为下标, 链接到下一个数组或者子节点, 比如下图表示了4个key的Trie树: bachelorbabybadgejar. 根节点(s)通过字符j, 链接到子节点(t).

代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define TREE_WIDTH 256
#define WORD_LEN_MAX  128struct trie_node_t {int count;int pass;struct trie_node_t *next[TREE_WIDTH];
};static struct trie_node_t root={0,0, {NULL}};int insert(const char *word)
{struct trie_node_t *current;struct trie_node_t *new_node;if (word[0] == '\0') {return 0;}current = &root;for (int i=0; ; ++i) {if (word[i] == '\0') {break;}current->pass++;if (current->next[word[i]] == NULL) {new_node = (struct trie_node_t *)malloc(sizeof(struct trie_node_t));memset(new_node, 0, sizeof(struct trie_node_t));current->next[word[i]] = new_node;}current = current->next[word[i]];}current->count ++;return 0;
};int do_travel(struct trie_node_t *pRoot)
{static char word_dump[WORD_LEN_MAX];static int pos = 0;if (pRoot == NULL) {return 0;}if (pRoot->count > 0) {word_dump[pos] = '\0';fprintf (stdout, "word: %s, pass: %d, count: %d\n", word_dump, pRoot->pass, pRoot->count);}for (int i=0; i<TREE_WIDTH; ++i) {word_dump[pos++] = i;do_travel(pRoot->next[i]);pos--;}return 0;
}
int main(void)
{static char *text = "bechelor baby badge jar";char buffer[128];snprintf (buffer, 128, "%s", text);char *ptr = buffer;while (1) {char *word = strsep(&ptr, " ");if (word == NULL) {break;}if (word[0] == '\0') {continue;}insert(word);}do_travel(&root);// do freereturn 0;
}

list-structured Trie

基于数组结构Trie树, 空间消耗很大, 虽然可以把无子节点的链接置空, 以节约空间. 比如根节点(s)除了bj相关的链接指向子节点, 其余链接全部置为NULL. 但是数组空间的利用率依然低下, 比如根节点的利用率为1/13. 鉴于此, 有人认为, 应当压缩空链接, 于是就形成 list structured Trie.

链表节点的定义:

struct trie_node_t {char arc;struct trie_node_t *child;struct trie_node_t *sibling;
};

下图展示了4个Key(bachelorbabybadgejar)的list-structured存储结构, 如果要匹配baby, 首先定位到根节点, 然后顺着child链接,找到a节点, 接着通过child链接找到c节点, 因为c != b, 于是沿着sibling链接找到b节点, 最后找到y节点, 匹配成功.

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define TREE_WIDTH 256
#define WORD_LEN_MAX  128struct trie_node_t {int count;int pass;char arc;struct trie_node_t *child;struct trie_node_t *sibling;
};static struct trie_node_t root = {0, 0, 0, NULL, NULL};int insert(const char *word)
{struct trie_node_t *current;struct trie_node_t *prev;if (word[0] == '\0') {return 0;}current = &root;prev = current;int i = 0;while (1) {if (current == NULL) {current = new trie_node_t();current->pass = 0;current->count = 0;current->arc = word[i];current->child = NULL;current->sibling = NULL;prev->child = current;}while (current != NULL) {if (current->arc == word[i]) {break;}prev = current;current = current->sibling;}if (current == NULL) {current = new trie_node_t();current->pass = 0;current->count = 0;current->arc = word[i];current->child = NULL;current->sibling = NULL;prev->sibling = current;}i++;if (word[i] == '\0') {break;}current->pass++;prev = current;current = current->child;}current->count ++;return 0;
};int do_travel(struct trie_node_t *pRoot)
{static char word_dump[WORD_LEN_MAX];static int pos = 0;if (pRoot == NULL) {return 0;}if (pRoot == NULL) {return 0;}if (pRoot->count > 0) {word_dump[pos] = pRoot->arc;word_dump[pos+1] = '\0';fprintf (stdout, "word: %s, pass: %d, count: %d\n", word_dump, pRoot->pass, pRoot->count);}struct trie_node_t *current;for (current = pRoot; current != NULL; current = current->sibling) {word_dump[pos++] = current->arc;do_travel(current->child);pos--;}return 0;
}int main(void)
{static char *text = "bechelor baby badge jar";char buffer[128];snprintf (buffer, 128, "%s", text);char *ptr = buffer;while (1) {char *word = strsep(&ptr, " ");if (word == NULL) {break;}if (word[0] == '\0') {continue;}insert(word);}do_travel(root.sibling);// freereturn 0;
}

Reduced Tried(后缀压缩)

K表示key的集合, 在节点s中, 如果字符c能唯一的在K标识key, 那么

t = g(s, c)

节点t就被称为*separate node*. 从节点t到最后一个节点组成的字符串就称为节点t的*single string*, trie 树可以分成两个部分, 根节点到*separate node*还放在list-structured数据结构中, single string 另外存储在tail数组中. 从而进一步压缩空间.

假设 K = {bachelorbabybadgejar}, 从上文list-structured trie图中可以看出, bachelor 只需要存储b,a,c3个节点, 就能在K中唯一定位到bachelorc节点就称为*separate node*, 后缀helor其实就是*single string*, 直接存储在tail数组中.

总结

本文从基础出发, 介绍了Trie树的原理, 以及两种简单的压缩优化方案, 当然, 上面这些都比较简单, 真正复杂的是Triple-Array Trie, 特别是Double-Array Trie, 等了解清楚了, 再撰文以飨读者.

参考文档

  • An Efficient Implementation of Trie Structures
  • An Implementation of Double-Array Trie
  • The Art of Computer Programming Vol. 3, Sorting and Searching.

Trie Tree 介绍相关推荐

  1. C#,动态规划问题中基于单词搜索树(Trie Tree)的单词断句分词( Word Breaker)算法与源代码

    分词是自然语言处理的基础,分词准确度直接决定了后面的词性标注.句法分析.词向量以及文本分析的质量.英文语句使用空格将单词进行分隔,除了某些特定词,如how many,New York等外,大部分情况下 ...

  2. Trie Tree 实现中文分词器

    前言 继上一篇HashMap实现中文分词器后,对Trie Tree的好奇,又使用Trie Tree实现了下中文分词器.效率比HashMap实现的分词器更高. Trie Tree 简介 Trie Tre ...

  3. C++实现trie tree字典树(附完整源码)

    实现trie tree字典树 实现trie tree字典树算法的完整源码(定义,实现,main函数测试) 实现trie tree字典树算法的完整源码(定义,实现,main函数测试) #include ...

  4. LSM Tree介绍及其应用

    1. LSM Tree介绍 1.1 概念 ​B+树读效率高而写效率差:log型文件操作写效率高而读效率差:因此要在排序和log型文件操作之间做个折中,于是就引入了log-structed merge ...

  5. 字典树(Trie tree)

    Trie,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字 ...

  6. 红黑树 (Red-Black Tree) – 介绍

    介绍: 红黑树是一种自平衡二叉搜索树,其中每个节点都有一个额外的位,并且该位通常被解释为颜色(红色或黑色).这些颜色用于确保树在插入和删除期间保持平衡.虽然树的平衡并不完美,但减少搜索时间并将其保持在 ...

  7. k-d tree 介绍

    作为存取高维数据的一种数据结构,k-d tree 在静态查询和插入方面的效率还是很高的.本文在这里对 k-d tree 的内容作一些介绍,可能也会结合自己使用 k-d tree 的一些体验作一些点评. ...

  8. Centered Interval tree 介绍(中心区间树)

    参考:https://en.wikipedia.org/wiki/Interval_tree 百度很多关于interval tree和segment tree的感觉都有问题,于是去Wikipedia查 ...

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

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

最新文章

  1. windows监控——再见zmq
  2. 后台开发经典书籍--Linux多线程服务端编程:使用muduo C++网络库
  3. java dbrecord_JFinal 独创 Db + Record 模式
  4. Nacos部署中的一些常见问题汇总
  5. iPhone降价都救不回销量?苹果仍需努力!
  6. 形态学操作之提取水平与垂直直线
  7. spdy_buffer
  8. Python快速入门教材推荐!
  9. 超好用的搜索引擎推荐
  10. ccf201412-3集合竞价
  11. Android自定义相机自动对焦、定点对焦
  12. VM虚拟机局域网组网配置
  13. 光辉国际宣布陈兆丰先生为新任中国区总裁
  14. linux 统计字数 行数
  15. ps4帧数测试软件,《地平线:零之黎明》PS4/Pro帧数测试 优化极佳
  16. mpstat命令(linux cpu监控工具)
  17. 一个老鼠走迷宫问题的python解法
  18. 第三章习题2存款利息的计算
  19. css浮动布局自适应,CSS 几种常用自适应布局
  20. javascript添加addClass()方法

热门文章

  1. BIO、NIO、AIO详解
  2. RSD高分卫星数据处理能力提升——日正射处理数千景高分数据集
  3. 分步傅里叶算法_分步傅立叶算法(SSFFT),split-step fast Fourier transformation(SSFFT),音标,读音,翻译,英文例句,英语词典...
  4. Phase-Functioned Neural Networks for Character Control论文翻译(用于角色控制的相位功能神经网络)
  5. mysql 大量 time wait_mysql出现大量的TIME_WAIT解决办法
  6. 【毕业设计】时间序列的股票预测与分析系统 - python 大数据
  7. 决策树算法:对鸢尾花进行分类
  8. sklearn调库实现决策树算法
  9. Fiddler抓取手机数据包(315晚会上出现了它的影子。。。)
  10. [ZT]哈佛寓言13则