数据结构——键树之双链表

#include "stdio.h"
#include "stdlib.h"
#include "string.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 Nil ' ' //定义结束符为空格
#define STACK_INIT_SIZE 10 // 存储空间初始分配量
#define STACKINCREMENT 2 // 存储空间分配增量

struct Others // 记录的其它部分
 {
   int ord;
 };

struct KeysType // 关键字类型
 {
   char ch[MAXKEYLEN]; // 关键字
   int num; // 关键字长度
 };

 struct Record // 记录类型
 {
   KeysType key; // 关键字
   Others others; // 其它部分(由主程定义)
 };

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

typedef struct DLTNode // 双链树类型
 {
   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(Record e)
 {
   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;
         i++;
         ap=(DLTree)malloc(sizeof(DLTNode));

         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

 //如下为对栈的操作
Status InitStack(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 StackisEmpty(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(SqStack S,Status(*visit)(SElemType))
 { // 从栈底到栈顶依次对栈中每个元素调用函数visit()。
   // 一旦visit()失败,则操作失败
   while(S.top>S.base)
     visit(*S.base++);
   printf("\n");
   return OK;
 }

 void TraverseDSTable(DLTree DT,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;    //Nil
         Push(s,e);// 叶子结点入栈

         Vi(*(p->infoptr));        //print
         i++;

         while(!StackisEmpty(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
   }//if(DT)
 }//TraverseDSTable

void INputD(DLTree &t,Record r[])
{
    Record *p;   
    for(int i=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

void UserSearch(DLTree t)
{
    char s[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;
    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("按关键字符串的顺序遍历双链键树:\n");
    TraverseDSTable(t,print);
    UserSearch(t);
    DestroyDSTable(t);

return 1;   
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minyuanxiani

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值