一、基本概念

1、键树:如果一个关键字可以表示成字符的序号,即字符串,那么可以用键树(keyword tree),又称数字搜索树(digital search tree)或字符树,来表示这样的字符串的集合。

2、键树相关说明:键树是一棵多叉树(度>=2),树中每个结点并不代表一个关键字或元素,而只代表字符串中的一个字符。例如,它可以表示数字串中的一个数位,或单词中的一个字母等等。根结点不代表任何字符,根以下第一层的结点对应于字符串的第一个字符,第二层的结点对应于字符串的第二个字符……每个字符串可由一个特殊的字符如“$”等作为字符串的结束符,用一个叶子结点来表示该特殊字符。把从根到叶子的路径上,所有结点(除根以外)对应的字符连接起来,就得到一个字符串。因此,键树的深度和关键字集合的大小无关。因此,每个叶子结点对应一个关键字。在叶子结点还可以包含一个指针,指向该关键字所对应的元素。整个字符串集合中的字符串的数目等于叶子结点的数目。如果一个集合中的关键字都具有这样的字符串特性,那么,该关键字集合就可采用这样一棵键树来表示。事实上,还可以赋予“字符串”更广泛的含义,它可以是任何类型的对象组成的串。

3、键树被约定为是一棵有序树,即同一层中兄弟结点之间依所含符号自左至右有序,并约定结束符‘$’小于任何其它符号。

二、算法思想

如下为在树的孩子兄弟链表来表示下的算法描述:

假设: T 为指向双链树根结点的指针,K.ch[0..K.num-1] 为待查关键字,其中K.ch[0]至K.ch[num-2]表示待查关键字中num-1个字符,K.ch[num-1]为结束字符$。

则查找过程中的基本操作为进行下列比较:

K.ch[i] =? p->symbol

其中: p 指向双链树中某个结点,0≤ i ≤ K.num-1,初始状态:p=T->first; i = 0;

若 ( p && p->symbol == K.ch[i] &&i<K.num-1)则继续和给定值的下一位进行比较p=p->first; i++;

若 ( p && p->symbol != K.ch[i] )则继续在键树的同一层上进行查找  p=p->next;

若 ( p == NULL)则表明查找不成功,返回“空指针”;

若  ( p &&p->symbol==K.ch[i] && i==K.num-1)则 查找成功,返回指向相应记录的指针 p->infoptr。

三、C语言描述

四、C语言实现

#include"stdio.h"

#include"stdlib.h"

#include"string.h"

#define OK 1

#define ERROR 0

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

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

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

#define MAXKEYLEN16 // 关键字的最大长度

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

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

#defineSTACKINCREMENT 2 // 存储空间分配增量

struct Others // 记录的其它部分

{

int ord;

};

struct KeysType //关键字类型

{

char ch[MAXKEYLEN]; // 关键字

int num; // 关键字长度

};

struct Record // 记录类型

{

KeysType key; // 关键字

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

};

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

typedef structDLTNode // 双链树类型

{

char symbol;

DLTNode *next; // 指向兄弟结点的指针

NodeKind kind;

union

{

Record *infoptr; // 叶子结点的记录指针

DLTNode *first; // 分支结点的孩子链指针

};

}DLTNode,*DLTree;

struct SElemType // 定义栈元素类型

{

char ch;

DLTree p;

};

struct SqStack

{

SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL

SElemType *top; // 栈顶指针

int stacksize; // 当前已分配的存储空间,以元素为单位

}; // 顺序栈

Status InitDSTable(DLTree &DT)

{ // 操作结果: 构造一个空的双链键树DT

DT=NULL;

return OK;

}//InitDSTable

void DestroyDSTable(DLTree &DT)

{ // 初始条件: 双链键树DT存在。操作结果: 销毁双链键树DT

//我们采用深度优先来销毁。

if(DT) // 非空树

{

if(DT->kind==BRANCH&&DT->first) // *DT是分支结点且有孩子

DestroyDSTable(DT->first); // 销毁孩子子树

if(DT->next) // 有兄弟

DestroyDSTable(DT->next); // 销毁兄弟子树

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

DT=NULL; // 空指针赋0

}

}//DestroyDSTable

