二叉树
第一步:建立抽象
第二步:定义接口
第三步:实现接口
主函数
/*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!