当前位置:   article > 正文

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

tire数据结构

了解这个数据结构之前我们需要了解它能被用来做什么

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

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

所谓的字典树,其实就是一个n叉树


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


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


又是喜(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;
同时有:

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

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

代码如下

  1. /*头文件可以忽略,只是一些常用的宏*/
  2. #include <map>
  3. #include <queue>
  4. #include <cstdlib>
  5. #include <cmath>
  6. #include <cstdio>
  7. #include <string>
  8. #include <cstring>
  9. #include <fstream>
  10. #include <iostream>
  11. #include <sstream>
  12. #include <algorithm>
  13. #define _mem(a,b) memset(a,0,(b+3)<<2)
  14. #define fori(a) for(int i=0;i<a;i++)
  15. #define forj(a) for(int j=0;j<a;j++)
  16. #define ifor(a) for(int i=1;i<=a;i++)
  17. #define jfor(a) for(int j=1;j<=a;j++)
  18. #define mem(a,b) memset(a,b,sizeof(a))
  19. #define IN freopen("in.txt","r",stdin)
  20. #define OUT freopen("out.txt","w",stdout)
  21. #define IO do{\
  22. ios::sync_with_stdio(false);\
  23. cin.tie(0);\
  24. cout.tie(0);}while(0)
  25. #define mp(a,b) make_pair(a,b);
  26. using namespace std;
  27. typedef long long ll;
  28. const int maxn = 1e5;
  29. const int INF = 0x3f3f3f3f;
  30. const int inf = 0x3f;
  31. const double EPS = 1e-7;
  32. const double Pi = acos(-1);
  33. const int MOD = 1e9+7;
  34. int Tire[maxn][26];
  35. char str[2000005];
  36. bool v[maxn];
  37. string s;
  38. int cnt = 1;
  39. //建树,每输入一个单词到s里面就调用_insert()就好
  40. void _insert(){
  41. int root = 0;
  42. fori(s.size()){
  43. int next = s[i] - 'A';
  44. if(!Tire[root][next])
  45. Tire[root][next] = ++cnt;
  46. root = Tire[root][next];
  47. }
  48. v[root] = true;//这里用了一个标记数组表示该点存在一个完整的单词,比如说`app`和`apple`
  49. //在最后一个`p`的位置就会被标记true
  50. }
  51. //查找最长公共前缀
  52. int _find(char bufs[],int leng){
  53. int root = 0;
  54. int cns = 0;
  55. int next;
  56. int res = 0;
  57. fori(leng){
  58. next = bufs[i] - 'A';
  59. if(Tire[root][next] == 0)
  60. break;
  61. root = Tire[root][next];
  62. cns++;
  63. if(v[root])
  64. res = cns;
  65. }
  66. return res;
  67. }

转载于:https://www.cnblogs.com/bestsort/p/10588835.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/727776
推荐阅读
相关标签
  

闽ICP备14008679号