void print(Recorde)

{

int i;

printf("(");

for(i=0;i<e.key.num;i++)

printf("%c",e.key.ch[i]);

printf(",%d)",e.others.ord);

}//print

Record*SearchDLTree(DLTree T,KeysType K)

{ // 在非空双链键树T中查找关键字等于K的记录,若存在,

// 则返回指向该记录的指针,否则返回空指针。算法9.15,有改动

DLTree p;

int i;

if(T)

{

p=T; // 初始化

i=0;

while(p&&i<K.num)

{

while(p&&p->symbol!=K.ch[i])// 查找关键字的第i位

p=p->next;

if(p&&i<K.num) // 准备查找下一位

p=p->first;

++i;

} // 查找结束

if(!p) // 查找不成功

return NULL;

else // 查找成功

return p->infoptr;

}//if

else

return NULL; // 树空

}//SearchDLTree

void InsertDSTable(DLTree &DT,Record *r)

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

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

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

DLTree p=NULL,q,ap;

int i=0;

KeysType K=r->key;

if(!DT&&K.num) // 空树且关键字符串非空

{

DT=ap=(DLTree)malloc(sizeof(DLTNode));

for(;i<K.num;i++) // 插入分支结点

{

if(p)

p->first=ap;

ap->next=NULL;

ap->symbol=K.ch[i];

ap->kind=BRANCH;

p=ap;

ap=(DLTree)malloc(sizeof(DLTNode));

}//for

p->first=ap; // 插入叶子结点

ap->next=NULL;

ap->symbol=Nil;

ap->kind=LEAF;

ap->infoptr=r;//在叶子结点处记录指向该关键字的指针

}//if

else // 非空树

{

p=DT; // 指向根结点

while(p&&i<K.num)

{

while(p&&p->symbol<K.ch[i]) // 沿兄弟结点查找

{

q=p;

p=p->next;

}//while

if(p&&p->symbol==K.ch[i]) // 找到与K.ch[i]相符的结点

{

q=p;

p=p->first; // p指向将与K.ch[i+1]比较的结点

++i;

}//if

else // 没找到,插入关键字

{

ap=(DLTree)malloc(sizeof(DLTNode));

if(q->first==p)

q->first=ap; // 在长子的位置插入

else // q->next==p

q->next=ap; // 在兄弟的位置插入

ap->next=p;

ap->symbol=K.ch[i];

ap->kind=BRANCH;

p=ap;

ap=(DLTree)malloc(sizeof(DLTNode));

i++;

for(;i<K.num;i++) // 插入分支结点

{

p->first=ap;

ap->next=NULL;

ap->symbol=K.ch[i];

ap->kind=BRANCH;

p=ap;

ap=(DLTree)malloc(sizeof(DLTNode));

}//for

p->first=ap; // 插入叶子结点

ap->next=NULL;

ap->symbol=Nil;

ap->kind=LEAF;

ap->infoptr=r;

}//else

}//while

}//else

}//InsertDSTable

//如下为对栈的操作

StatusInitStack(SqStack &S)

{ // 构造一个空栈S

if(!(S.base=(SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType))))

exit(-1); // 存储分配失败

S.top=S.base;

S.stacksize=STACK_INIT_SIZE;

return OK;

}

Status DestroyStack(SqStack &S)

{ // 销毁栈S,S不再存在

free(S.base);

S.base=NULL;

S.top=NULL;

S.stacksize=0;

return OK;

}

Status ClearStack(SqStack &S)

{ // 把S置为空栈

S.top=S.base;

return OK;

}

Status StackEmpty(SqStack S)

{ // 若栈S为空栈,则返回TRUE,否则返回false

if(S.top==S.base)

return true;

else

return false;

}

int StackLength(SqStack S)

{ // 返回S的元素个数,即栈的长度

return S.top-S.base;

}

Status GetTop(SqStack S,SElemType &e)

{ // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR

if(S.top>S.base)

{

e=*(S.top-1);

return OK;

}

else

return ERROR;

}

