C day19 高级数据表示(二叉树)

二叉树

第一步:建立抽象
在这里插入图片描述

第二步:定义接口
在这里插入图片描述在这里插入图片描述
第三步:实现接口

主函数

/*petclub.c  --使用二叉查找树*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "tree.h"

char menu(void);
void addpet(Tree *pt);
void droppet(Tree *pt);
void showpets(const Tree *pt);
void findpet(const Tree *pt);
void printitem(Item item);
void uppercase(char *str);
char *s_gets(char *st, int n);
void eatline();

int main()
{
    Tree pets;
    char choice;

    InitializeTree(&pets);//把宠物用二叉查找树存储
    while((choice = menu())!='q')
    {
        switch(choice)
        {
        case 'a':
            addpet(&pets);break;
        case 'l':
            showpets(&pets);break;
        case 'f':
            findpet(&pets);break;
        case 'n':
            printf("%u pets in club.\n", TreeItemsCount(&pets));
            break;
        case 'd':
            droppet(&pets);break;
        default:
            puts("Switching error.");break;
        }

    }
    DeleteAll(&pets);
    puts("All of the pets have been deleted!");
    return 0;
}

char menu(void)
{
    /*菜单程序获取选项并确保选项正确符合要求*/
    int ch;

    puts("Nerfville Pet Club Membership Program");
    puts("Enter the letter corresponding to your choice:");
    puts("a) add a pet         l) show list of pets");
    puts("n) number of pets    f) find pets");
    puts("d) delete a pet      q) quit");
    while((ch = getchar())!=EOF)
    {
        eatline();
        ch = tolower(ch);
        if(strchr("alnfdq", ch) == NULL)
            puts("Please enter an a, l, n, f, d, q.");
        else
            break;//跳出循环
    }
    if (ch == EOF)
        ch = 'q';

    return ch;
}

void eatline()
{
    while(getchar()!='\n')
        ;
}

void addpet(Tree *pt)
{
    Item temp;

    if(TreeIsFull(pt))
        puts("No room in the club.");
    else
    {
        puts("Enter name of pet:");
        s_gets(temp.petname, SLEN);
        puts("Enter pet kind:");
        s_gets(temp.petkind, SLEN);
        uppercase(temp.petname);
        uppercase(temp.petkind);

        if(AddItem(&temp, pt))
            puts("The pet has been added to the club!");
        //  无需考虑没添加成功的情况,因为AddItem()会报错
    }
}

void showpets(const Tree *ptree)
{
    if(TreeIsEmpty(ptree))
        puts("No entries!");
    else
        Traverse(ptree, printitem);//牛逼!!真聪明,Traverse()接口函数真有用

}

void printitem(Item item)
{
    printf("Pet: %-19s Kind: %-19s\n", item.petname, item.petkind);
}

void findpet(const Tree *pt)
{
    Item temp;

    if(TreeIsEmpty(pt))
    {
        puts("No entries!");
        return;
    }
    puts("Enter name of your pet you wish to find:");
    s_gets(temp.petname, SLEN);
    puts("Enter kind of your pet:");
    s_gets(temp.petkind, SLEN);
    uppercase(temp.petname);
    uppercase(temp.petkind);
    printf("%s the %s ", temp.petname, temp.petkind);
    if(InTree(&temp, pt))
        printf("is a member.\n");
    else
        printf("is not a member.\n");
}

void droppet(Tree *pt)
{
    Item temp;

    if(TreeIsEmpty(pt))
    {
        puts("No Entries!");
        return;
    }
    puts("Enter name of pet you wish to drop:");
    s_gets(temp.petname, SLEN);
    puts("Enter pet kind");
    s_gets(temp.petkind, SLEN);
    uppercase(temp.petname);
    uppercase(temp.petkind);
    printf("%s the %s ", temp.petname, temp.petkind);
    if(DeleteItem(&temp, pt))
        printf("is dropped from the club.\n");
    else
        printf("is not a member.\n");//如果没有这一项,DeleteItem会返回false
}

void uppercase(char *st)
{
    while(*st)
    {
        *st = toupper(*st);
        st++;
    }
}

