二叉查找樹
二叉查找樹是一種結合了二分查找策略的鏈接結構。二叉樹的每個節點都包含一個項和兩個指向其他節點(稱爲子節點:左節點/右節點)的指針。其順序按照按照如下規定:左節點的項在父節點的項的前面;右節點的項在父節點的項的後面。
二叉樹ADT
二叉查找樹接口(tree.h)
接口中的函數是使用二叉樹ADT的程序員可以操作的:
#ifndef _TREE_H_
#define _TREE_H_
#include<stdbool.h>
#define SLEN 20
typedef struct item
{
char petname[SLEN];
char petkind[SLEN];
} Item;
#define MAXITEMS 10
typedef struct trnode
{
Item item;
struct trnode * left; //指向左分支的指針
struct trnode * right; //指向右分支的指針
} Trnode;
typedef struct tree
{
Trnode * root; //指向根結點的指針
int size; //樹的項數
} Tree;
//函數原型
//初始化樹
void InitializeTree(Tree * ptree);
//確定樹是否爲空
bool TreeIsEmpty(const Tree * ptree);
//確定樹是否爲滿
bool TreeIsFull(const Tree * ptree);
//確定樹的項數
int TreeItemCount(const Tree * ptree);
//在樹中添加一個項
bool AddItem(const Item * pi, Tree * ptree);
//在樹中查找一個項
bool InTree(const Item * pi, Tree * ptree);
//在樹中刪除一個項
bool DeleteItem(const Item * pi, Tree * ptree);
//把函數應用於樹中每一項
void Traverse(const Tree * ptree, void (*pfun) (Item item));
//刪除樹中所有內容
void DeteleAll(Tree * ptree);
#endif
二叉樹的實現(tree.c)
//樹的支持函數
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"tree.h"
//局部數據類型
typedef struct pair {
Trnode * parent;
Trnode * child;
} Pair;
//局部函數原型,這些函數是底層進行的操作
static Trnode * MakeNode(const Item *pi);
static bool ToLeft(const Item *i1, const Item *i2);
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 * pi, const Tree * ptree);
static void DeleteNode(Trnode **ptr);
static void DeleteAllNodes(Trnode * root);
//函數定義
void InitializeTree(Tree * ptree)
{
ptree->root = NULL;
ptree->size = 0;
}
bool TreeIsEmpty(const Tree * ptree)
{
if (ptree->root == NULL)
return true;
else
return false;
}
bool TreeIsFull(const Tree * ptree)
{
if (ptree->size == MAXITEMS)
return true;
else
return false;
}
int TreeItemCount(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 * pi, Tree * ptree)
{
return (SeekItem(pi, ptree).child == NULL) ? false : true;
}
bool DeleteItem(const Item * pi, Tree * ptree)
{
Pair look;
look = SeekItem(pi, ptree);
if (look.child == NULL)
return false;
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);
}
void DeleteAll(Tree * ptree)
{
if (ptree != NULL)
DeleteAllNodes(ptree->root);
ptree->root = NULL;
ptree->size = 0;
}
//局部函數
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);
}
}
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()\n");
exit(1);
}
}
static bool ToLeft(const Item *i1, const Item *i2)
{
int comp1;
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;
}
static bool ToRight(const Item *i1, const Item *i2)
{
int comp1;
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;
}
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 Pair SeekItem(const Item * pi, const Tree * ptree)
{
Pair look;
look.parent = NULL;
look.child = ptree->root;
if (look.child == NULL)
return look;
while (look.child != NULL)
{
if (ToLeft(pi,&(look.child->item)))
{
look.parent = look.child;
look.child = look.child->left;
}
else if (ToRight(pi,&(look.child->item)))
{
look.parent = look.child;
look.child = look.child->right;
}
else
break; //如果前兩項都不滿足,則必定是相等的情況
}
return look;
}
static void DeleteNode(Trnode **ptr)
{
Trnode * temp;
if ((*ptr)->left == NULL)
{
temp = *ptr;
*ptr = (*ptr)->right;
free(temp);
}
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->right = (*ptr)->right;
temp = *ptr;
*ptr = (*ptr)->left;
free(temp);
}
}
主程序(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);
int main(void)
{
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("%d pets in club.\n", TreeItemCount(&pets));
break;
case 'd': droppet(&pets);
break;
default: puts("Switching error");
}
}
DeleteAll(&pets);
puts("Bye.");
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)
{
while (getchar() != '\n') /* discard rest of line */
continue;
ch = tolower(ch);
if (strchr("alrfndq",ch) == NULL)
puts("Please enter an a, l, f, n, d, or q:");
else
break;
}
if (ch == EOF) /* make EOF cause program to quit */
ch = 'q';
return ch;
}
void addpet(Tree * pt)
{
Item temp;
if (TreeIsFull(pt))
puts("No room in the club!");
else
{
puts("Please enter name of pet:");
s_gets(temp.petname,SLEN);
puts("Please enter pet kind:");
s_gets(temp.petkind,SLEN);
uppercase(temp.petname);
uppercase(temp.petkind);
AddItem(&temp, pt);
}
}
void showpets(const Tree * pt)
{
if (TreeIsEmpty(pt))
puts("No entries!");
else
Traverse(pt, printitem);
}
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; /* quit function if tree is empty */
}
puts("Please enter name of pet you wish to find:");
s_gets(temp.petname, SLEN);
puts("Please enter pet kind:");
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; /* quit function if tree is empty */
}
puts("Please enter name of pet you wish to delete:");
s_gets(temp.petname, SLEN);
puts("Please 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");
}
void uppercase(char * str)
{
while (*str)
{
*str = toupper(*str);
str++;
}
}
char * s_gets(char * st, int n)
{
char * ret_val;
char * find;
ret_val = fgets(st, n, stdin);
if (ret_val)
{
find = strchr(st, '\n'); // look for newline
if (find) // if the address is not NULL,
*find = '\0'; // place a null character there
else
while (getchar() != '\n')
continue; // dispose of rest of line
}
return ret_val;
}