转载的,学习哈。。嘿嘿
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 'z' - 'a' + 1
#define SLEN 11
struct tire {
struct tire *next[MAX];
char s[SLEN];
int isword;
} *root;
struct tire *init(void) {
struct tire *root = (struct tire *)malloc(sizeof(struct tire));
int i;
for (i = 0; i < MAX; i++) root->next[i] = NULL;
root->isword = 0;
return root;
}
void insert(char path[], char s[]) {
struct tire *t, *p = root;
int i, j, n = strlen(path);
for (i = 0; i < n; i++) {
if (p->next[path[i] - 'a'] == NULL) {
t = (struct tire *)malloc(sizeof(struct tire));
for (j = 0; j < MAX; j++) t->next[j] = NULL;
t->isword = 0;
p->next[path[i] - 'a'] = t;
}
p = p->next[path[i] - 'a'];
}
p->isword = 1;
strcpy(p->s, s);
}
char *find(char path[], int delflag) {
struct tire *p = root;
int i, n = strlen(path);
i = 0;
while (p && path[i]) p = p->next[path[i++] - 'a'];
if (p && p->isword) {
p->isword = delflag;
return p->s;
}
return NULL;
}
void del(struct tire *root) {
int i;
if (!root) return;
for (i = 0; i < MAX; i++)
if (root->next[i]) del(root->next[i]);
free(root->next[i]);
}
int main(void) {
char line[256], s1[256], s2[256];
char *s;
root = init();
while (gets(line)) {
if (sscanf(line, "%s %s", s1, s2) != 2) break;
insert(s2, s1);
}
while (scanf("%s", s1) == 1) {
s = find(s1, 1);
if (s == NULL) printf("eh/n");
else printf("%s/n", s);
}
return 0;
}
查找和插入可以用同一函数实现,改天研究哈嗯哪