Trie树——字典树

转载 2012年10月18日 23:21:57

1、 概述

Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。

Trie一词来自retrieve,发音为/tri:/ “tree”,也有人读为/traɪ/ “try”。

Trie树可以利用字符串的公共前缀来节约存储空间。如下图所示,该trie树用10个节点保存了6个字符串tea,ten,to,in,inn,int:

在该trie树中,字符串in,inn和int的公共前缀是“in”,因此可以只存储一份“in”以节省空间。当然,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存,这也是trie树的一个缺点。

Trie树的基本性质可以归纳为:

(1)根节点不包含字符,除根节点意外每个节点只包含一个字符。

(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。

(3)每个节点的所有子节点包含的字符串不相同。

2、 Trie树的基本实现

字母树的插入(Insert)、删除( Delete)和查找(Find)都非常简单,用一个一重循环即可,即第i 次循环找到前i 个字母所对应的子树,然后进行相应的操作。实现这棵字母树,我们用最常见的数组保存(静态开辟内存)即可,当然也可以开动态的指针类型(动态开辟内存)。至于结点对儿子的指向,一般有三种方法:

1、对每个结点开一个字母集大小的数组,对应的下标是儿子所表示的字母,内容则是这个儿子对应在大数组上的位置,即标号;

2、对每个结点挂一个链表,按一定顺序记录每个儿子是谁;

3、使用左儿子右兄弟表示法记录这棵树。

三种方法,各有特点。第一种易实现,但实际的空间要求较大;第二种,较易实现,空间要求相对较小,但比较费时;第三种,空间要求最小,但相对费时且不易写。

下面给出动态开辟内存的实现:

#define MAX_NUM 26
enum NODE_TYPE{ //"COMPLETED" means a string is generated so far.
	COMPLETED,
	UNCOMPLETED
};
struct Node {
	enum NODE_TYPE type;
	char ch;
	struct Node* child[MAX_NUM]; //26-tree->a, b ,c, .....z
};

struct Node* ROOT; //tree root

struct Node* createNewNode(char ch){
	// create a new node
	struct Node *new_node = (struct Node*)malloc(sizeof(struct Node));
	new_node->ch = ch;
	new_node->type == UNCOMPLETED;
	int i;
	for(i = 0; i < MAX_NUM; i++)
		new_node->child[i] = NULL;
	return new_node;
}

void initialization() {
	//intiazation: creat an empty tree, with only a ROOT
	ROOT = createNewNode(' ');
}

int charToindex(char ch) { //a "char" maps to an index<br>
	return ch - 'a';
}

int find(const char chars[], int len) {
	struct Node* ptr = ROOT;
	int i = 0;
	while(i < len) {
		if(ptr->child[charToindex(chars[i])] == NULL) {
			break;
		}
		ptr = ptr->child[charToindex(chars[i])];
		i++;
	}
	return (i == len) && (ptr->type == COMPLETED);
}

void insert(const char chars[], int len) {
	struct Node* ptr = ROOT;
	int i;
	for(i = 0; i < len; i++) {
		if(ptr->child[charToindex(chars[i])] == NULL) {
			ptr->child[charToindex(chars[i])] = createNewNode(chars[i]);
		}
		ptr = ptr->child[charToindex(chars[i])];
	}
	ptr->type = COMPLETED;
}

3、 Trie树的高级实现

可以采用双数组(Double-Array)实现。利用双数组可以大大减小内存使用量,具体实现细节见参考资料(5)(6)。

4、 Trie树的应用

Trie是一种非常简单高效的数据结构,但有大量的应用实例。

(1) 字符串检索

事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。

举例:

@  给出N 个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

@  给出一个词典,其中的单词为不良单词。单词均为小写字母。再给出一段文本,文本的每一行也由小写字母构成。判断文本中是否含有任何不良单词。例如,若rob是不良单词,那么文本problem含有不良单词。

(2)字符串最长公共前缀

Trie树利用多个字符串的公共前缀来节省存储空间,反之,当我们把大量字符串存储到一棵trie树上时,我们可以快速得到某些字符串的公共前缀。

举例:

@ 给出N 个小写英文字母串,以及Q 个询问,即询问某两个串的最长公共前缀的长度是多少?

解决方案:首先对所有的串建立其对应的字母树。此时发现,对于两个串的最长公共前缀的长度即它们所在结点的公共祖先个数,于是,问题就转化为了离线(Offline)的最近公共祖先(Least Common Ancestor,简称LCA)问题。

而最近公共祖先问题同样是一个经典问题,可以用下面几种方法:

1. 利用并查集(Disjoint Set),可以采用采用经典的Tarjan 算法;

2. 求出字母树的欧拉序列(Euler Sequence )后,就可以转为经典的最小值查询(Range Minimum Query,简称RMQ)问题了;

(关于并查集,Tarjan算法,RMQ问题,网上有很多资料。)

(3)排序

Trie树是一棵多叉树,只要先序遍历整棵树,输出相应的字符串便是按字典序排序的结果。

举例:

@ 给你N 个互不相同的仅由一个单词构成的英文名,让你将它们按字典序从小到大排序输出。

(4) 作为其他数据结构和算法的辅助结构

如后缀树,AC自动机等

5、 Trie树复杂度分析

(1) 插入、查找的时间复杂度均为O(N),其中N为字符串长度。

(2) 空间复杂度是26^n级别的,非常庞大(可采用双数组实现改善)。

6、 总结

Trie树是一种非常重要的数据结构,它在信息检索,字符串匹配等领域有广泛的应用,同时,它也是很多算法和复杂数据结构的基础,如后缀树,AC自动机等,因此,掌握Trie树这种数据结构,对于一名IT人员,显得非常基础且必要!

7、 参考资料

(1)wiki:http://en.wikipedia.org/wiki/Trie

(2) 博文《字典树的简介及实现》:

http://hi.baidu.com/luyade1987/blog/item/2667811631106657f2de320a.html

(3) 论文《浅析字母树在信息学竞赛中的应用》

(4)  论文《Trie图的构建、活用与改进》

(5)  博文《An Implementation of Double-Array Trie》:

http://linux.thai.net/~thep/datrie/datrie.html

(6) 论文《An Efficient Implementation of Trie Structures》:

http://www.google.com.hk/url?sa=t&source=web&cd=4&ved=0CDEQFjAD&url=http%3A%2F%2Fciteseerx.ist.psu.edu%2Fviewdoc%2Fdownload%3Fdoi%3D10.1.1.14.8665%26rep%3Drep1%26type%3Dpdf&ei=qaehTZiyJ4u3cYuR_O4B&usg=AFQjCNF5icQbRO8_WKRd5lMh-eWFIty_fQ&sig2=xfqSGYHBKqOLXjdONIQNVw

————————————————————————————————————-

更多关于数据结构和算法的介绍,请查看:数据结构与算法汇总

————————————————————————————————————-

原创文章,转载请注明: 转载自董的博客

本文链接地址: http://dongxicheng.org/structure/trietree/


经典算法——hihocoder#1014 : Trie树(字典树)

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,...

中级篇——字典树(Trie树)

Trie树也叫字典树,查新效率高且适用于字符串查找。相关例题HDU 1671,1251,1075,1247 字典树由链表构成,以查询英文字母组成的例题为例。建立字典树时,每个节点都有26个子节点,代表...

Trie树(字典树)HDU——1251

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀...
  • lyysr
  • lyysr
  • 2015年07月13日 12:08
  • 152

C++——字典树(Trie树)例题——Phone List(POJ3630)(HDU1671)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 28678   Ac...

字典树的简介及实现(Trie树)

Trie,又称字典树、单词查找树,是一种树形结构,用于保存大量的字符串。它的优点是:利用字符串的公共前缀来节约存储空间。 相对来说,Trie树是一种比较简单的数据结构.理解起来比较简单,正所谓简单的...

hiho #1014 : Trie树 (字典树的建立和查找)

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,...

Trie树(字典树):应用于统计和排序

转载这篇关于字典树的原因是看到腾讯面试相关的题:就是在海量数据中找出某一个数,比如2亿QQ号中查找出某一个特定的QQ号。。 有人提到字典树,我就顺便了解下字典树。 [转自:http...
  • wzy0754
  • wzy0754
  • 2017年04月10日 15:44
  • 330

trie树(字典树)java实现

public class Trie{ private Node root; public Trie(){ root = new Node(' '); } ...

Trie树|字典树的简介

Trie树|字典树的简介 Trie,又称字典树、单词查找树,是一种树形结构,用于保存大量的字符串,其核心思想是空间换时间。它的优点是:利用字符串的公共前缀来节约存储空间。 相对来说,Tri...
  • cai0538
  • cai0538
  • 2011年11月10日 15:51
  • 673

Hihicoder 题目1 : Trie树 经典字典树

题目:点击打开链接    原博客:点击打开链接 Hihicoder 题目1 : Trie树(字典树,经典题) 题目1 : Trie树 时间限制:10...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Trie树——字典树
举报原因:
原因补充:

(最多只允许输入30个字)