char *s_gets(char *st, int n)
{
    char *find, *ret_val;

    ret_val = fgets(st, n, stdin);
    if(ret_val)
    {
        find = strchr(st, '\n');
        if(find)
            *find = '\0';
        else
            eatline();
    }

    return ret_val;
}

接口头文件(函数原型)

/*tree.h 二叉查找树*/
//注意树中不能有重复的项!!!

#ifndef _TREE_H_
#define _TREE_H_
#include <stdbool.h>
#define SLEN 20
#define MAXITEMS 10
typedef struct item{
    char petname[SLEN];
    char petkind[SLEN];
}Item;

typedef struct trnode{
    Item item;
    struct trnode *left;
    struct trnode *right;
}Trnode;

typedef struct tree{
    Trnode *root;
    unsigned int size;
}Tree;



/*函数原型*/

/*操作:初始化数为空*/
/*前提条件:ptree指向一个树*/
/*后置条件:树被初始化为空*/
void InitializeTree(Tree *ptree);

/*操作:确定树是否为空*/
/*前提条件:ptree指向一个树*/
/*后置条件:如果为空返回true,否则返回false*/
bool TreeIsEmpty(const Tree *ptree);

/*操作:确定树是否已满*/
/*前提条件:ptree指向一个树*/
/*后置条件:已满则返回true,否则返回false*/
bool TreeIsFull(const Tree *ptree);

/*操作:确定树的项数*/
/*前提条件:ptree指向一个树*/
/*后置条件:返回树的项数*/
unsigned int TreeItemsCount(const Tree *ptree);

/*操作:在树中添加一个项*/
/*前提条件:ptree指向一个已初始化的树,pi是待添加项的地址*/
/*后置条件:如果可以添加,则添加并返回true;否则返回false*/
bool AddItem(const Item *pi, Tree *ptree);

/*操作:在树中查找一个项*/
/*前提条件:pi指向一个项,ptree指向一个已初始化的树*/
/*后置条件:查找到了则返回true;否则返回false*/
bool InTree(const Item *pi, const Tree *ptree);

/*操作:从树中删除一项*/
/*前提条件:pi是删除项的地址,ptree指向一个已初始化的树*/
/*后置条件:如果成功删除则返回true,否则返回false*/
bool DeleteItem(const Item *pi, Tree *ptree);

/*操作:把函数应用于树的每一项*/
/*前提条件:ptree指向一个树,pfun指向一个函数,该函数接受一个Item类型的参数,无返回值*/
/*后置条件:pfun指向的函数应用于树中的每一项一次*/
void Traverse(const Tree *ptree, void(*pfun)(Item item));

/*操作:删除树中所有内容*/
/*前提条件:ptree指向一个已初始化的树*/
/*后置条件:树为空*/
void DeleteAll(Tree *ptree);

#endif // _TREE_H_

接口函数

//tree.c//二叉查找树
//本程序中的所有使用递归的函数都是没有返回值的,所以还没那么复杂一点
#include "tree.h"
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct pair{
    Trnode *parent;
    Trnode *child;
}Pair;//用于SeekItem函数的返回值,但是SeekItem()不是公共接口,只是实现实现细节,所以只在本文件可见

/*局部函数的原型*/
static Trnode * MakeNode(const Item *);//分配内存并把项存进去,返回分配的地址的指针
static bool ToLeft(const Item *i1, const Item *i2);//如果第一个参数指向的项在第二个参数指向的项左边则返回true
static bool ToRight(const Item *i1, const Item *i2);
static void AddNode(Trnode *new_node, Trnode *root);//添加一个节点
static void InOrder(const Trnode *root, void(*pfun)(Item item));//超难!!!递归
static Pair SeekItem(const Item *item, const Tree * ptree);
static void DeleteNode(Trnode **ptr);//传入的参数有点难理解,看函数代码就明白,总之(*ptr)是指向待删除项的指针
static void DeleteAllNodes(Trnode *root);

void InitializeTree(Tree * ptree)
{
    ptree->root = NULL;
    ptree->size = 0;
}

bool TreeIsEmpty(const Tree *ptree)
{
    return (ptree->size == 0)?true:false;
    //return (ptree->root == NULL)?true:false;
}

bool TreeIsFull(const Tree *ptree)
{
    return (ptree->size == MAXITEMS)?true:false;
}

