键树
键树有两种存储结构
1)以树的孩子兄弟链表来表示键树,每个分支结点包括三个域,symbol域:存储关键字的一个字符;first域:存储指向第一棵子树根的指针;next域:存储指向右兄弟的指针。同时,叶子结点的infoptr域存储指向该关键字记录的指针。此时的键树又称双链树。
用代码实现如下:(参考自:http://www.cnblogs.com/gentleming/archive/2010/08/18/1802390.html)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 #define N 16 6 #define MAXKEYLEN 16 7 #define Nil ' ' 8 #define STACK_INIT_SIZE 10 9 #define STACKINCREMENT 2 10 11 struct Others//记录的其他部分 12 { 13 int ord; 14 }; 15 16 struct KeysType//关键字类型 17 { 18 char ch[MAXKEYLEN];//关键字 19 int num;//关键字长度 20 }; 21 22 struct Record//记录类型 23 { 24 KeysType key;//关键字 25 Others others;//其他部分 26 }; 27 28 enum NodeKind{LEAF,BRANCH};//结点种类:{叶子,分支} 29 30 typedef struct DLTNode//双链树类型 31 { 32 char symbol; 33 DLTNode *next;//指向兄弟结点的指针 34 NodeKind kind; 35 union 36 { 37 Record *infoptr;//叶子结点的记录指针 38 DLTNode *first;//分支结点的孩子链指针 39 }; 40 }DLTNode,*DLTree; 41 42 struct SElemType//定义栈元素类型 43 { 44 char ch; 45 DLTree p; 46 }; 47 48 struct SqStack 49 { 50 SElemType *base;//在栈构造之前和销毁之后,base的值为NULL 51 SElemType *top;//栈顶指针 52 int stacksize;//当前分配的存储空间,以元素为单位 53 };//顺序栈 54 55 int InitDSTable(DLTree &DT) 56 { 57 //构造一个空的双链键树DT 58 DT=NULL; 59 return 1; 60 }//初始化 61 62 void DestroyDSTable(DLTree &DT) 63 { 64 //双链键树DT存在,销毁双链键树DT,采用深度优先来销毁 65 if(DT) 66 { 67 if(DT->kind==BRANCH&&DT->first)//*dt是分支结点且有孩子 68 DestroyDSTable(DT->first);//销毁孩子子树 69 if(DT->next)//有兄弟 70 DestroyDSTable(DT->next);//销毁兄弟子树 71 free(DT);//释放根结点 72 DT=NULL;//指针赋值为空 73 } 74 }//DestroyDSTable 75 76 void print(Record e) 77 { 78 int i; 79 printf("("); 80 for(i=0;i<e.key.num;i++) 81 printf("%c",e.key.ch[i]); 82 printf(",%d)",e.others.ord); 83 }//print 84 85 Record *SearchDLTree(DLTree T,KeysType k) 86 { 87 //在非空双链树T中查找关键字等于K的记录,若存在,则返回指向该记录的指针,否则返回空指针 88 DLTree p; 89 int i; 90 if(T) 91 { 92 p=T;//初始化 93 i=0; 94 while(p&&i<k.num)//查找过程,很巧妙的程序。 95 { 96 while(p&&p->symbol!=k.ch[i])//查找关键字的第i位,从第0位开始 97 p=p->next; 98 if(p&&i<k.num)//准备查找下一位 99 p=p->first; 100 i++; 101 }//查找结束 102 103 if(!p) 104 return NULL; 105 else//查找成功 106 return p->infoptr; 107 }//if 108 else 109 return NULL; 110 }//SearchDLTree 111 112 void InsertDSTable(DLTree &DT,Record *r) 113 { 114 //初始条件:双链键树DT存在,r为待插入的数据元素的指针 115 //操作结果:若DT中不存在其关键字等于(*r).key.ch的数据元素,则按关键字顺序插入r到DT中 116 DLTree p=NULL,q,ap; 117 int i=0; 118 KeysType k=r->key; 119 if(!DT&&k.num)//空树且关键字符串非空 120 { 121 DT=ap=(DLTree)malloc(sizeof(DLTNode)); 122 for(;i<k.num;i++)//插入分支结点 123 { 124 if(p) 125 p->first=ap; 126 ap->next=NULL; 127 ap->symbol=k.ch[i]; 128 ap->kind=BRANCH; 129 p=ap; 130 ap=(DLTree)malloc(sizeof(DLTNode)); 131 }//for 132 133 p->first=ap;//插入叶子结点 134 ap->next=NULL; 135 ap->symbol=Nil; 136 ap->kind=LEAF; 137 ap->infoptr=r;//在叶子结点处记录指向该关键字的指针 138 }//if 139 else//非空树 140 { 141 p=DT;//指向根结点 142 while(p&&i<k.num) 143 { 144 while(p&&p->symbol<k.ch[i])//沿兄弟结点查找 145 { 146 q=p; 147 p=p->next; 148 }//while 149 if(p&&p->symbol==k.ch[i])//找到相符的结点 150 { 151 q=p; 152 p=p->first;//p指向分支结点的孩子链指针,与k.ch[i+1]比较结点 153 i++; 154 } 155 else//没找到,插入关键字,对应分析 156 { 157 ap=(DLTree)malloc(sizeof(DLTNode)); 158 if(q->first==p) 159 q->first=ap;//在长子的位置插入,这种情况对应执行了上面的if语句 160 else 161 q->next=ap;//在兄弟的位置插入,这种情况对应没有执行上面的if语句 162 ap->next=p; 163 ap->symbol=k.ch[i]; 164 ap->kind=BRANCH; 165 p=ap; 166 i++; 167 ap=(DLTree)malloc(sizeof(DLTNode)); 168 169 for(;i<k.num;i++) 170 { 171 p->first=ap; 172 ap->next=NULL; 173 ap->symbol=k.ch[i]; 174 ap->kind=BRANCH; 175 p=ap; 176 ap=(DLTree)malloc(sizeof(DLTNode)); 177 } 178 179 p->first=ap; 180 ap->next=NULL; 181 ap->symbol=Nil; 182 ap->kind=LEAF; 183 ap->infoptr=r; 184 }//else 185 }//while 186 }//else 187 }//InsertDSTable 188 189 int InitStack(SqStack &s) 190 { 191 //构造一个空栈S 192 if(!(s.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)))) 193 exit(-1); 194 s.top=s.base; 195 s.stacksize=STACK_INIT_SIZE; 196 return 1; 197 } 198 199 int DestroyStack(SqStack &s) 200 { 201 free(s.base); 202 s.base=NULL; 203 s.top=NULL; 204 s.stacksize=0; 205 return 1; 206 } 207 208 int ClearStack(SqStack &s) 209 { 210 s.top=s.base; 211 return 1; 212 } 213 214 int StackIsEmpty(SqStack s) 215 { 216 if(s.top==s.base) 217 return 1; 218 else 219 return 0; 220 } 221 222 int StackLength(SqStack s) 223 { 224 return s.top-s.base;//返回栈的元素个数,即栈的长度 225 } 226 227 int GetTop(SqStack s,SElemType &e) 228 { 229 if(s.top>s.base) 230 { 231 e=*(s.top-1); 232 return 1; 233 } 234 else 235 return 0; 236 } 237 238 int Push(SqStack &s,SElemType e) 239 {//插入元素e为新的栈顶元素 240 if(s.top-s.base>=s.stacksize)//栈满,追加栈空间 241 { 242 s.base=(SElemType *)realloc(s.base,(s.stacksize+STACKINCREMENT)*sizeof(SElemType));//新的基址 243 //新基址 244 if(!s.base) 245 exit(-1);//存储分配失败 246 s.top=s.base+s.stacksize; 247 s.stacksize+=STACKINCREMENT; 248 } 249 *(s.top)++=e; 250 return 1; 251 } 252 253 int Pop(SqStack &s,SElemType &e) 254 { 255 if(s.top==s.base) 256 return -1; 257 e=*--s.top; 258 } 259 260 void TraverseDSTable(DLTree DT,void(*Vi)(Record)) 261 { 262 //初始条件:双链键树DT存在,Vi是对结点操作的应用函数 263 //操作结果:按关键字的顺序输出关键字及其对应的记录 264 SqStack s; 265 SElemType e; 266 DLTree p; 267 int i=0,n=8; 268 if(DT) 269 { 270 InitStack(s); 271 e.p=DT; 272 e.ch=DT->symbol; 273 Push(s,e);//根结点入栈 274 p=DT->first; 275 276 while(p->kind==BRANCH)//分支结点 277 { 278 e.p=p; 279 e.ch=p->symbol; 280 Push(s,e);//分支结点入栈 281 p=p->first; 282 }//while 283 284 e.p=p; 285 e.ch=p->symbol;//Nil 286 Push(s,e);//叶子结点入栈 287 288 Vi(*(p->infoptr));//print 289 i++; 290 291 while(!StackIsEmpty(s)) 292 { 293 Pop(s,e); 294 p=e.p; 295 if(p->next)//有兄弟结点 296 { 297 p=p->next; 298 while(p->kind==BRANCH)//分支结点 299 { 300 e.p=p; 301 e.ch=p->symbol; 302 Push(s,e);//分支结点入栈 303 p=p->first; 304 }//while 305 306 e.p=p; 307 e.ch=p->symbol; 308 Push(s,e);//叶子结点入栈 309 310 Vi(*(p->infoptr)); 311 i++; 312 313 if(i%n==0) 314 printf("\n"); 315 }//if 316 }//while 317 }//if 318 }//TraverseDSTable 319 320 void INputD(DLTree &t,Record r[]) 321 { 322 Record *p; 323 for(int i=0;i<N;i++) 324 { 325 r[i].key.num=strlen(r[i].key.ch); 326 p=SearchDLTree(t,r[i].key); 327 if(!p)//t中不存在关键字为r[i].key的项 328 InsertDSTable(t,&r[i]); 329 }//for 330 }//INputD 331 332 void UserSearch(DLTree t) 333 { 334 char s[MAXKEYLEN+1]; 335 Record *p; 336 KeysType k; 337 printf("\n请输入待查找记录的关键字:"); 338 scanf("%s",s); 339 k.num=strlen(s); 340 strcpy(k.ch,s); 341 p=SearchDLTree(t,k); 342 if(p) 343 print(*p); 344 else 345 printf("没找到"); 346 printf("\n"); 347 } 348 349 int main() 350 { 351 DLTree t; 352 Record r[N]={ 353 {{"CAI"},1},{{"CAO"},2},{{"LI"},3},{{"LAN"},4}, 354 {{"CHA"},5},{{"CHANG"},6},{{"WEN"},7},{{"CHAO"},8}, 355 {{"YUN"},9},{{"YANG"},10},{{"LONG"},11},{{"WANG"},12}, 356 {{"ZHAO"},13},{{"LIU"},14},{{"WU"},15},{{"CHEN"},16} 357 }; 358 InitDSTable(t); 359 INputD(t,r); 360 printf("按关键字符串的顺序遍历双链键树:\n"); 361 TraverseDSTable(t,print); 362 UserSearch(t); 363 DestroyDSTable(t); 364 return 1; 365 }
2)以树的多重链表表示的键树称为Trie树,Tire树中有两种结点:分支结点(含有d个指针域和一个指示该结点中非空指针域的个数的整数域)和叶子结点(含有关键字域和指向记录的指针域),在分支结点中不设数据域,每个分支结点所表示的字符均有其双亲节点中(指向该结点)的指针所在的位置决定。(若从简述中某个结点到叶子结点的路径上每个结点都只有一个孩子,则可将该路径上所有节点压缩成一个“叶子结点”,且在该叶子结点中存储关键字及指向记录的指针等信息)
书上的图感觉分析起来都有问题。。。是我还没看懂???
违心了,下面的代码就完全是别人的了,自己写到一半不知道该怎么写了。。暂存,后面回头再来看看。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include "stdio.h" 2 #include "stdlib.h" 3 #include "string.h" 4 #include "ctype.h" 5 6 #define OK 1 7 #define ERROR 0 8 typedef int Status; // Status是函数的类型,其值是函数结果状态代码,如OK等 9 typedef int Boolean; // Boolean是布尔类型,其值是TRUE或false 10 #define N 16 // 数据元素个数 11 #define MAXKEYLEN 16 // 关键字的最大长度 12 #define STACK_INIT_SIZE 10 // 存储空间初始分配量 13 #define STACKINCREMENT 2 // 存储空间分配增量 14 #define LENGTH 27 // 结点的最大度+1(大写英文字母) 15 #define Nil ' ' // 定义结束符为空格 16 #define MAXKEYLEN 16 // 关键字的最大长度 17 18 // 对两个字符串型关键字的比较约定为如下的宏定义 19 #define EQ(a,b) (!strcmp((a),(b))) 20 #define LT(a,b) (strcmp((a),(b))<0) 21 #define LQ(a,b) (strcmp((a),(b))<=0) 22 23 struct Others // 记录的其它部分 24 { 25 int ord; 26 }; 27 28 struct KeysType // 关键字类型 29 { 30 char ch[MAXKEYLEN]; // 关键字 31 int num; // 关键字长度 32 }; 33 34 struct Record // 记录类型 35 { 36 KeysType key; // 关键字 37 Others others; // 其它部分(由主程定义) 38 }; 39 40 enum NodeKind{LEAF,BRANCH}; // 结点种类:{叶子,分支} 41 42 typedef struct TrieNode // Trie键树类型 43 { 44 NodeKind kind; 45 union 46 { 47 struct // 叶子结点 48 { 49 KeysType K; 50 Record *infoptr; 51 }lf; 52 struct // 分支结点 53 { 54 TrieNode *ptr[LENGTH]; // LENGTH为结点的最大度+1,在主程定义 55 // int num; 改 56 }bh; 57 };//union 58 }TrieNode,*TrieTree; 59 60 Status InitDSTable(TrieTree &T) 61 { //操作结果: 构造一个空的Trie键树T 62 T=NULL; 63 return OK; 64 } 65 66 void DestroyDSTable(TrieTree &T) 67 { //初始条件: Trie树T存在。操作结果: 销毁Trie树T 68 int i; 69 if(T) // 非空树 70 { 71 for(i=0;i<LENGTH;i++) 72 { 73 if(T->kind==BRANCH && T->bh.ptr[i]) // 第i个结点不空 74 { 75 if(T->bh.ptr[i]->kind==BRANCH) // 是子树 76 { 77 DestroyDSTable(T->bh.ptr[i]); 78 } 79 else // 是叶子 80 { 81 free(T->bh.ptr[i]); 82 T->bh.ptr[i]=NULL; 83 } 84 } 85 }//for 86 free(T); // 释放根结点 87 T=NULL; // 空指针赋0 88 }//if(T) 89 }//DestroyDSTable 90 91 Status pr(Record *r) 92 { 93 printf("(%s,%d) ",r->key.ch,r->others.ord); 94 return OK; 95 }//pr 96 97 int ord(char c) 98 { 99 c=toupper(c); 100 if(c>='A'&&c<='Z') 101 return c-'A'+1; //英文字母返回其在字母表中的序号 102 else 103 return 0; // 其余字符返回0 104 }//ord 105 106 Record *SearchTrie(TrieTree T,KeysType K) 107 { // 在键树T中查找关键字等于K的记录 108 TrieTree p; 109 int i; 110 111 for(p=T,i=0;p && p->kind==BRANCH && i<K.num; p=p->bh.ptr[ord(K.ch[i])],++i ) 112 ;//对K的每个字符逐个查找,*p为分支结点,ord()求字符在字母表中序号 113 if(p && p->kind==LEAF && p->lf.K.num==K.num && EQ(p->lf.K.ch,K.ch)) //查找成功 114 return p->lf.infoptr; 115 else // 查找不成功 116 return NULL; 117 }//SearchTrie 118 119 void InsertTrie(TrieTree &T,Record *r) 120 { // 初始条件: Trie键树T存在,r为待插入的数据元素的指针 121 // 操作结果: 若T中不存在其关键字等于(*r).key.ch的数据元素, 122 // 则按关键字顺序插r到T中 123 TrieTree p,q,ap; 124 int i=0,j; 125 KeysType K1,K=r->key; 126 if(!T) // 空树 127 { 128 T=(TrieTree)malloc(sizeof(TrieNode)); //新建根节点 129 T->kind=BRANCH; 130 for(i=0;i<LENGTH;i++) // 初始化,指针量赋初值NULL 131 T->bh.ptr[i]=NULL; 132 p=T->bh.ptr[ord(K.ch[0])]=(TrieTree)malloc(sizeof(TrieNode)); //新建叶子节点 133 p->kind=LEAF; 134 p->lf.K=K; 135 p->lf.infoptr=r; 136 }//if 137 else // 非空树 138 { 139 for(p=T,i=0; p && p->kind==BRANCH && i<K.num; ++i) 140 { 141 q=p; 142 p=p->bh.ptr[ord(K.ch[i])]; 143 }//for 144 i--; 145 146 if(p && p->kind==LEAF && p->lf.K.num==K.num && EQ(p->lf.K.ch,K.ch)) // T中存在该关键字 147 return; 148 149 else // T中不存在该关键字,插入之 150 { 151 if(!p) // 分支空,建立叶子节点,指向关键字记录 152 { 153 p=q->bh.ptr[ord(K.ch[i])]=(TrieTree)malloc(sizeof(TrieNode)); 154 p->kind=LEAF; 155 p->lf.K=K; 156 p->lf.infoptr=r; 157 }//if 158 else if(p->kind==LEAF) // 有不完全相同的叶子, 159 { 160 K1=p->lf.K; 161 do 162 { 163 ap=q->bh.ptr[ord(K.ch[i])]=(TrieTree)malloc(sizeof(TrieNode)); 164 ap->kind=BRANCH; 165 for(j=0;j<LENGTH;j++) // 指针量赋初值NULL 166 ap->bh.ptr[j]=NULL; 167 q=ap; 168 i++; 169 }while( ord(K.ch[i])==ord(K1.ch[i]) );//do...while 170 //如果原来的记录和新插入的关键字的第i个字符还是相等的话就再新生一个分支, 171 //直到不相等时,把原来的记录重新连到这个分支上,再新建一个叶子节点指向新插入的关键字 172 173 q->bh.ptr[ord(K1.ch[i])]=p; //把原来的不相同的叶子挂回来,如果是空字符,就挂在0号指针下 174 175 p=q->bh.ptr[ord(K.ch[i])]=(TrieTree)malloc(sizeof(TrieNode)); 176 p->kind=LEAF; 177 p->lf.K=K; 178 p->lf.infoptr=r; 179 }//else if 180 }//else 181 }//else 182 }//InsertTrie 183 184 void InputD(TrieTree &t,Record r[]) 185 { 186 Record *p; 187 for(int i=0;i<N;i++) 188 { 189 r[i].key.num=strlen(r[i].key.ch)+1; 190 r[i].key.ch[r[i].key.num]=Nil; // 在关键字符串最后加结束符 191 p=SearchTrie(t,r[i].key); 192 if(!p) 193 InsertTrie(t,&r[i]); 194 }//for 195 }//InputD 196 197 void TraverseDSTable(TrieTree T,Status(*Vi)(Record*)) 198 { // 初始条件: Trie键树T存在,Vi是对记录指针操作的应用函数 199 // 操作结果: 按关键字的顺序输出关键字及其对应的记录 200 TrieTree p; 201 int i; 202 if(T) 203 { 204 for(i=0;i<LENGTH;i++) 205 { 206 p=T->bh.ptr[i]; 207 if(p&&p->kind==LEAF) 208 Vi(p->lf.infoptr); 209 else if(p&&p->kind==BRANCH) 210 TraverseDSTable(p,Vi); 211 }//for 212 }//if 213 printf("\n"); 214 }//TraverseDSTable 215 216 void UserSearch(TrieTree t) 217 { 218 char s[MAXKEYLEN+1]; 219 KeysType k; 220 Record *p; 221 printf("\n请输入待查找记录的关键字符串: "); 222 scanf("%s",s); 223 k.num=strlen(s)+1; 224 strcpy(k.ch,s); 225 k.ch[k.num]=Nil; // 在关键字符串最后加结束符 226 p=SearchTrie(t,k); 227 if(p) 228 pr(p); 229 else 230 printf("没找到"); 231 printf("\n"); 232 }//UserSearch 233 234 int main() 235 { 236 TrieTree t; 237 Record r[N]={{{"CAI"},1},{{"CAO"},2},{{"LI"},3},{{"LAN"},4}, 238 239 {{"CHA"},5},{{"CHANG"},6},{{"WEN"},7},{{"CHAO"},8}, 240 241 {{"YUN"},9},{{"YANG"},10},{{"LONG"},11},{{"WANG"},12}, 242 243 {{"ZHAO"},13},{{"LIU"},14},{{"WU"},15},{{"CHEN"},16}}; 244 InitDSTable(t); 245 InputD(t,r); 246 printf("按关键字符串的顺序遍历Trie树(键树):\n"); 247 TraverseDSTable(t,pr); 248 UserSearch(t); 249 DestroyDSTable(t); 250 return 1; 251 }
看了下http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html 这篇文章对Trie树的介绍,或许又多了一层的理解,同时在这个过程的学习中,明白,或许学习就应该这样,多思考,不要盲目地看着别人怎么写,自己却不动脑子,这是很可悲,可恨的事情。说实话,我现在还有哪些弊病,还在改进当中,对下面这篇博文,我学到的东西,作者说是动态规划思想,我却没有很深的理解,怎样读取文件算是知道了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 using System.IO; 8 9 namespace ConsoleApplication2 10 { 11 public class Program 12 { 13 public static void Main() 14 { 15 Trie trie = new Trie(); 16 17 var file = File.ReadAllLines(Environment.CurrentDirectory + "//1.txt"); 18 19 foreach (var item in file) 20 { 21 var sp = item.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 22 23 trie.AddTrieNode(sp.LastOrDefault().ToLower(), Convert.ToInt32(sp[0])); 24 } 25 26 Stopwatch watch = Stopwatch.StartNew(); 27 28 //检索go开头的字符串 29 var hashSet = trie.SearchTrie("go"); 30 31 foreach (var item in hashSet) 32 { 33 Console.WriteLine("当前字符串的编号ID为:{0}", item); 34 } 35 36 watch.Stop(); 37 38 Console.WriteLine("耗费时间:{0}", watch.ElapsedMilliseconds); 39 40 Console.WriteLine("\n\ngo 出现的次数为:{0}\n\n", trie.WordCount("go")); 41 Console.ReadKey(); 42 } 43 } 44 45 public class Trie 46 { 47 public TrieNode trieNode = new TrieNode(); 48 49 #region Trie树节点 50 /// <summary> 51 /// Trie树节点 52 /// </summary> 53 public class TrieNode 54 { 55 /// <summary> 56 /// 26个字符,也就是26叉树 57 /// </summary> 58 public TrieNode[] childNodes; 59 60 /// <summary> 61 /// 词频统计 62 /// </summary> 63 public int freq; 64 65 /// <summary> 66 /// 记录该节点的字符 67 /// </summary> 68 public char nodeChar; 69 70 /// <summary> 71 /// 插入记录时的编号id 72 /// </summary> 73 public HashSet<int> hashSet = new HashSet<int>(); 74 75 /// <summary> 76 /// 初始化 77 /// </summary> 78 public TrieNode() 79 { 80 childNodes = new TrieNode[26]; 81 freq = 0; 82 } 83 } 84 #endregion 85 86 #region 插入操作 87 /// <summary> 88 /// 插入操作 89 /// </summary> 90 /// <param name="word"></param> 91 /// <param name="id"></param> 92 public void AddTrieNode(string word, int id) 93 { 94 AddTrieNode(ref trieNode, word, id); 95 } 96 97 /// <summary> 98 /// 插入操作 99 /// </summary> 100 /// <param name="root"></param> 101 /// <param name="s"></param> 102 public void AddTrieNode(ref TrieNode root, string word, int id) 103 { 104 if (word.Length == 0) 105 return; 106 107 //求字符地址,方便将该字符放入到26叉树中的哪一叉中 108 int k = word[0] - 'a'; 109 110 //如果该叉树为空,则初始化 111 if (root.childNodes[k] == null) 112 { 113 root.childNodes[k] = new TrieNode(); 114 115 //记录下字符 116 root.childNodes[k].nodeChar = word[0]; 117 } 118 119 //该id途径的节点 120 root.childNodes[k].hashSet.Add(id); 121 122 var nextWord = word.Substring(1); 123 124 //说明是最后一个字符,统计该词出现的次数 125 if (nextWord.Length == 0) 126 root.childNodes[k].freq++; 127 128 AddTrieNode(ref root.childNodes[k], nextWord, id); 129 } 130 #endregion 131 132 #region 检索操作 133 /// <summary> 134 /// 检索单词的前缀,返回改前缀的Hash集合 135 /// </summary> 136 /// <param name="s"></param> 137 /// <returns></returns> 138 public HashSet<int> SearchTrie(string s) 139 { 140 HashSet<int> hashSet = new HashSet<int>(); 141 142 return SearchTrie(ref trieNode, s, ref hashSet); 143 } 144 145 /// <summary> 146 /// 检索单词的前缀,返回改前缀的Hash集合 147 /// </summary> 148 /// <param name="root"></param> 149 /// <param name="s"></param> 150 /// <returns></returns> 151 public HashSet<int> SearchTrie(ref TrieNode root, string word, ref HashSet<int> hashSet) 152 { 153 if (word.Length == 0) 154 return hashSet; 155 156 int k = word[0] - 'a'; 157 158 var nextWord = word.Substring(1); 159 160 if (nextWord.Length == 0) 161 { 162 //采用动态规划的思想,word最后节点记录这途经的id 163 hashSet = root.childNodes[k].hashSet; 164 } 165 166 SearchTrie(ref root.childNodes[k], nextWord, ref hashSet); 167 168 return hashSet; 169 } 170 #endregion 171 172 #region 统计指定单词出现的次数 173 174 /// <summary> 175 /// 统计指定单词出现的次数 176 /// </summary> 177 /// <param name="root"></param> 178 /// <param name="word"></param> 179 /// <returns></returns> 180 public int WordCount(string word) 181 { 182 int count = 0; 183 184 WordCount(ref trieNode, word, ref count); 185 186 return count; 187 } 188 189 /// <summary> 190 /// 统计指定单词出现的次数 191 /// </summary> 192 /// <param name="root"></param> 193 /// <param name="word"></param> 194 /// <param name="hashSet"></param> 195 /// <returns></returns> 196 public void WordCount(ref TrieNode root, string word, ref int count) 197 { 198 if (word.Length == 0) 199 return; 200 201 int k = word[0] - 'a'; 202 203 var nextWord = word.Substring(1); 204 205 if (nextWord.Length == 0) 206 { 207 //采用动态规划的思想,word最后节点记录这途经的id 208 count = root.childNodes[k].freq; 209 } 210 211 WordCount(ref root.childNodes[k], nextWord, ref count); 212 } 213 214 #endregion 215 216 #region 修改操作 217 /// <summary> 218 /// 修改操作 219 /// </summary> 220 /// <param name="newWord"></param> 221 /// <param name="oldWord"></param> 222 /// <param name="id"></param> 223 public void UpdateTrieNode(string newWord, string oldWord, int id) 224 { 225 UpdateTrieNode(ref trieNode, newWord, oldWord, id); 226 } 227 228 /// <summary> 229 /// 修改操作 230 /// </summary> 231 /// <param name="root"></param> 232 /// <param name="newWord"></param> 233 /// <param name="oldWord"></param> 234 /// <param name="id"></param> 235 public void UpdateTrieNode(ref TrieNode root, string newWord, string oldWord, int id) 236 { 237 //先删除 238 DeleteTrieNode(oldWord, id); 239 240 //再添加 241 AddTrieNode(newWord, id); 242 } 243 #endregion 244 245 #region 删除操作 246 /// <summary> 247 /// 删除操作 248 /// </summary> 249 /// <param name="root"></param> 250 /// <param name="newWord"></param> 251 /// <param name="oldWord"></param> 252 /// <param name="id"></param> 253 public void DeleteTrieNode(string word, int id) 254 { 255 DeleteTrieNode(ref trieNode, word, id); 256 } 257 258 /// <summary> 259 /// 删除操作 260 /// </summary> 261 /// <param name="root"></param> 262 /// <param name="newWord"></param> 263 /// <param name="oldWord"></param> 264 /// <param name="id"></param> 265 public void DeleteTrieNode(ref TrieNode root, string word, int id) 266 { 267 if (word.Length == 0) 268 return; 269 270 //求字符地址,方便将该字符放入到26叉树种的哪一颗树中 271 int k = word[0] - 'a'; 272 273 //如果该叉树为空,则说明没有找到要删除的点 274 if (root.childNodes[k] == null) 275 return; 276 277 var nextWord = word.Substring(1); 278 279 //如果是最后一个单词,则减去词频 280 if (word.Length == 0 && root.childNodes[k].freq > 0) 281 root.childNodes[k].freq--; 282 283 //删除途经节点 284 root.childNodes[k].hashSet.Remove(id); 285 286 DeleteTrieNode(ref root.childNodes[k], nextWord, id); 287 } 288 #endregion 289 } 290 }
作者:wj704
出处:http://www.cnblogs.com/wj204/