• 键树


    键树有两种存储结构

    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
  • 相关阅读:
    Oracle数据库之SQL基础和分支循环
    Oracle数据库基础--存储过程和函数
    oracle 子查询中null的问题(in 和 not in)
    Oracle伪列rownum
    Java基础之I/O流
    Java基础之Comparable接口, Collections类,Iterator接口,泛型(Generic)
    java基础之容器、集合、集合常用方法
    Java基础之常用类
    电子宠物加强版
    SIGAI机器学习第九集 数据降维2
  • 原文地址:https://www.cnblogs.com/wj204/p/3116203.html
Copyright © 2020-2023  润新知