键树

键树有两种存储结构

1)以树的孩子兄弟链表来表示键树,每个分支结点包括三个域,symbol域:存储关键字的一个字符;first域:存储指向第一棵子树根的指针;next域:存储指向右兄弟的指针。同时,叶子结点的infoptr域存储指向该关键字记录的指针。此时的键树又称双链树。

用代码实现如下:(参考自:http://www.cnblogs.com/gentleming/archive/2010/08/18/1802390.html)

  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 }
View Code


2)以树的多重链表表示的键树称为Trie树,Tire树中有两种结点:分支结点(含有d个指针域和一个指示该结点中非空指针域的个数的整数域)和叶子结点(含有关键字域和指向记录的指针域),在分支结点中不设数据域,每个分支结点所表示的字符均有其双亲节点中(指向该结点)的指针所在的位置决定。(若从简述中某个结点到叶子结点的路径上每个结点都只有一个孩子,则可将该路径上所有节点压缩成一个“叶子结点”,且在该叶子结点中存储关键字及指向记录的指针等信息)

书上的图感觉分析起来都有问题。。。是我还没看懂???

违心了,下面的代码就完全是别人的了,自己写到一半不知道该怎么写了。。暂存,后面回头再来看看。

  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 }
View Code

  看了下http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html 这篇文章对Trie树的介绍,或许又多了一层的理解,同时在这个过程的学习中,明白,或许学习就应该这样,多思考,不要盲目地看着别人怎么写,自己却不动脑子,这是很可悲,可恨的事情。说实话,我现在还有哪些弊病,还在改进当中,对下面这篇博文,我学到的东西,作者说是动态规划思想,我却没有很深的理解,怎样读取文件算是知道了。

  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  }
View Code

 

posted @ 2013-06-03 22:43  wj704  阅读(286)  评论(0编辑  收藏  举报