Status Push(SqStack &S,SElemType e)

{ // 插入元素e为新的栈顶元素

if(S.top-S.base>=S.stacksize) // 栈满,追加存储空间

{

S.base=(SElemType*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));

if(!S.base)

exit(-1); // 存储分配失败

S.top=S.base+S.stacksize;

S.stacksize+=STACKINCREMENT;

}

*(S.top)++=e;

return OK;

}

Status Pop(SqStack &S,SElemType &e)

{ // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR

if(S.top==S.base)

return ERROR;

e=*--S.top;

return OK;

}

Status StackTraverse(SqStackS,Status(*visit)(SElemType))

{ // 从栈底到栈顶依次对栈中每个元素调用函数visit()。

// 一旦visit()失败,则操作失败

while(S.top>S.base)

visit(*S.base++);

printf("\n");

return OK;

}

void TraverseDSTable(DLTreeDT,void(*Vi)(Record))

{ // 初始条件: 双链键树DT存在,Vi是对结点操作的应用函数,

//          ViR是对记录操作的应用函数

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

//我们用

SqStack s;

SElemType e;

DLTree p;

int i=0,n=8;

if(DT)

{

InitStack(s);

e.p=DT;

e.ch=DT->symbol;

Push(s,e);

p=DT->first;

while(p->kind==BRANCH) // 分支结点

{

e.p=p;

e.ch=p->symbol;

Push(s,e);// 分支结点入栈

p=p->first;

}//while

e.p=p;

e.ch=p->symbol;

Push(s,e);// 叶子结点入栈

Vi(*(p->infoptr));

i++;

while(!StackEmpty(s))

{//广度优先进行遍历

Pop(s,e);

p=e.p;

if(p->next) // 有兄弟结点

{

p=p->next;

while(p->kind==BRANCH) // 分支结点

{

e.p=p;

e.ch=p->symbol;

Push(s,e);// 叶子结点入栈

p=p->first;

}//while

e.p=p;

e.ch=p->symbol;

Push(s,e);// 叶子结点入栈

Vi(*(p->infoptr));

i++;

if(i%n==0)

printf("\n"); // 输出n个元素后换行

}//if

}//while

}

}//TraverseDSTable

void INputD(DLTree&t,Record r[])

{

Record *p;

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

{

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

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

if(!p) // t中不存在关键字为r[i].key的项

InsertDSTable(t,&r[i]);

}//for

}//INputD

voidUserSearch(DLTree t)

{

chars[MAXKEYLEN+1];

Record *p;

KeysType k;

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

scanf("%s",s);

k.num=strlen(s);

strcpy(k.ch,s);

p=SearchDLTree(t,k);

if(p)

print(*p);

else

printf("没找到");

printf("\n");

}//UserSearch

int main()

