trie树总结

算法总结第4弹,今天来总结下字典树(Trie树),Trie树算是我学的第一个高级数据结构了吧,还是比较简单的。

Trie树

Trie树,又称字典树,是字典的一种存储方式,字典中的每一个单词在Trie树种体现为从根节点出发的路径,路径中每条边代表一个字母,将边连接起来便形成了对应的单词,如图,就是一颗Trie树,其中存储了ab,ac,bc,c,cd五个单词(其中加粗节点表示单词结尾节点)。

一:Trie树的基本概念

Trie树是由链接的节点所组成的数据结构,这些链接可能为空,也可能指向其他节点,每个节点只有一个指向它的节点,称之为父节点,(只有根节点例外),每个节点都有X条链接,其中X代表字符集的大小。每一个链接代表一个字符,指向下一个节点。对于每一个节点,从根节点到该节点的路径中包含的链接串就是该节点所表示的字符串,也就是字典树中的一个单词。在一般的应用中,我们常常在Trie树的节点中保存一个bool值flag表示该节点是否是一个单词的结尾。也就是说,我们从根节点按照对应的链接走到一个节点,发现该节点的flag值为真,那么表示当前的字符串就是字典树中保存的单词。(如下图,加粗节点的flag就为1,表示该节点是一个单词的结尾)因为从根节点到一个节点的路径是唯一的,所以Trie树种每一个节点唯一地表示一个字符串(根节点表示空串)。下面是Trie树节点的代码实现:

const int maxn=100010;//节点总数的最大值
const int knum=26;//字符集的大小
struct Trie
{
    struct Node
    {
        int next[knum];//next[i]表示该节点通过第i条边连接的节点编号
        int flag;//标记
        void INIT()//初始化节点
        {
            memset(next,0,sizeof(next));
            flag=0;
        }
    }node[maxn];
    int tot;//当前Trie树中节点的数量
}T;
void TRIE_INIT(Trie &T)//初始化Trie树和根节点
{
    T.tot=0;//将节点总数清0,0号点为根节点
    T.node[0].INIT();//初始化根节点
}

Trie树的节点数量和Trie树所存单词的总长度有关,设Trie树中所存单词总长度为n,则其空间复杂度为O(n*m),其中m为每一个节点所占空间,m的值取决于字符集knum的大小和标记的数量,对于不同的问题有不同的空间复杂度。

二:Trie树的基本操作

Trie树有两个基本操作,插入操作和查询操作,其中插入操作一般用来更新Trie树,

向Trie树中插入一个新的单词,查询操作一般是查询某个特定单词是否在Trie树中存在。

1. 插入操作

现在来介绍Trie树的插入操作,首先举个例子。假设插入字符串abc,Trie树现在是一颗空树,只有根节点存在,从根节点出发,依次按字符更新Trie树,第一个字符为a,因为目前根节点next数组均为空,不存在表示a的边,所以我们要新建一个节点,使得根节点通过a边连接到新建节点,同时我们进入新节点,判断下一个字符边是否存在,如果存在就直接运动到下一个节点,否则继续新建一个节点,直到到达最后一个字符c。插入字符串abc的过程如下图所示。

插入完字符串abc后,为了表示这个字符串为Trie树中的一个单词,我们还需要将插入操作中最后一个节点的flag设为1,表示它是单词abc的结尾。

现在如果要继续插入单词,如abd,那么同样要从根节点开始插入,由于根节点已经存在a边,所以不需要新建节点,直接移动到a节点即可,同理移动到ab节点,这时插入字符d,由于ab节点没有d边,所以要新建节点。最后还要标记新建立的节点为单词的结尾。插入abd的过程如下图所示。

插入单词的代码实现如下。

//向Trie树中插入单词str
void INSERT(Trie &T,char *str)
{
    int p=0;//p表示当前节点的标号,0表示根节点的标号
    int len=strlen(str);  //len表示插入单词的长度
    for(int i=0;i<len;i++)
    {
        int index=str[i]-'a';//index是下一个字符的编号,这里默认第一个字符为a
        if(T.node[p].next[index]==0)
        {
            T.node[++T.tot].INIT();//新建一个节点
            T.node[p].next[index]=T.tot;//将当前节点的index边连向新建立节点
        }
        p=T.node[p].next[index];//进入下一个节点
    }
    T.node[p].flag=1;//将该节点标记为单词的结尾
}

