trie的原理是利用字符串集合中字符串的公共前缀来降低时间开销以达到提高效率的目的。
它具有以下性质:1,根结点不包含任何字符信息;2,如果字符的种数为n,则每个结点的出度为n(这样必然会导致浪费很多空间,这也是trie的缺点,我还没有想到好点的办法避免);3,查找,插入复杂度为O(n),n为字符串长度。
举一个例子,给50000个由小写字母构成的长度不超过10的单词,然后问某个公共前缀是否出现过。如果我们直接从字符串集中从头往后搜,看给定的字符串是否为字符串集中某个字符串的前缀,那样复杂度为O(50000^2),这样显然会TLE。又或是我们对于字符串集中的每个字符串,我们用MAP存下它所有的前缀。然后询问时可以直接给出结果。这样复杂度为O(50000*len),最坏情况下len为字符串最长字符串的长度。而且这没有算建立MAP存储的时间,也没有算用MAP查询的时间,实际效率会更低。但如果我们用trie的话,当查询如字符串abcd是否为某字符串的前缀时,显然以b,c,d....等不是以a开头的字符串就不用查找了。实际查询复杂度只有O(len),建立trie的复杂度为O(50000).这是完全可以接受的。
如给定字符串集合abcd,abd,cdd,efg,hij,hi六个字符串建立的trie tree如下图所示:
查找一个字符串时,我们只需从根结点按字符串中字符出现顺序依次往下走。如果到最后字符串结束时,对应的结点标记为红色,则该字符串存在;否则不存在。
插入时也只需从根结点往下遍历,碰到已存在的字符结点就往下遍历,否则,建立新结点;最后标记最后一个字符的结点为红色即可。
同时我们看到,如果字符的种类为n,则需要结点的个数为n级数。(谁有好办法降低空间开销,请告诉我)
#include <stdio.h> #include <stdlib.h>
#define KIND 26
typedef struct trie { int num; struct trie* next[KIND]; }TRIE;
void init_trie(TRIE* T) { int i = 0; T->num = 0; for(; i<KIND; i++) T->next[i] = NULL; }
void add_trie(TRIE** T, char* word) { int num = 0; int i = 0; if(*T == NULL) { *T = (TRIE*)malloc(sizeof(TRIE)); init_trie(*T); } TRIE* location = *T; while(word[i]!='\0') { num = *word-'a'; if(location->next[num]!=NULL) (location->num)++; else { TRIE* p = (TRIE*)malloc(sizeof(TRIE)); init_trie(p); location->next[num] = p; } i++; location = location->next[num]; } }
int find_trie(TRIE* T, char* word) { int i = 0; int num; int ret = 1; TRIE* location = T; while(word[i]!='\0') { num = *word-'a '; if(location->next[num]!=NULL) { i++; location = location->next[num]; } else { ret = 0; break; } } return ret; } int main(int argc, char *argv[]) { TRIE* T = NULL; int i = 0; char* word[] = {"abcd","abd","cdd","efg","hij","hi"}; for(;i<6;i++) add_trie(&T, word[i]); printf("%d\n",find_trie( T, "abd")); system("PAUSE"); return 0; }
|
阅读(910) | 评论(1) | 转发(0) |