{

DLTree t;

Recordr[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("按关键字符串的顺序遍历双链键树:\n");

TraverseDSTable(t,print);

UserSearch(t);

DestroyDSTable(t);

return 1;

}

五、复杂度分析

参考前面关于树的操作。

31、键树的插入、查找(孩子兄弟存储结构)相关推荐

  1. 族谱管理系统(孩子兄弟存储结构)

    */* Copyright (c) 2016,烟台大学计算机与控制工程学院* All rights reserved.* 文件名:test.cpp* 作者:常轩* 微信公众号:Worldhello* ...

  2. 树的存储结构(树的二叉链表(孩子—兄弟))

    // c6-5.h 树的二叉链表(孩子-兄弟)存储结构(见图6.32) typedef struct CSNode {TElemType data;CSNode *firstchild,*nextsi ...

  3. java 孩子节点所有_树的孩子-兄弟结点存储之Java实现

    /** * 本类为树(孩子-兄弟存储)的结点 * * @version 1.0, 2008-01-24 * @author 李赫元 北京交通大学 * @since JDK1.6 */ public c ...

  4. 树的存储结构以及实现代码

    树的存储结构以及实现代码   1.首先假设有一个树如下: 2.双亲表示法 我们假设以一组连续空间存储树的结点,在每个结点中,附设一个指示器指示其双亲结点到链表中的位置.这样,每个结点除了知道自己是谁以 ...

  5. 数据结构c语言——树的三种存储结构(双亲表示法、孩子表示法、兄弟表示法)

    在大量的应用中,人们曾使用多种形式的存储结构来表示树.这里,我们介绍3种常用的链表结构. 1.双亲表示法: 假设以一组连续空间存储树的结点,同时在每个结点中附设一个指示器指示其双亲结点在链表中的位置, ...

  6. 树的概念及存储结构(双亲表示法,孩子表示法,孩子兄弟表示法)

    文章目录 一. 树的概念 二. 树的存储结构 (一). 双亲表示法 (二). 孩子表示法 1. 定长结点链表存储结构 2. 孩子链表存储结构 (三). 孩子兄弟表示法 一. 树的概念 树(Tree)是 ...

  7. c语言 trie树,C语言实现Trie树(字典树)的插入查找删除与遍历操作

    Trie树,也称作是字典树,是一种哈希树的变种,查询效率较高.Trie树可以用于统计或者排序大量的字符串,比如对一系列字符串按照字典序排序. 字典树是一个多叉树,每一个节点上存储的不是一个字符串,而是 ...

  8. 设树采用孩子兄弟表示法存放.用类c语言设计算法计算树的高度.,(数据结构课程设计分类题目.doc...

    (数据结构课程设计分类题目 线性表 顺序表: 1.设有一元素为整数的线性表L=(a1,a2,a3,-,an),存放在一维数组A[N]中,设计一个算法,以表中an作为参考元素,将该表分为左.右两部分,其 ...

  9. c语言实现家谱(孩子兄弟树)数据结构

    一.需求分析 (一)题目 [问题描述] 家谱记载了一个家族的世系繁衍及重要人物事迹.使用树型结构对家谱进行管理,实现查看祖先和子孙个人信息,插入家族成员,删除家族成员的功能 [基本要求] (1)采用树 ...

  10. 键树查找基本内容介绍

    问题简介: 键树,又称数字查找树.它是一棵度>=2的树,树中的每个结点只含有组成关键字的某个符号.例如,如果关键字是数值,则一个结点中只包含一个数位:若关键字是单词,则一个结点只包含一个字母.每 ...

最新文章

  1. 混合图 (Standard IO)
  2. Vmware安装与使用
  3. keyStore vs trustStore--转载
  4. delphi dll是否可用var参数_时间序列之向量自回归(VAR)学习重点
  5. 完美粉红噪声及各种声波测试软件,粉红噪声的测试原理
  6. pandas的自带数据集_pandas.DataFrame.sample随机抽样
  7. windows下搭建OpenGL ES开发环境
  8. Jmeter中java接口测试
  9. python读写excel模块pandas_python3 基于pandas读写Excel
  10. [渝粤教育] 广东-国家-开放大学 21秋期末考试马克思主义基本原理概论(A)10882k1 (2)
  11. 【数据分享】某产品付费用户数据
  12. p6spy mysql8_P6spy监控打印SQL语句
  13. 软考中的网络工程师难考吗?
  14. 论文到底怎么降重才有效
  15. bootstrap3 侧边导航栏
  16. QModelIndex/Role/Model介紹 二
  17. 从现实世界的角度去理解计算机领域的知识
  18. NP管理器 NPManager v3.0.49 安卓APK逆向反编译工具
  19. ImageWarping--反距离加权插值(IDW)方法实现及报告
  20. Android 音乐APP(一)扫描本地音乐

热门文章

  1. git push reject 解决方案
  2. 如何查看XP系统的密匙
  3. 《自然语言处理简明教程》读书笔记:第十四章 文本数据挖掘
  4. 计算机系统汉字编码分为,计算机中的汉字编码
  5. Python格式化字符串f-string概览
  6. C51最小单片机系统
  7. 《东周列国志》第三十五回 晋重耳周游列国 秦怀嬴重婚公子
  8. html代码鼠的故事游戏,分享一个html+js实现打地鼠游戏的实例代码
  9. PLC可编程控制器实验装置
  10. 25个移动APP图表设计欣赏(译)