unsigned int TreeItemsCount(const Tree *ptree)
{
    return ptree->size;
}

bool AddItem(const Item *pi, Tree *ptree)
{
    Trnode *new_node;

    if(TreeIsFull(ptree))
        {
            fprintf(stderr, "Tree is full!\n");
            return false;
        }
    if(SeekItem(pi, ptree).child != NULL)
    {
        fprintf(stderr, "Attempted to add duplicate item.\n");
        return false;
    }
    new_node = MakeNode(pi);
    if(new_node == NULL)
    {
        fprintf(stderr, "Couldn't create node!\n");
        return false;
    }
    ptree->size++;
    //树为空
    if(ptree->root == NULL)
        ptree->root = new_node;
    else
        AddNode(new_node, ptree->root);

    return true;
}

bool InTree(const Item *item, const Tree *ptree)
{
    return (SeekItem(item, ptree).child)?true:false;
}

/*删除项是和用户交互的公共接口函数,处理的是用户关心的问题,实际上它调用的DeleteNode()才是真正处理指针等实质性任务的函数。*/
bool DeleteItem(const Item *item, Tree *ptree)
{
    Pair look;
    look = SeekItem(item, ptree);
    if(look.child == NULL)
        return false;//没找到这一项
    if(look.parent == NULL)
        DeleteNode(&ptree->root);//删除根节点
    else if(look.parent->left == look.child)
        DeleteNode(&look.parent->left);
    else
        DeleteNode(&look.parent->right);
    ptree->size--;

    return true;
}

/*遍历树*/
void Traverse(const Tree *ptree, void(*pfun)(Item item))
{
    if(ptree != NULL)
        InOrder(ptree->root, pfun);
}

/*清空树,也需要像遍历一样访问每个节点,用free释放内存*/
/*记得重置Tree*的信息*/
/*还是一样,公共接口函数都是要调用一个真正做实事的函数,接口函数只是写点外层的处理*/
void DeleteAll(Tree *ptree)
{
    if(ptree != NULL)
        DeleteAllNodes(ptree->root);
    ptree->root = NULL;
    ptree->size = 0;
}



/*局部静态函数*/
/*不属于公共接口,而是隐藏在tree.c中的处理实现细节的静态函数*/
static Trnode * MakeNode(const Item *pi)
{
    Trnode * new_node;

    new_node = (Trnode *)malloc(sizeof(Trnode));
    if(new_node!=NULL)
    {
        new_node->item = *pi;
        new_node->left = NULL;
        new_node->right = NULL;
    }
    return new_node;
}

/*递归,虽然递归很难理解,但是我总觉得它充满智慧,化复杂为简单*/
static void AddNode(Trnode *new_node, Trnode *root)
{
    //重点是找符合自己的空位
    if(ToLeft(&new_node->item, &root->item))//和根节点比较,决定放在左子树还是右子树
    {
        if(root->left == NULL)
            root->left = new_node;
        else
            AddNode(new_node, root->left);
    }
    else if(ToRight(&new_node->item, &root->item))
    {
        if(root->right == NULL)
            root->right = new_node;
        else
            AddNode(new_node, root->right);
    }
    else{
        fprintf(stderr, "Location error in AddNode()");
        exit(1);
    }
}

static bool ToLeft(const Item *i1, const Item *i2)
{
    int comp1;

    if((comp1 = strcmp(i1->petname, i2->petname)) < 0)
        return true;//小于0表示i1中的宠物名的首字符顺序在前
    else if(comp1 == 0 && strcmp(i1->petkind, i2->petkind)<0)
        return true;
    else//两个都等于0则ToLeft()和ToRight()都返回false
        return false;
}

static bool ToRight(const Item *i1, const Item *i2)
{
    int comp1;//strcmp()返回的是整型

    if((comp1 = strcmp(i1->petname, i2->petname)) > 0)
        return true;
    else if(comp1 == 0 && strcmp(i1->petkind, i2->petkind) > 0)
        return true;
    else
        return false;

}

