字典树讲解
来源:互联网 发布:ipad清理空间软件 编辑:程序博客网 时间:2024/09/21 11:15
Trie树就是字典树,核心思想就是用空间来换时间。
举个栗子:(转)
现在有 100000 个长度不超过 10 的单词。对于每一个单词,我们要判断他有没有出现过,如果出现了,第一次出现在第几个位置。
这题当然可以用hash来解决,但是这里要介绍的是 trie 树。在某些方面它的用途更大。比如说对于某一个单词,要询问它的前缀是否出现过。这样 hash 就不好解决了,而用 trie 还是很简单。
现在回到例子中,如果我们用最原始的方法,对于每一个单词,我们都要去查找它前面的单词中是否有它。那么这个算法的复杂度就是O(n^2)。显然对于100000的范围难以接受。现在我们换个思路想。假设我要查询的单词是abcd,那么在他前面的单词中,以b,c,d,f之类开头的我显然不必考虑。而只要找以a开头的中是否存在abcd就可以了。同样的,在以a开头中的单词中,我们只要考虑以b作为第二个字母的……这样一个树的模型就渐渐清晰了……
假设有b,abc,abd,bcd,abcd,efg,hii 这6个单词,我们构建的树就是这样的。
对于每一个节点,从根遍历到他的过程就是一个单词,如果这个节点被标记为红色,就表示这个单词存在,否则不存在。
那么,对于一个单词,我只要顺着他从根走到对应的节点,再看这个节点是否被标记为红色就可以知道它是否出现过了。把这个节点标记为红色,就相当于插入了这个单词。
这样一来我们询问和插入可以一起完成,所用时间仅仅为单词长度,在这一个样例,便是10。
我们可以看到,trie树每一层的节点数是26^i 级别的。所以为了节省空间。我们用动态链表,或者用数组来模拟动态。空间的花费,不会超过单词数×单词长度。
模版一:(统计是否存在相同前缀)
#include <cstdio>#include <cstring>#include <malloc.h>#include <iostream>using namespace std;#define MAXN 10typedef struct Trie{ int v;//根据需要变化 Trie *next[MAXN]; //next是表示每层有多少种类的数,如果只是小写字母,则26即可, //若改为大小写字母,则是52,若再加上数字,则是62了}Trie;Trie* root;void createTrie(char *str){ int len = strlen(str); Trie *p = root, *q; for(int i = 0; i < len; i++) { int id = str[i]-'0'; if(p->next[id] == NULL) { q = (Trie *)malloc(sizeof(Trie)); q->v = 1;//初始v==1 for(int j = 0; j < MAXN; j++) q->next[j] = NULL; p->next[id] = q; p = p->next[id]; } else { // p->next[id]->v++; p = p->next[id]; } } p->v = -1;//若为结尾,则将v改成-1表示}int findTrie(char *str){ int len = strlen(str); Trie *p = root; for(int i = 0; i < len; i++) { int id = str[i]-'0'; p = p->next[id]; if(p == NULL) //若为空集,表示不存以此为前缀的串 return 0; if(p->v == -1) //字符集中已有串是此串的前缀 return -1; } //return p->v; return -1; //此串是字符集中某串的前缀}int dealTrie(Trie* T){ //动态字典树,有时会超内存,这是就要记得释放空间了 if(T==NULL) return 0; for(int i = 0; i < MAXN; i++) { if(T->next[i]!=NULL) dealTrie(T->next[i]); } free(T); return 0;}int main(){ int t, n; char str[15]; scanf("%d",&t); while(t--) { int flag = 0; root = (Trie *)malloc(sizeof(Trie)); for(int i = 0; i < MAXN; i++) root->next[i] = NULL; scanf("%d",&n); for(int i = 0; i < n; i++) { scanf("%s",str); if(findTrie(str) == -1) { flag = 1; continue; } createTrie(str); } if(flag) printf("NO\n"); else printf("YES\n"); dealTrie(root); } return 0;}
模板二:(统计前缀相同数量)
#include <cstdio>#include <cstring>#include <malloc.h>#include <iostream>using namespace std;#define MAXN 26typedef struct Trie{ int v;//根据需要变化 Trie *next[MAXN]; //next是表示每层有多少种类的数,如果只是小写字母,则26即可, //若改为大小写字母,则是52,若再加上数字,则是62了}Trie;Trie *root;void createTrie(char *str){ int len = strlen(str); Trie *p = root, *q; for(int i = 0; i < len; i++) { int id = str[i]-'a'; if(p->next[id] == NULL) { q = (Trie *)malloc(sizeof(Trie)); q->v = 1;//初始v==1 for(int j = 0; j < MAXN; j++) q->next[j] = NULL; p->next[id] = q; p = p->next[id]; } else { p->next[id]->v++; p = p->next[id]; } } // p->v = -1;//若为结尾,则将v改成-1表示}int findTrie(char *str){ int len = strlen(str); Trie *p = root; for(int i = 0; i < len; i++) { int id = str[i]-'a'; p = p->next[id]; if(p == NULL) //若为空集,表示不存以此为前缀的串 return 0; // if(p->v == -1) //字符集中已有串是此串的前缀 // return -1; } return p->v; //return -1; //此串是字符集中某串的前缀}int dealTrie(Trie* T){ //动态字典树,有时会超内存,这时就要记得释放空间了 if(T==NULL) return 0; for(int i = 0; i < MAXN; i++) { if(T->next[i]!=NULL) dealTrie(T->next[i]); } free(T); return 0;}int main(){ char str[15]; root = (Trie *)malloc(sizeof(Trie)); for(int i = 0; i < MAXN; i++) root->next[i] = NULL; while(gets(str) && str[0]!='\0') createTrie(str); memset(str, 0, sizeof(str)); while(scanf("%s", str) != EOF) { int ans = findTrie(str); printf("%d\n", ans); } return 0;}
- 字典树讲解
- 字典树 模板+讲解
- 字典树(讲解+模版)
- 字典树 讲解+模版
- 字典树讲解
- 字典树讲解
- Tree字典树讲解
- 字典树讲解+模板
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模版)
- 字典树(讲解+模板)
- ASP.NET 的亲密朋友之--JavaScript
- ASP常见的安全漏洞
- Knight Moves
- C#——标志枚举示例学习
- 浅析ASP内置组件
- 字典树讲解
- Linux——安装中文输入法
- 函数控制最小化窗口
- 表中的列与现有主键或UNIQUE约束不匹配
- leetcode第一刷_Add Two Numbers
- Java学习笔记2:理解运行例程
- ios性能提升
- 编程之美--树
- C语言函数calloc