32、键树的插入、查找(多重链表存储)

本文详细介绍了Trie树的存储结构与算法思想,包括使用多重链表存储的Trie树(检索树)的基本概念、查找与插入操作的具体实现,并提供了完整的C语言代码示例。Trie树是一种用于快速查找字符串的高效数据结构。
摘要由CSDN通过智能技术生成

一、基本概念

    

在《键树的插入、查找(孩子兄弟存储)》一文中,已经讨论了基本概念,此不赘述。

http://blog.163.com/zhoumhan_0351/blog/static/3995422720091024111855806

    

以多重链表存储作存储结构时,此时的键树又称为Trie(retrieve检索)树。在叶子结点中存储关键字及指向记录的指针信息,在分支结点中不设数据域,每个分支结点所表示的字符均由其双亲结点中(指向该结点)的指针所在位置决定。其存储结构及Trie键树如图示:

    

二、算法思想

    

如下为在多重链表来表示存储结构下的算法描述:

    

假设: T 为指向  Trie 树根结点的指针,K.ch[0..K.num-1] 为待查关键字(给定值)。则查找过程中的基本操作为:搜索和对应字母相应的指针。

    

若 p 不空,且 p 所指为分支结点,则p = p->bh.Ptr[ord(K.Ch[i])] ;(其中: 0 ≤i≤ K.num-1)初始状态: p=T; i = 0;

若 ( p && p->kind == BRANCH && i<K.num)则继续搜索下一层的结点p=p->bh.ptr[ord(K.ch[i])]; i++;其中,ord 为求字符在字母表中序号的函数;

若  ( p &&  p->kind==LEAF && p->lf.K==K)则查找成功,返回指向相应记录的指针 p->lf.infoptr; 

反之,即 ( !p || p->kind==LEAF && p->lf.K!=K )则表明查找不成功,返回“空指针”。

    

可见,查找成功时,其过程为走了一条从根到叶子结点的路径。在Trie树上进行插入和删除,只是需要相应地增加和删除一些分支结点,当分支结点中num域的值减为1时,便可删除。

    

三、C语言描述


四、C语言实现

#include  "stdio.h"

#include  "stdlib.h"

#include  "string.h"

#include  "ctype.h"

#define OK 1

#define ERROR 0

typedef int Status;  // Status是函数的类型,其值是函数结果状态代码,如OK等

typedef int  Boolean; // Boolean是布尔类型,其值是TRUE或false

#define N 16 // 数据元素个数

#define MAXKEYLEN  16 // 关键字的最大长度

#define  STACK_INIT_SIZE 10 // 存储空间初始分配量

#define  STACKINCREMENT 2 // 存储空间分配增量

#define LENGTH 27  // 结点的最大度+1(大写英文字母)

#define Nil ' ' //  定义结束符为空格

#define MAXKEYLEN  16 // 关键字的最大长度

#define EQ(a,b)  (!strcmp((a),(b)))

#define LT(a,b)  (strcmp((a),(b))<0)

#define LQ(a,b)  (strcmp((a),(b))<=0)

 struct Others // 记录的其它部分

 {

   int ord;

 };

 struct KeysType // 关键字类型

 {

   char ch[MAXKEYLEN]; // 关键字

   int num; // 关键字长度

 };

 struct Record // 记录类型

 {

   KeysType key; // 关键字

   Others others; // 其它部分(由主程定义)

 };

 enum NodeKind{LEAF,BRANCH}; // 结点种类:{叶子,分支}

 typedef struct TrieNode // Trie键树类型

 {

   NodeKind kind;

   union

   {

     struct // 叶子结点

     {

       KeysType K;

       Record *infoptr;

     }lf;

     struct // 分支结点

     {

       TrieNode *ptr[LENGTH]; // LENGTH为结点的最大度+1,在主程定义

     //   int num; 改

     }bh;

   };//union

 }TrieNode,*TrieTree;

 // 对两个字符串型关键字的比较约定为如下的宏定义

Status InitDSTable(TrieTree  &T)

 { // 操作结果: 构造一个空的Trie键树T

   T=NULL;

   return OK;

 }

 void DestroyDSTable(TrieTree &T)

 { // 初始条件: Trie树T存在。操作结果: 销毁Trie树T

   int i;

   if(T) // 非空树

   {

     for(i=0;i<LENGTH;i++)

        if(T->kind==BRANCH&&T->bh.ptr[i]) // 第i个结点不空

          if(T->bh.ptr[i]->kind==BRANCH) // 是子树

           DestroyDSTable(T->bh.ptr[i]);

         else // 是叶子

         {

           free(T->bh.ptr[i]);

           T->bh.ptr[i]=NULL;

         }

     free(T); // 释放根结点

     T=NULL; // 空指针赋0

   }

 }

Status pr(Record  *r)

 {

    printf("(%s,%d)",r->key.ch,r->others.ord);

   return OK;

 }//pr

int ord(char c)

 {

   c=toupper(c);

   if(c>='A'&&c<='Z')

     return c-'A'+1; //英文字母返回其在字母表中的序号

   else

     return 0; // 其余字符返回0

 }//ord

