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