插入一个单词的时间复杂度为O(n),其中n为插入单词的长度。

插入多个单词和插入一个单词的思路是一致的,事实上我们可以将第一次插入看成是在一颗包含空串的Trie树上插入一个单词,所以插入多个单词就是多次在当前Trie树的基础上调用insert函数。

2. 查询操作

查询操作和插入类似,也是从根节点开始,不过查找不能新建节点,如果查找的过程中遇到空边表示查找的单词不在Trie树中,另外,如果查找完成,没有遇到空边,也不能说明查找单词一定存在,这时还要判断最后一个节点的flag标记是否为1,如果不为1,则依然表示查找的单词不在Trie树中(因为它是Trie树中一个单词的前缀),否则表示单词存在。

//在Trie树中查找单词str,若存在返回true,否则返回false
bool SEARCH(Trie &T,char *str)
{
    int p=0;//p表示当前节点的标号,0表示根节点的标号
    int len=strlen(str);//len表示插入单词的长度
    for(int i=0;i<len;i++)
    {
        int index=str[i]-'a';//index是下一个字符的编号,这里默认第一个字符为a
        if(T.node[p].next[index]==0)
        return false;
        p=T.node[p].next[index];
    }
    if(T.node[p].flag)
    return true;
    return false;
}

同插入操作,查询一个单词的时间复杂度为O(n),其中n为查询单词的长度。

以上就是Trie树的基本操作,在实际应用中,常常会根据题目需要在节点中维护多个信息,下面举几个例子来说明Trie树的应用。

时间: 2024-10-26 01:46:14

trie树总结的相关文章

poj3630 Phone List (trie树模板题)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26328   Accepted: 7938 Description Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogu

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

Trie树学习2

数组实现的Trie树 字符容量有限,可以使用链表实现更为大容量的Trie #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <map> #include <set> #include <algorithm> #include <cstdlib> #

trie树(字典树)

1. trie树,又名字典树,顾名思义,它是可以用来作字符串查找的数据结构,它的查找效率比散列表还要高. trie树的建树: 比如有字符串"ab" ,"adb","adc"   可以建立字典树如图: 树的根节点head不存储信息,它有26个next指针,分别对应着字符a,b,c等.插入字符串ab时,next['a'-'a']即next[0]为空,这是申请一个结点放在next[0]的位置,插入字符串db时,next['d'-'a']即next[3]

Trie树

Trie树,即字典树或单词查找树,主要用于大量字符串的检索.去重.排序等操作. 主要原理就是利用字符串的公共前缀建立一棵多叉树,牺牲空间换取时间. 1 //Trie树 2 #include <iostream> 3 #include <string> 4 using std::cin; 5 using std::cout; 6 using std::endl; 7 using std::string; 8 9 const int SIZE = 26; 10 const char B

bzoj4103异或运算 可持久化trie树

要去清华冬令营了,没找到2016年的题,就先坐一坐15年的. 因为n很小,就按照b串建可持久化trie树,a串暴力枚举. 其他的直接看代码. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; inline int read() { int x=0,f=1,ch=getchar(); while(ch<'0'||ch

hihoCoder 1014 Trie树

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字

HDU 1251 Trie树模板题

1.HDU 1251 统计难题  Trie树模板题,或者map 2.总结:用C++过了,G++就爆内存.. 题意:查找给定前缀的单词数量. #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b #define F(i,a,b

hiho一下 第二周&amp;第四周:从Trie树到Trie图

hihocoder #1014 题目地址:http://hihocoder.com/problemset/problem/1014 hihocoder #1036 题目地址: http://hihocoder.com/problemset/problem/1036 trie图其实就是trie树+KMP #1014trie树 #include<stdio.h> #include <algorithm> #include <cstring> #include <str

剑指Offer——Trie树(字典树)

剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高. Trie的核心思想是空间换时间.利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的. Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.可见,优