1.概念
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
Trie树与二叉搜索树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀(prefix),也就是这个节点对应的字符串,而根节点对应空字符串。
2.优点:
利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
3.基本性质:
a.根节点不包含任何字符,除根节点外每一个节点都只包含一个字符;
b.从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串; 每个节点的所有子节点包含的字符都不相同。
c.字典树的节点并不真正存储该字符。
4.功能:
a.查找某单词
b.统计某单词出现的次数
c.对单词进行排序
d.删除(比较少用)
5.应用:
1.串的快速检索
给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。
在这道题中,我们可以用数组枚举,用哈希,用字典树,先把熟词建一棵树,然后读入文章进行比较,这种方法效率是比较高的。
2.“串”排序
给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出
用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。
3.最长公共前缀
对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为当时公共祖先问题。
6.实现分析:
本文以都是小写字母为例,即该树有26个叉(后期丰富可以加上大写字母,规定字符等)
每一个节点都含有一个指针数组,每个指针数组里装26根指针,除了该指针数组之外,每个节点还应该有一个标志flag。
flag有以下作用:
a.初始为0,当当前节点是某个单词结尾的时候,flag加一,
b.同时flag也可以用来统计单词数
Trie树的实现
Trie树是一种形似树的数据结构,它的每个节点都包含一个指针数组,假设,我们要构建一个26个字母的Trie树,那么每一个指针对应着字母表里的一个字母。从根节点开始,我们只要依次找到目标单词里下一个字母对应的指针,就可以一步步查找目标了。假设,我们要把字符串AB,ABBA,ABCD和BCD插入到Trie树中,由于Trie树的根节点不保存任何字母,我们从根节点的直接后继开始保存字母。如下图所示,我们在Trie树的第二层中保存了字母A和B,第三层中保存了B和C,其中B被标记为深蓝色表示单词AB已经插入完成。
图2 Trie树的实现
我们发现由于Trie的每个节点都有一个长度为26指针数组,但我们知道并不是每个指针数组都保存记录,空的指针数组导致内存空间的浪费。
假设,我们要设计一个翻译软件,翻译软件少不了查词功能,而且当用户输入要查询的词汇时,软件会提示相似单词,让用户选择要查询的词汇,这样用户就无需输入完整词汇就能进行查询,而且用户体验更好。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 typedef struct node 6 { 7 int nFlag; 8 struct node *pCharacter[26]; 9 char *str;//若某节点是某个单词的结尾,则把该单词保存在该节点中。 10 }TrieTree; 11 12 void AddWord(TrieTree *pTree,char *str) 13 { 14 int i; 15 TrieTree *pTemp = NULL; 16 for(i = 0;i<strlen(str);i++) 17 { 18 //节点的对应位置空 添加节点 19 if(pTree->pCharacter[str[i]-97] == NULL) 20 { 21 pTemp = (TrieTree*)malloc(sizeof(TrieTree)); 22 memset(pTemp,0,sizeof(TrieTree)); 23 24 pTree->pCharacter[str[i]-97] = pTemp; 25 } 26 27 //向下走 28 pTree = pTree->pCharacter[str[i]-97]; 29 } 30 31 //末尾标记 32 pTree->nFlag++; 33 pTree->str = str; 34 } 35 36 37 TrieTree *CreateTrieTree(char *str[],int nLength) 38 { 39 if(str == NULL || nLength <=0)return NULL; 40 41 TrieTree *pRoot = NULL; 42 pRoot = (TrieTree*)malloc(sizeof(TrieTree)); 43 memset(pRoot,0,sizeof(TrieTree)); 44 45 //将字符串依次添加 46 int i; 47 for(i = 0;i<nLength;i++) 48 { 49 AddWord(pRoot,str[i]); 50 } 51 return pRoot; 52 } 53 54 void Search(TrieTree *pRoot,char *str) 55 { 56 if(str == NULL || pRoot == NULL)return; 57 58 int i; 59 for(i = 0;i<strlen(str);i++) 60 { 61 if(pRoot->pCharacter[str[i]-97] == NULL) 62 { 63 printf("failed. "); 64 return; 65 } 66 pRoot = pRoot->pCharacter[str[i]-97]; 67 } 68 69 //检测末尾标志 70 if(pRoot->nFlag != 0 ) 71 { 72 printf("%s ",pRoot->str); 73 return; 74 } 75 else 76 { 77 printf("hhhh failed. "); 78 return; 79 } 80 } 81 82 void Traversal(TrieTree *pTree) 83 { 84 if(pTree == NULL) 85 return; 86 87 if(pTree->nFlag != 0) 88 { 89 printf("%s ",pTree->str); 90 } 91 92 int i; 93 for(i = 0;i<26;i++) 94 { 95 Traversal(pTree->pCharacter[i]); 96 } 97 } 98 99 int main() 100 { 101 char *str[] = {"app","apple","orange","lemon","logo","ball","tie","glory"}; 102 TrieTree *pRoot = CreateTrieTree(str,8); 103 Traversal(pRoot); 104 printf("---------------------- "); 105 Search(pRoot,"ball"); 106 return 0; 107 }