Tre树(字典树)数据结构详解(图解)及模板

了解这个数据结构以前咱们须要了解它能被用来作什么ios

字典树又称单词查找树,Tire树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不只限于字符串),因此常常被搜索引擎系统用于文本词频统计。它的优势是:利用字符串的公共前缀来减小查询时间,最大限度地减小无谓的字符串比较,查询效率比哈希树高。web

说到底,字典树就是用来查询公共前缀的一个工具,延伸的话能够用来进行串匹配,词频统计等,也是学AC自动机的前置技能.数组

所谓的字典树,其实就是一个n叉树数据结构


咱们对于每一个字母,若是有公共前缀的,咱们找到它的前缀,在后面不一样的部分创建不一样的子节点,好比说apple,appear,appxy 三个不一样的单词,公共前缀为app,因此建树以下:
在这里插入图片描述
若是下面有一个单词为apzl的话,建树以下
在这里插入图片描述
也就是只注意前缀相同的部分,后面即便有同样的字母也从新创建子节点app


那么问题来了,单词的第一个字母不止A啊,应该怎么办?
其实不难,学过二分图的应该能想出来:设置一个超级源点,咱们在第一个字母上面再设一个超级源点,这样计算的时候不考虑他就好了,这样第一层就能够和下面的子节点同样创建了
如图所示(ps:此图有误,apply应该为appxy)
在这里插入图片描述svg


又是喜(sang)闻(xin)乐(bing)见(kuang)的代码环节了
我的因为ACM的缘由,就只放数组实现的板子了,(反正懂原理了指针版的也挺简单的)工具

因为数组不能动态开内存,因此咱们就得采用模拟的形式了,这里其实用了一点并查集的思想,各位客官看下图
在这里插入图片描述
因为不能动态分配内存,同时字典树又是比较耗费空间的,因此咱们的内存分配尽量大,开一个二维数组tire[maxn][26],而后tire[i][j] = k 表明编号为i的节点的第j个孩子是编号为k的节点,这里的j一般指当前位的字母A-Z而后关于编号,咱们这里的存树方式是:若是要生成新节点,则编号++,不然编号不动,因此如上图,APPLY的对应编号应该为1,2,3,4,10,11;
同时有:搜索引擎

tire[1]['A'-'A'] = 2;
tire[2]['P'-'A'] = 3;
tire[3]['P'-'A']  = 10;
tire[10]['X'-'A'] = 11;
tire[11]['Y'-'A'] = 0;

这样,查找的时候利用并查集的思想不断向下查找便可spa

代码以下3d

/*头文件能够忽略,只是一些经常使用的宏*/
#include <map>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#define _mem(a,b) memset(a,0,(b+3)<<2)
#define fori(a) for(int i=0;i<a;i++)
#define forj(a) for(int j=0;j<a;j++)
#define ifor(a) for(int i=1;i<=a;i++)
#define jfor(a) for(int j=1;j<=a;j++)
#define mem(a,b) memset(a,b,sizeof(a))
#define IN freopen("in.txt","r",stdin)
#define OUT freopen("out.txt","w",stdout)
#define IO do{\ ios::sync_with_stdio(false);\ cin.tie(0);\ cout.tie(0);}while(0)
#define mp(a,b) make_pair(a,b);
using namespace std;
typedef long long ll;
const int maxn =  1e5;
const int INF = 0x3f3f3f3f;
const int inf = 0x3f;
const double EPS = 1e-7;
const double Pi = acos(-1);
const int MOD = 1e9+7;
int Tire[maxn][26];
char str[2000005];
bool v[maxn];
string s;
int cnt = 1;
//建树,每输入一个单词到s里面就调用_insert()就好
void _insert(){
    int root = 0;
    fori(s.size()){
        int next = s[i] - 'A';
        if(!Tire[root][next])
            Tire[root][next] = ++cnt;
        root = Tire[root][next];
    }
    v[root] = true;//这里用了一个标记数组表示该点存在一个完整的单词,好比说`app`和`apple`
    					 //在最后一个`p`的位置就会被标记true
}

//查找最长公共前缀
int _find(char bufs[],int leng){
    int root = 0;
    int cns = 0;
    int next;
    int res = 0;
    fori(leng){
        next = bufs[i] - 'A';
        if(Tire[root][next] == 0)
            break;
        root = Tire[root][next];
        cns++;
        if(v[root])
            res = cns;
    }
    return res;
}
相关文章
相关标签/搜索