/*查找项很麻烦,返回两个指针打包为结构体,C不支持多个返回值,python支持*/
static Pair SeekItem(const Item *item, const Tree *ptree)
{
    Pair look;
    look.parent = NULL;//如果返回的父指针为空,则在根节点查找到该项
    look.child = ptree->root;//子指针初始化为根节点,然后逐渐往下找

    if(look.child == NULL)
        return look;//空树,提前返回
    while(look.child != NULL)
    {
        if(ToLeft(item, &look.child->item))
        {
            look.parent = look.child;
            look.child = look.child->left;
        }
        else if(ToRight(item, &look.child->item))
        {
            look.parent = look.child;
            look.child = look.child->right;
        }
        else//相等,如果树中真有要找的那项,则一定会到这里来,从而跳出while循环
            //如果树中没有这一项,则会一直找,直到树的边缘子节点使得look.child为NULL从而跳出循环
            break;
    }

    return look;
    /*
    //我写的递归,不对
    if(ToLeft(item, &look.child->item))
        {
            look.parent = look.child;
            look.child = look.child->left;
            SeekItem(item, look.parent);
        }
    else if(ToRight(item, &look.child->item))
        {
            look.parent = look.child;
            look.child = look.child->right;
            SeekItem(item, look.parent);
        }
    else//相等
        return look;
    */
}



/*删除项最麻烦*/
static void DeleteNode(Trnode **ptr)
{
    //ptr是指向待删除项的父节点的指针成员的指针,很难理解,把这个函数看明白就理解了,指针的指针还有成员,确实很麻烦
    Trnode *temp;

    //待删除项只有一个子节点
    //*ptr指向目标节点,所以*ptr是目标节点的父节点的指针成员
    if((*ptr)->left == NULL)//如果左指针为空,则待删除项有一个右子树
    {
        temp = *ptr;
        *ptr = (*ptr)->right;//别忘记括号!!!
        free(temp);//释放指向目标节点的指针,注意,由于*ptr要变,所以必须借助一个临时指针
    }
    else if((*ptr)->right == NULL)
    {
        temp = *ptr;
        *ptr = (*ptr)->left;
        free(temp);
    }
    else//待删除节点有两个子节点
    {
        for(temp = (*ptr)->left;temp->right!=NULL;temp = temp->right)
            continue;
            //temp = (*ptr)->left使得temp指向目标节点的左子树
            //temp的右指针不是空,则让temp等于temp的右指针,一直向右找下去

        temp->right = (*ptr)->right;//把目标节点的右子树挂在左子树的右子树的右子树的右子树···的末尾
        temp = *ptr;
        *ptr = (*ptr)->left;//目标节点的父节点
        free(temp);
    }

}



/*使用递归,分而治之*/
static void InOrder(const Trnode *root, void(*pfun)(Item item))
{
    if(root!=NULL)
    {
        InOrder(root->left, pfun);
        (*pfun)(root->item);
        InOrder(root->right, pfun);
    }
}



static void DeleteAllNodes(Trnode *root)
{
    Trnode *pright;

    if(root != NULL)
    {
        pright = root->right;//先指向右子树
        DeleteAllNodes(root->left);//递归删除左子树
        free(root);//释放指向根节点的指针
        DeleteAllNodes(pright);//递归删除右子树
    }
}

输出

Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
a
Enter name of pet:
chichi
Enter pet kind:
dog
The pet has been added to the club!
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
a
Enter name of pet:
mimi
Enter pet kind:
cat
The pet has been added to the club!
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
hula
Please enter an a, l, n, f, d, q.
dog
Enter name of pet you wish to drop:
mimi
Enter pet kind
cat
MIMI the CAT is dropped from the club.
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
a
Enter name of pet:
hula
Enter pet kind:
dog
The pet has been added to the club!
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
a
Enter name of pet:
emma
Enter pet kind:
chick
The pet has been added to the club!
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
n
3 pets in club.
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
f
Enter name of your pet you wish to find:
huhu
Enter kind of your pet:
dog
HUHU the DOG is not a member.
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
f
Enter name of your pet you wish to find:
hula
Enter kind of your pet:
dog
HULA the DOG is a member.
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
l
Pet: CHICHI              Kind: DOG
Pet: EMMA                Kind: CHICK
Pet: HULA                Kind: DOG
Nerfville Pet Club Membership Program
Enter the letter corresponding to your choice:
a) add a pet         l) show list of pets
n) number of pets    f) find pets
d) delete a pet      q) quit
q
All of the pets have been deleted!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值