字典树(trie)简介以及两种实现
来源:互联网 发布:linux 建立用户 编辑:程序博客网 时间:2024/05/15 06:11
字典树简介:
字典树(trie)是用来存储字符串集合的一种数据结构。
对于一个字符串构成的集合,最普通的存储方式是直接开一个数组存储,每个元素是一个字符串。而字典树则利用了不同字符串之间可能有相同前缀这一特点,节省了存储空间,同时在查询和插入时可以获得前缀相关的信息。一个典型的字典树如下图:
我们可以看到,每一个字符串对应了从根出发的一条路径。为了记录哪些路径才是真正在集合中的字符串,每个节点需要一个end标记,表示当前节点是否是一个字符串的终点。
字典树的实现:
一般来说字典树是一个有根树,每个点的儿子的数量是不确定的。在字符集为无穷集的情况下需要转化成一颗二叉树来实现。经典的转化方法是左儿子右兄弟转化(left-child right-sibling binary tree),如下图所示:
但是在一般情况下字符集是一个有限集,比如电话号码包含0-9,单词包含a-z。在空间允许的情况下可以直接开一个数组记录所有儿子。
实现方法一:静态数组
代码(来自POJ 3630):
/* PROG: POJ3630 PROB: trie using array (fixed size dictionary)*/#include <cstdio>#include <cstring>using namespace std;#define DEBUG 1#define LOG(...) do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while(0)#define MAXN 100005int trie[MAXN][10], L;char mark[MAXN], ans;char buf[20];void init(void) { L = 1; memset(trie, 0, sizeof(trie)); memset(mark, 0, sizeof(mark)); ans = true;}int main(void) { int Z; scanf("%d", &Z); while (Z--) { init(); int n; scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%s", buf); if (!ans) continue; int r = 0, t; for (int j = 0; buf[j]&&ans; ++j) { if (mark[r]) { ans = false; break; } t = buf[j]-'0'; if (!trie[r][t]) trie[r][t] = L++; r = trie[r][t]; } for (int j = 0; j < 10; ++j) if (trie[r][j]) { ans = false; break; } if (mark[r]) ans = false; mark[r] = 1; } if (ans) printf("YES\n"); else printf("NO\n"); } return 0;}
这里数组trie[r][i]可以理解成一个函数trie(r, i),它表示节点r的第i个儿子(对应第i个字符)的位置(即下标)。插入时需要插入的字符串在trie中走过一条路径,r表示当前所走到的位置。trie[r][t]为0表示r节点还没有第i个儿子,于是在trie末尾新增节点,并将这个节点作为r的第t个儿子。L记录数组trie中下一个要加入的节点的位置。
实现方法二:利用vector
有时数据较大,使用静态数组可能导致MLE,这时可以用vector。
代码(来自HDU2328)
/* PROG: HDU2328 PROB: trie*/#include <cstdio>#include <cstring>#include <vector>#include <algorithm>using namespace std;#define DEBUG 1#define LOG(...) do { if (DEBUG) fprintf(stderr, __VA_ARGS__); } while(0)#define MAXN 4005char P[MAXN][205];char ans_buf[205];struct Node { int next[26]; int cnt, curr; Node() : cnt(0), curr(0) { memset(next, 0, sizeof(next)); }};int main(void) { int N; while (scanf("%d", &N), N) { for (int i = 0; i < N; ++i) scanf("%s", P[i]); vector<Node> trie; trie.push_back(Node()); int ans = 0; for (int k = 0; k < N; ++k) { for (int i = 0; P[k][i]; ++i) { int r = 0; for (int j = 0; P[k][i+j]; ++j) { int x = P[k][i+j]-'a'; if (!trie[r].next[x]) { trie[r].next[x] = trie.size(); trie.push_back(Node()); } r = trie[r].next[x]; if (trie[r].cnt<k) break; if (trie[r].curr<=k) { trie[r].curr = k+1; trie[r].cnt += 1; } if (k==N-1 && j+1 >= ans) { bool flag = false; if (j+1 > ans) flag = true; else { char tmp[205]; strncpy(tmp, P[k]+i, j+1); tmp[j+1] = '\0'; if (strcmp(tmp, ans_buf)<0) flag = true; } if (flag) { ans = j+1; strncpy(ans_buf, P[k]+i, j+1); ans_buf[j+1] = '\0'; } } } } } if (ans) printf("%s\n", ans_buf); else printf("IDENTITY LOST\n"); } return 0;}这里struct Node表示trie的一个结点,next[]是该节点各个儿子的位置。实现原理与静态数组时一样的,只是换成了vector,不需要提前预留足够内存。与指针实现相比,这种方法不需要手动释放内存。
0 0
- 字典树(trie)简介以及两种实现
- trie字典树实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现(转)
- Trie树|字典树的简介及实现(转)
- Trie树|字典树的简介及实现(转)
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树/字典树的简介及实现
- Trie树|字典树的简介及实现
- Trie树|字典树的简介及实现
- 编程规范1
- struct 成员的对齐方式
- 简单工厂和工厂方法模式Java示例
- 警告:针对Windows Patch Bundles 11.2.0.4.8到11.2.0.4.161018 版本的db,需要进行scn 检查.
- angularJS搭建简单应用程序的基本架构分析
- 字典树(trie)简介以及两种实现
- ElasticSearch {"error":"MapperParsingException[failed to parse];JsonParseException[Unexpected charac
- Java语言的关键字(保留字)
- 取消电脑关机提示是否强制关机
- C语言中.h和.c文件解析(很精彩)
- QuickxDev插件(一) 简介
- JavaScript 数据类型
- 117. Populating Next Right Pointers in Each Node II 非常精炼的代码,值得好好体会
- Web 前端趋势