Record  *SearchTrie(TrieTree T,KeysType K)

 { // 在键树T中查找关键字等于K的记录

   TrieTree p;

   int i;

    for(p=T,i=0;p&&p->kind==BRANCH&&i<K.num;p=p->bh.ptr[ord(K.ch[i])],++i);

   // 对K的每个字符逐个查找,*p为分支结点,ord()求字符在字母表中序号

    if(p&&p->kind==LEAF&&p->lf.K.num==K.num&&EQ(p->lf.K.ch,K.ch))  // 查找成功

     return p->lf.infoptr;

   else // 查找不成功

     return NULL;

 }//SearchTrie

void  InsertTrie(TrieTree &T,Record *r)

 { // 初始条件: Trie键树T存在,r为待插入的数据元素的指针

   // 操作结果: 若T中不存在其关键字等于(*r).key.ch的数据元素,

   //            则按关键字顺序插r到T中

   TrieTree p,q,ap;

   int i=0,j;

   KeysType K1,K=r->key;

   if(!T) // 空树

   {

     T=(TrieTree)malloc(sizeof(TrieNode));

     T->kind=BRANCH;

     for(i=0;i<LENGTH;i++) // 指针量赋初值NULL

       T->bh.ptr[i]=NULL;

      p=T->bh.ptr[ord(K.ch[0])]=(TrieTree)malloc(sizeof(TrieNode));

     p->kind=LEAF;

     p->lf.K=K;

     p->lf.infoptr=r;

   }//if

   else // 非空树

   {

      for(p=T,i=0;p&&p->kind==BRANCH&&i<K.num;++i)

     {

       q=p;

       p=p->bh.ptr[ord(K.ch[i])];

     }//for

     i--;

      if(p&&p->kind==LEAF&&p->lf.K.num==K.num&&EQ(p->lf.K.ch,K.ch))  // T中存在该关键字

       return;

     else // T中不存在该关键字,插入之

     {

       if(!p) // 分支空

       {

          p=q->bh.ptr[ord(K.ch[i])]=(TrieTree)malloc(sizeof(TrieNode));

         p->kind=LEAF;

         p->lf.K=K;

         p->lf.infoptr=r;

       }//if

       else if(p->kind==LEAF) // 有不完全相同的叶子

       {

         K1=p->lf.K;

         do

              {

            ap=q->bh.ptr[ord(K.ch[i])]=(TrieTree)malloc(sizeof(TrieNode));

           ap->kind=BRANCH;

           for(j=0;j<LENGTH;j++) // 指针量赋初值NULL

             ap->bh.ptr[j]=NULL;

           q=ap;

           i++;

           }while(ord(K.ch[i])==ord(K1.ch[i]));//do...while

         q->bh.ptr[ord(K1.ch[i])]=p; //由于是空字符,所以挂在0号指针下

               if(ord(K1.ch[i])==0)

                      puts(K1.ch);

          p=q->bh.ptr[ord(K.ch[i])]=(TrieTree)malloc(sizeof(TrieNode));

         p->kind=LEAF;

         p->lf.K=K;

         p->lf.infoptr=r;

           }//else if

       }//else

   }//else

 }//InsertTrie

void  InputD(TrieTree &t,Record r[])

{

Record *p;   

for(int  i=0;i<N;i++)

   {

     r[i].key.num=strlen(r[i].key.ch)+1;

     r[i].key.ch[r[i].key.num]=Nil; // 在关键字符串最后加结束符

     p=SearchTrie(t,r[i].key);

     if(!p)

       InsertTrie(t,&r[i]);

   }//for

}//InputD

void  TraverseDSTable(TrieTree T,Status(*Vi)(Record*))

 { // 初始条件: Trie键树T存在,Vi是对记录指针操作的应用函数

   // 操作结果: 按关键字的顺序输出关键字及其对应的记录

   TrieTree p;

   int i;

   if(T)

   {

     for(i=0;i<LENGTH;i++)

     {

       p=T->bh.ptr[i];

       if(p&&p->kind==LEAF)

         Vi(p->lf.infoptr);

       else if(p&&p->kind==BRANCH)

         TraverseDSTable(p,Vi);

     }//for

   }//if

 }//TraverseDSTable

void  UserSearch(TrieTree t)

{

char  s[MAXKEYLEN+1];

KeysType k;

Record *p;   

printf("\n请输入待查找记录的关键字符串: ");

scanf("%s",s);

k.num=strlen(s)+1;

strcpy(k.ch,s);

k.ch[k.num]=Nil;  // 在关键字符串最后加结束符

p=SearchTrie(t,k);

if(p)

pr(p);

else

printf("没找到");

printf("\n");

}//UserSearch

int main()

{

TrieTree t;

Record  r[N]={{{"CAI"},1},{{"CAO"},2},{{"LI"},3},{{"LAN"},4},

                     {{"CHA"},5},{{"CHANG"},6},{{"WEN"},7},{{"CHAO"},8},

                     {{"YUN"},9},{{"YANG"},10},{{"LONG"},11},{{"WANG"},12},

                     {{"ZHAO"},13},{{"LIU"},14},{{"WU"},15},{{"CHEN"},16}};

InitDSTable(t);

InputD(t,r);

printf("按关键字符串的顺序遍历Trie树(键树):\n");

TraverseDSTable(t,pr);

UserSearch(t);

DestroyDSTable(t);

return 1;

}

五、派生数据类型

    

http://blog.163.com/zhoumhan_0351/blog/static/399542272009102541440834

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值