c3链表队列二叉树
2010年12月23日
结构和其他数据形式
关键字: struct, union, typedef
运算符: . ->
/* book.c --仅包含一本书的图书目录 */
#include
#define MAXTITL 41
#define MAXAUTL 31
struct book { //结构模板:标记为book
char title[MAXTITL];
char author[MAXAUTL];
float value;
}; //结构模板结束
int main(void)
{
struct book library; //把library声明为book类型的变量
printf("Please enter the book title.\n");
gets(library.title);
printf("Now enter the author.\n");
gets(library.author);
printf("Now enter the value.\n");
scanf("%f", &library.value);
printf("%s by %s: $%.2f\n", library.title, library.author, library.value);
printf("%s: \"%s\" ($%.2f)\n", library.author, library.title, library.value);
printf("Done.\n");
return 0;
}
------------------------------
结构声明(structure declaration)
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
struct book dickens;
struct book library;
--------------
struct { /* 无标记 */
char title[MAXTITL];
char author[MAXTITL];
float value;
} library;
gets(library.title);
scanf("%f", &library.value);
struct book gift = { .value = 25.00,
.author = "James Broadfool",
.title = "Rue for the Toad" };
---------------------------
声明结构数组
struct book library[MAXBKS];
--------------
while (countincome is fellow[0].income if him == &fellow[0]
fellow[0].income == (*him).income
barney.income == (*him).income == him->income // him == &barney
-----------------------------------
/* names.c --使用指向结构的指针 */
#include
#include
struct namect {
char fname[20];
char lname[20];
int letters;
};
void getinfo (struct namect *);
void makeinfo (struct namect *);
void showinfo (const struct namect *);
int main(void)
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
return 0;
}
void getinfo(struct namect * pst)
{
printf("Please enter your first name.\n");
gets(pst->fname);
printf("Please enter you last name.\n");
gets(pst->lname);
}
void makeinfo(struct namect * pst)
{
pst->letters=strlen(pst->fname)+
strlen(pst->lname);
}
void showinfo(const struct namect * pst)
{
printf("%s %s, your name contains %d letters. \n", pst->fname, pst->lname, pst->letters);
}
----------------------------
#define MAXTITL 40
struct book {
char title[MAXTITL];
char author[MAXTITL];
float value;
}
------------------------
C四种处理数据的特性:结构、联合、枚举和typedef
联合(union)是一个能在同一个存储空间里(但不同时)存储不同类型数据的数据类型。
union hold {
int digit;
double bigfl;
char letter;
};
union hold fit; //hold类型的联合变量
union hold save[10]; //10个联合变量的数组
union hold *pu; //指向hold类型变量的指针
----------------
struct {
int code;
float cost;
} item;
item.code=1234;
问接成员运算符:->
----------------------
struct {
int code;
float cost;
} item, * ptrst;
ptrst=&item;
ptrst->code=3451;
最后一条语句将一个int值赋给item的成员code。以下的三个表达式是等价的:
ptrst->code item.code (*ptrst).code
-------------------------------------
枚举类型(enumerated type)声明代表整数常量的符号名称。
enum spectrum {red, orange, yellow, green, blue, violet};
enum spectrum color;
typedef struct { double x; double y; } rect;
------------------------------------------------
void show(void (* fp)(char *), char * str)
{
(*fp)(str); // 把所选函数作用于str
puts(str);
}
-----------------------------
枚举使得您可以创建一组代表整数常量的符号(枚举常量)
typedef工具可用来建立该函数的地址。这个指向函数的地址可作为参数被传递给使用该函数的另一个函数。
----------------------------
位操作
运算符:~ & | ^ >> >= C库
预处理器指令:#define #include #ifdef #else #endif #ifndef #if #elif #line #error #pragma
函数:sqrt(), atan(), aran2(), exit(), atexit(), assert(), memcpy(), memmove(), va_start(), va_arg(), va_copy(), va_end()
类函数宏和条件编译,内联函数
---------------------------------
// names_st.h --names_st结构的头文件
// 常量
#define SLEN 32
//结构声明
struct names_st
{
char first[SLEN];
char last[SLEN];
}
//类型定义
typedef struct names_st names;
//函数原型
void get_names(names *);
void show_names(const name *);
这处文件中含有头文件中常见的许多内容:#define指令、结构声明、typedef语句和函数原型。
--------------------------------
#include和#define指令是最常用的C预处理器的两个功能。
#undef指令取消定义一个给定的#define。
#define LIMIT 400
#undef LIMIT
------------------
// names_st.h --带有多次包含保护的修订版本
#ifndef NAMES_H_
#define NAMES_H_
// 常量
#define SLEN 32
//结构声明
struct names_st
{
char first[SLEN];
char last[SLEN];
};
//类型定义
typedef struct names_st names;
//函数原型
void get_names(names *);
void show_names(const names *);
#endif
------------------
使用头文件
头文件的内容的最常见的形式包括:
明显常量--例如典型的stdio.h文件定义EOF、NULL和BUFSIZE(标准I/O缓冲区的大小)。
宏函数--例如getchar()通常被定义为getc(stdin),getc()通常被定义为较复杂的宏,而头文件ctype.h通常包含ctype函数的宏定义。
函数声明--例如头文件string.h包含字符串函数系列的函数声明。在ANSI C中,声明采用函数原型形式。
结构模板定义--标准I/O函数使用FILE结构,该结构包含文件及文件相关缓冲区的信息。头文件stdio.h中存放FILE结构的声明。
类型定义--可以使用指向FILE的指针作为参数调用标准I/O函数。通常,stdio.h用#define或typedef使得FILE代表指向FILE结构的指针。与之类似,size_t和time_t类型也在头文件中定义。
可以使用头文件来声明多个文件共享的外部变量。
使用具有文件作用域、内部链接和const限定词的变量或数组。
使用具有文件作用域、内部链接和const限定词的变量或数组。
使用const可以避免值被意外地改变。
使用static后,每个包含该头文件的文件都获得一份该变量的副本。
--------------------------------
其他指令:#undef
条件编译:#ifdef #else #endif #ifndef #if #elif #line #error #pragma
---------------------
预定义宏
__DATE__ 进行预处理的日期("Mmm dd yyyy"形式的字符串文字
__FILE__ 代表当前源代码文件名的字符串文字
__LINE__ 代表当前源代码文件中的行号的整数常量
__STDC__ 设置为1时,表示该实现遵循C标准
__STDC_HOSTED__ 为本机环境设置为1,否则设为0
__STDC_VERSION__ 为C99时设置为199901L
__TIME__ 源文件编译时间,格式为"hh:mm:ss"
__func__ 当前函数名
------------------------
内联函数(inline function)
把函数变为内联函数将建议编译器尽可能快速地调用该函数。
创建内联函数的方法是在函数声明中使用函数说明符inline。
---------------------
C库:数学库、通用工具库(包含随机数产生函数、搜索和排序函数、转换函数、内存管理函数)、诊断库
atexit()函数使用函数指针作为参数
ANSI C中,在非递归的main()函数中使用exit()函数等价于使用关键字return。
qsort()
C99标准ANSI C库:诊断assert.h 复数complex.h 字符处理ctype.h 错误报告errno.h 浮点数环境fenv.h 整数类型的格式转换inttypes.h 本地化locale.h 数学库math.h 非本地跳转setjmp.h 信号处理signal.h 可变参数stdarg.h 布尔支持stdbool.h 通用定义stddef.h 整数类型stdint.h 标准I/O库stdio.h 通用工具stdlib.h 字符处理string.h 通用类型数学tgmath.h 日期和时间time.h 扩展的多字节字符和宽字符工具wchar.h 宽字符分类和映射工具wctype.h 可选的拼写iso646.h
---------------------------
高级数据表示
用C表示多种数据类型
新的算法,以及增强您从概念上开发程序的能力
抽象数据类型(Abstract data type, ADT)
结构本身不能含有同类型的结构,但是它可以含有指向同类型结构的指针。这样的定义是定义一个链表(linked list)的基础。链表是一个列表,其中的每一项都包含描述何处能找到下一项的信息。
// filmlist.c --使用结构链表
#include
#include //提供malloc()原型
#include //提供strcpy()原型
#define TSIZE 45 //存放片名的数组大小
struct film {
char title[TSIZE];
int rating;
struct film * next; //指向链表的下一个结构
};
int main(void)
{
struct film * head = NULL;
struct film * prev, * current;
char input[TSIZE];
//收集并存储信息
puts("Enter first movie title: ");
while (gets(input) != NULL && input[0] != '\0')
{
current=(struct film * malloc(sizeof(struct film));
if (head == NULL) //第一个结构
head = current;
else //后续结构
prev->next=current;
current->next=NULL;
strcpy(current->title, input);
puts("Enter your rating : ");
scanf("%d", ¤t->rating);
while (getchar() != '\n')
continue;
puts("Enter next movie title (empty line to stop): ");
prev=current;
}
//给出电影列表
if (head==NULL)
printf("No data entered. ");
else
printf("Here is the movie list: \n");
current=head;
while(current != NULL)
{
printf("Movie: %s Rating: %d\n", current->title, current->rating);
current=current->next;
}
//任务已完成,因此释放所分配的内存
current=head;
while (current != NULL)
{
free(current);
current=current->next;
}
printf("Bye!\n");
return 0;
}
----------------------------
用抽象数据类型方法进行C语言编程包含下面三个步骤:
1.以抽象、通用的方式描述一个类型,包括其操作。
2.设计一个函数接口来表示这种新类型。
3.编写具体代码以实现这个接口。
--------------------
队列(queue)是具有两个特殊属性的列表:
第一、新的项目只能被添加到列表结尾处
第二、项目只能从列表开始处被移除
// queue.h --队列接口头文件
#pragma c9x on
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include
//Item定义类型
typedef int Item;
#define MAXQUEUE 10
typedef struct node
{
Item item;
struct node * next;
} Node;
typedef struct queue
{
Node * front;
Node * rear;
int items;
} Queue;
void InitializeQueue(Queue * pq);
bool QueueIsFull(const Queue * pg);
bool QueueIsEmpty(const Queue * pg);
int QueueItemCount(const Queue * pg);
bool EnQueue(Item item, Queue * pg);
bool DeQueue(Item * pitem, Queue * pg);
void EmptyTheQueue(Queue * pg);
#endif
---------------
//实现接口函数
void InitializeQueue(Queue * pg)
{
pg->front=pq->rear=NULL;
pg->items=0;
}
bool QueueIsFull(const Queue * pq)
{
return pq->items == MAXQUEUE;
}
bool QueueIsEmpty(const Queue * pg)
{
return pq->items==0;
}
int QueueItemCount(const Queue * pq)
{
return pq->items;
}
bool EnQueue(Item item, Queue * pq)
{
Node * pnew;
if(QueueIsFull(pq))
return false;
pnew=(Node *)malloc(sizeof (Node));
if(pnew==NULL)
{
fprintf(stderr, "Unable to allocte memory!\n");
exit(1);
}
CopyToNode(item, pnew);
pnew->next=NULL;
if(QueueIsEmpty(pq))
pq->front=pnew; //项目位于队列首端
else
pq->rear->next=pnew; //链接到队列尾端
pq->rear=pnew; //记录队列尾端的位置
pq->items++; //队列项目个数增1
return true;
}
static void CopyToNode(Item item, Node * pn)
{
pn->item=item;
}
bool DeQueue(Item * pitem, Queue * pq)
{
Node * pt;
if (QueueIsEmpty(pq))
return false;
CopyToItem(pq->front, pitem);
pt=pq->front;
pq->front=pq->front->next;
free(pt);
pq->item--;
if(pq->items==0)
pq->rear=NULL;
return true;
}
void EmptyTheQueue(Queue * pq)
{
Item dummy;
while(!QueueIsEmpty(pq))
DeQueue(&dummy, pq);
}
---------------------
// queue.c --队列类型的实现文件
#include
#include
#include "queue.h"
//局部函数
static void CopyToNode(Item item, Node * pn);
static void CopyToItem(Node * pn, Item * pi);
void InitializeQueue(Queue * pq)
{
pq->front=pq->rear=NULL;
pq->items=0;
}
bool QueueIsFull(const Queue * pq)
{
return pq->items==MAXQUEUE;
}
bool QueueIsEmpty(const Queue * pq)
{
return pq->items==0;
}
int QueueItemCount(const Queue * pq)
{
return pq->items;
}
bool EnQueue(Item item, Queue * pq)
{
Node * pnew;
if(QueueIsFull(pq))
return false;
pnew=(Node *)malloc(sizeof(Node));
if(pnew==NULL)
{
fprintf(stderr, "Unable to allocate memory!\n");
exit(1);
}
CopyToNode(item, pnew);
pnew->next=NULL;
if(QueueIsEmpty(pq))
pq->front=pnew;
else
pq->rear->next=pnew
pq->rear=pnew;
pq->items++;
return true;
}
bool DeQueue(Item * pitem, Queue * pq)
{
Node * pt;
if (QueueIsEmpty(pq))
return false;
CopyToItem(pq->front, pitem);
pt=pq->front;
pq=front=pq->front->next;
free(pt);
pq->items--;
if(pq->items==0)
pq->rear==NULL;
return true;
}
void EmptyTheQueue(Queue * pq)
{
Item dummy;
while (!QueueIsEmpty(pq))
DeQueue(&dummy, pq);
}
//局部函数
static void CopyToNode(Item item, Node * pn)
{
pn->item=item;
}
static void CopyToItem(Node * pn, Item * pi)
{
*pi= pn->item;
}
------------------
// use_q.c --测试Queue接口的驱动程序
// compile with queue.c
#include
#include "queue.h"
int main(void)
{
Queue line;
Item temp;
char ch;
InitializeQueue(&line);
puts("Testing the Queue interface. Type a to add a value, ");
puts("type d to delete a value, and type q to quit.");
while ((ch=getchar()) != 'q')
{
if(ch != 'a' && ch != 'd')
continue;
if(ch == 'a')
{
printf("Integer to add: ");
scanf("%d", &temp);
if(!QueueIsFull(&line))
{
printf("Putting %d into queue\n", temp);
EnQueue(temp, &line);
}
else
puts("Queue is full!");
}
else
{
if (QueueIsEmpty(&line))
puts("Nothing to delete!");
else
{
DeQueue(&temp, &line);
printf("Removing %d from queue\n", temp);
}
}
printf("%d items in queue\n", QueueItemCount(&line));
puts("Type a to add, d to delete, q to quit: ");
}
EmptyTheQueue(&line);
puts("Bye!");
return 0;
}
----------------------
对数组来说,可以用数组索引直接访问任意元素。这称为随机访问(random access)。
对链表来说,必须从列表头开始,逐个节点地移动到所需的节点外,这叫做顺序访问(sequential access)。
二叉搜索树(binary search tree)是一种结合了折关搜索策略的链接结构。树中的每一个节点都包含一个项目和两个指向其他节点(称为子节点,child node)的指针。
----------------------
二叉搜索树的接口
typedef SOMETHING Item;
typedef struct node
{
Item item;
struct node * left;
struct node * right;
} Node;
typedef struct tree
{
Node * root;
int size;
} Tree;
-----------------------
// tree.h二叉搜索树接口头文件
// 树中不允许有相同的项目
#ifndef _TREE_H_
#define _TREE_H_
#include
typedef struct item
{
char petname[20];
char petname[20];
} Item;
#define MAXITEMS 10
typedef struct node
{
Item item;
struct node * left;
struct node * right;
} Node;
typedef struct tree
{
Node * 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, const Tree * ptree);
bool DeleteItem(const Item * pi, Tree * ptree);
void Traverse(const Tree * ptree, void(*pfun)(Item item));
void DeleteAll(Tree * ptree);
#endif
-----------------
bool AddItem(const Item * pi, Tree * ptree)
{
Node * new_node;
if (TreeIsFull(ptree))
{
fprint(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->seize++;
if (ptree->root == NULL)
ptree->root = new_node;
else
AddNode(new_node, ptree->root);
return true;
}
static Node * MakeNode(const Item * pi)
{
Node * new_node;
new_node=(Node *)malloc(sizeof(Node));
if(new_node != NULL)
{
new_node->item=*pi;
new_node=NULL;
new_node=NULL;
}
return new_node;
}
static void AddNode(Node * new_node, Node * 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))petlind, i2->petkind)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;
}
bool InTree(const Item * pi, const Tree * ptree)
{
return (SeekItem(pi, ptree).child==NULL) ? false : true;
}
static void DeleteNode(Node **ptr)
// ptr是指向目标节点的父节点指针成员的地址
{
Node * temp;
puts((*ptr)->item.petname);
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);
}
}
bool DeleteItem(const Item * pi, Tree * ptree)
{
Pair look;
look = SeekItem(pi, 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);
}
static void InOrder(const Node * root, void (* pfun)(Item item)
{
if(root != NULL)
{
InOrder(root->left, pfun);
(*pfun)(root->item);
InOrder(root->right, pfun);
}
}
void DeleteAll(Tree * ptree)
{
if(ptree != NULL)
DeleteAllNodes(ptree->root);
ptree->root=NULL;
ptree->size=0;
}
static void DeleteAllNodes(Node * root)
{
Node * pright;
if (root != NULL)
{
pright=root->right;
DeleteAllNodes(root->left);
free(root);
DeleteAllNodes(pright);
}
}
------------------------------
------------------------------
重点完整的包
// tree.c --树类型的支持函数
#include
#include
#include
#include "tree.h"
//局部数据类型
typedef struct pair{
Node * parent;
Node * child;
} Pair;
//局部函数的原型
static Node * 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 (Node * new_node, Node * root);
static void InOrder(const Node * root, void (* pfun) (Item item));
static Pair SeekItem(const Item * pi, const Tree * ptree);
static void DeleteNode(Node ** ptr);
static void DeletAllNodes(Node * ptr);
//函数定义
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)
{
Node * 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, const 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;
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);
}
void DeleteAll(Tree * ptree)
{
if(ptree!=NULL)
DeleteAllNodes(ptree->root);
ptree->root=NULL;
ptree->size=0;
}
static void InOrder(const Node * root, void (* pfun)(Item item))
{
if(root != NULL)
{
InOrder(root->left,pfun);
(*pfun)(root->item);
InOrder(root->right,pfun);
}
}
static void DeleteAllNodes(Node * root)
{
Node * pright;
if (root != NULL)
{
pright=root->right;
DeleteAllNodes(root->left);
free(root);
DeleteAllNodes(pright);
}
}
static void AddNode (Node * new_node, Node * 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))petkind, i2->petkind)petname, i2->petname)) > 0)
return true;
else if (comp1==0 && strcmp(i1->petkind, i2->petkind) > 0)
return true;
else
return false;
}
static Node * MakeNode(const Item * pi)
{
Node * new_node;
new_node=(Node *)malloc(sizeof(Node));
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(Node **ptr)
{
Node * temp;
puts((*ptr)->item.petname);
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
#include
#include
#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);
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 clud\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 Menbership 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') //丢弃输入行的剩余部分
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)
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: ");
gets(temp.petname);
puts("Please enter pet kind: ");
gets(temp.petkind);
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;
}
puts("Please enter name of pet you wish to find: ");
gets(temp.petname);
puts("Please enter pet kind: ");
gets(temp.petkind);
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 a member.\n");
}
void droppet(Tree * pt)
{
Item temp;
if(TreeIsEmpty(pt))
{
puts("No enties!");
return;
}
puts("Please enter name of pet you wish to delete: ");
gets(temp.petname);
puts("Please enter pet kind: ");
gets(temp.petkind);
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++;
}
}
------------------------------------
一种数据类型是以如下几点为特征的:数据如何构建、如何存储,以及有什么可能的振作。抽象数据类型(ADT)以抽象方式指定构成某种类型特征的属性和操作。
从概念上讲,可以分两步将ADT翻译成一种具体的程序语言。第一步是定义编程接口。在C中,您通过在头文件中定义类型名并提供执行允许的和作的函数原型来实现。第二步是实现接口。在C中,可用源代码文件提供与原型相应的函数定义来实现。
一种数据类型是以如下几点为特征的:数据如何构建、如何存储,以及有什么可能的振作。抽象数据类型(ADT)以抽象方式指定构成某种类型特征的属性和操作。
列表、队列和二叉树是ADT的实例,在计算机程序设计中常常用到。通常用动态内存分配和链接结构来实现它们,但有时数组是实现它们的更好选择。
当使用一种特定的类型(如队列或树)进行编程的时候,要按照该类型的接口来写程序。那样的话,修改或改进实现时就无须更改使用接口的程序。
2010年12月23日
结构和其他数据形式
关键字: struct, union, typedef
运算符: . ->
/* book.c --仅包含一本书的图书目录 */
#include
#define MAXTITL 41
#define MAXAUTL 31
struct book { //结构模板:标记为book
char title[MAXTITL];
char author[MAXAUTL];
float value;
}; //结构模板结束
int main(void)
{
struct book library; //把library声明为book类型的变量
printf("Please enter the book title.\n");
gets(library.title);
printf("Now enter the author.\n");
gets(library.author);
printf("Now enter the value.\n");
scanf("%f", &library.value);
printf("%s by %s: $%.2f\n", library.title, library.author, library.value);
printf("%s: \"%s\" ($%.2f)\n", library.author, library.title, library.value);
printf("Done.\n");
return 0;
}
------------------------------
结构声明(structure declaration)
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
struct book dickens;
struct book library;
--------------
struct { /* 无标记 */
char title[MAXTITL];
char author[MAXTITL];
float value;
} library;
gets(library.title);
scanf("%f", &library.value);
struct book gift = { .value = 25.00,
.author = "James Broadfool",
.title = "Rue for the Toad" };
---------------------------
声明结构数组
struct book library[MAXBKS];
--------------
while (countincome is fellow[0].income if him == &fellow[0]
fellow[0].income == (*him).income
barney.income == (*him).income == him->income // him == &barney
-----------------------------------
/* names.c --使用指向结构的指针 */
#include
#include
struct namect {
char fname[20];
char lname[20];
int letters;
};
void getinfo (struct namect *);
void makeinfo (struct namect *);
void showinfo (const struct namect *);
int main(void)
{
struct namect person;
getinfo(&person);
makeinfo(&person);
showinfo(&person);
return 0;
}
void getinfo(struct namect * pst)
{
printf("Please enter your first name.\n");
gets(pst->fname);
printf("Please enter you last name.\n");
gets(pst->lname);
}
void makeinfo(struct namect * pst)
{
pst->letters=strlen(pst->fname)+
strlen(pst->lname);
}
void showinfo(const struct namect * pst)
{
printf("%s %s, your name contains %d letters. \n", pst->fname, pst->lname, pst->letters);
}
----------------------------
#define MAXTITL 40
struct book {
char title[MAXTITL];
char author[MAXTITL];
float value;
}
------------------------
C四种处理数据的特性:结构、联合、枚举和typedef
联合(union)是一个能在同一个存储空间里(但不同时)存储不同类型数据的数据类型。
union hold {
int digit;
double bigfl;
char letter;
};
union hold fit; //hold类型的联合变量
union hold save[10]; //10个联合变量的数组
union hold *pu; //指向hold类型变量的指针
----------------
struct {
int code;
float cost;
} item;
item.code=1234;
问接成员运算符:->
----------------------
struct {
int code;
float cost;
} item, * ptrst;
ptrst=&item;
ptrst->code=3451;
最后一条语句将一个int值赋给item的成员code。以下的三个表达式是等价的:
ptrst->code item.code (*ptrst).code
-------------------------------------
枚举类型(enumerated type)声明代表整数常量的符号名称。
enum spectrum {red, orange, yellow, green, blue, violet};
enum spectrum color;
typedef struct { double x; double y; } rect;
------------------------------------------------
void show(void (* fp)(char *), char * str)
{
(*fp)(str); // 把所选函数作用于str
puts(str);
}
-----------------------------
枚举使得您可以创建一组代表整数常量的符号(枚举常量)
typedef工具可用来建立该函数的地址。这个指向函数的地址可作为参数被传递给使用该函数的另一个函数。
----------------------------
位操作
运算符:~ & | ^ >> >= C库
预处理器指令:#define #include #ifdef #else #endif #ifndef #if #elif #line #error #pragma
函数:sqrt(), atan(), aran2(), exit(), atexit(), assert(), memcpy(), memmove(), va_start(), va_arg(), va_copy(), va_end()
类函数宏和条件编译,内联函数
---------------------------------
// names_st.h --names_st结构的头文件
// 常量
#define SLEN 32
//结构声明
struct names_st
{
char first[SLEN];
char last[SLEN];
}
//类型定义
typedef struct names_st names;
//函数原型
void get_names(names *);
void show_names(const name *);
这处文件中含有头文件中常见的许多内容:#define指令、结构声明、typedef语句和函数原型。
--------------------------------
#include和#define指令是最常用的C预处理器的两个功能。
#undef指令取消定义一个给定的#define。
#define LIMIT 400
#undef LIMIT
------------------
// names_st.h --带有多次包含保护的修订版本
#ifndef NAMES_H_
#define NAMES_H_
// 常量
#define SLEN 32
//结构声明
struct names_st
{
char first[SLEN];
char last[SLEN];
};
//类型定义
typedef struct names_st names;
//函数原型
void get_names(names *);
void show_names(const names *);
#endif
------------------
使用头文件
头文件的内容的最常见的形式包括:
明显常量--例如典型的stdio.h文件定义EOF、NULL和BUFSIZE(标准I/O缓冲区的大小)。
宏函数--例如getchar()通常被定义为getc(stdin),getc()通常被定义为较复杂的宏,而头文件ctype.h通常包含ctype函数的宏定义。
函数声明--例如头文件string.h包含字符串函数系列的函数声明。在ANSI C中,声明采用函数原型形式。
结构模板定义--标准I/O函数使用FILE结构,该结构包含文件及文件相关缓冲区的信息。头文件stdio.h中存放FILE结构的声明。
类型定义--可以使用指向FILE的指针作为参数调用标准I/O函数。通常,stdio.h用#define或typedef使得FILE代表指向FILE结构的指针。与之类似,size_t和time_t类型也在头文件中定义。
可以使用头文件来声明多个文件共享的外部变量。
使用具有文件作用域、内部链接和const限定词的变量或数组。
使用具有文件作用域、内部链接和const限定词的变量或数组。
使用const可以避免值被意外地改变。
使用static后,每个包含该头文件的文件都获得一份该变量的副本。
--------------------------------
其他指令:#undef
条件编译:#ifdef #else #endif #ifndef #if #elif #line #error #pragma
---------------------
预定义宏
__DATE__ 进行预处理的日期("Mmm dd yyyy"形式的字符串文字
__FILE__ 代表当前源代码文件名的字符串文字
__LINE__ 代表当前源代码文件中的行号的整数常量
__STDC__ 设置为1时,表示该实现遵循C标准
__STDC_HOSTED__ 为本机环境设置为1,否则设为0
__STDC_VERSION__ 为C99时设置为199901L
__TIME__ 源文件编译时间,格式为"hh:mm:ss"
__func__ 当前函数名
------------------------
内联函数(inline function)
把函数变为内联函数将建议编译器尽可能快速地调用该函数。
创建内联函数的方法是在函数声明中使用函数说明符inline。
---------------------
C库:数学库、通用工具库(包含随机数产生函数、搜索和排序函数、转换函数、内存管理函数)、诊断库
atexit()函数使用函数指针作为参数
ANSI C中,在非递归的main()函数中使用exit()函数等价于使用关键字return。
qsort()
C99标准ANSI C库:诊断assert.h 复数complex.h 字符处理ctype.h 错误报告errno.h 浮点数环境fenv.h 整数类型的格式转换inttypes.h 本地化locale.h 数学库math.h 非本地跳转setjmp.h 信号处理signal.h 可变参数stdarg.h 布尔支持stdbool.h 通用定义stddef.h 整数类型stdint.h 标准I/O库stdio.h 通用工具stdlib.h 字符处理string.h 通用类型数学tgmath.h 日期和时间time.h 扩展的多字节字符和宽字符工具wchar.h 宽字符分类和映射工具wctype.h 可选的拼写iso646.h
---------------------------
高级数据表示
用C表示多种数据类型
新的算法,以及增强您从概念上开发程序的能力
抽象数据类型(Abstract data type, ADT)
结构本身不能含有同类型的结构,但是它可以含有指向同类型结构的指针。这样的定义是定义一个链表(linked list)的基础。链表是一个列表,其中的每一项都包含描述何处能找到下一项的信息。
// filmlist.c --使用结构链表
#include
#include //提供malloc()原型
#include //提供strcpy()原型
#define TSIZE 45 //存放片名的数组大小
struct film {
char title[TSIZE];
int rating;
struct film * next; //指向链表的下一个结构
};
int main(void)
{
struct film * head = NULL;
struct film * prev, * current;
char input[TSIZE];
//收集并存储信息
puts("Enter first movie title: ");
while (gets(input) != NULL && input[0] != '\0')
{
current=(struct film * malloc(sizeof(struct film));
if (head == NULL) //第一个结构
head = current;
else //后续结构
prev->next=current;
current->next=NULL;
strcpy(current->title, input);
puts("Enter your rating : ");
scanf("%d", ¤t->rating);
while (getchar() != '\n')
continue;
puts("Enter next movie title (empty line to stop): ");
prev=current;
}
//给出电影列表
if (head==NULL)
printf("No data entered. ");
else
printf("Here is the movie list: \n");
current=head;
while(current != NULL)
{
printf("Movie: %s Rating: %d\n", current->title, current->rating);
current=current->next;
}
//任务已完成,因此释放所分配的内存
current=head;
while (current != NULL)
{
free(current);
current=current->next;
}
printf("Bye!\n");
return 0;
}
----------------------------
用抽象数据类型方法进行C语言编程包含下面三个步骤:
1.以抽象、通用的方式描述一个类型,包括其操作。
2.设计一个函数接口来表示这种新类型。
3.编写具体代码以实现这个接口。
--------------------
队列(queue)是具有两个特殊属性的列表:
第一、新的项目只能被添加到列表结尾处
第二、项目只能从列表开始处被移除
// queue.h --队列接口头文件
#pragma c9x on
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include
//Item定义类型
typedef int Item;
#define MAXQUEUE 10
typedef struct node
{
Item item;
struct node * next;
} Node;
typedef struct queue
{
Node * front;
Node * rear;
int items;
} Queue;
void InitializeQueue(Queue * pq);
bool QueueIsFull(const Queue * pg);
bool QueueIsEmpty(const Queue * pg);
int QueueItemCount(const Queue * pg);
bool EnQueue(Item item, Queue * pg);
bool DeQueue(Item * pitem, Queue * pg);
void EmptyTheQueue(Queue * pg);
#endif
---------------
//实现接口函数
void InitializeQueue(Queue * pg)
{
pg->front=pq->rear=NULL;
pg->items=0;
}
bool QueueIsFull(const Queue * pq)
{
return pq->items == MAXQUEUE;
}
bool QueueIsEmpty(const Queue * pg)
{
return pq->items==0;
}
int QueueItemCount(const Queue * pq)
{
return pq->items;
}
bool EnQueue(Item item, Queue * pq)
{
Node * pnew;
if(QueueIsFull(pq))
return false;
pnew=(Node *)malloc(sizeof (Node));
if(pnew==NULL)
{
fprintf(stderr, "Unable to allocte memory!\n");
exit(1);
}
CopyToNode(item, pnew);
pnew->next=NULL;
if(QueueIsEmpty(pq))
pq->front=pnew; //项目位于队列首端
else
pq->rear->next=pnew; //链接到队列尾端
pq->rear=pnew; //记录队列尾端的位置
pq->items++; //队列项目个数增1
return true;
}
static void CopyToNode(Item item, Node * pn)
{
pn->item=item;
}
bool DeQueue(Item * pitem, Queue * pq)
{
Node * pt;
if (QueueIsEmpty(pq))
return false;
CopyToItem(pq->front, pitem);
pt=pq->front;
pq->front=pq->front->next;
free(pt);
pq->item--;
if(pq->items==0)
pq->rear=NULL;
return true;
}
void EmptyTheQueue(Queue * pq)
{
Item dummy;
while(!QueueIsEmpty(pq))
DeQueue(&dummy, pq);
}
---------------------
// queue.c --队列类型的实现文件
#include
#include
#include "queue.h"
//局部函数
static void CopyToNode(Item item, Node * pn);
static void CopyToItem(Node * pn, Item * pi);
void InitializeQueue(Queue * pq)
{
pq->front=pq->rear=NULL;
pq->items=0;
}
bool QueueIsFull(const Queue * pq)
{
return pq->items==MAXQUEUE;
}
bool QueueIsEmpty(const Queue * pq)
{
return pq->items==0;
}
int QueueItemCount(const Queue * pq)
{
return pq->items;
}
bool EnQueue(Item item, Queue * pq)
{
Node * pnew;
if(QueueIsFull(pq))
return false;
pnew=(Node *)malloc(sizeof(Node));
if(pnew==NULL)
{
fprintf(stderr, "Unable to allocate memory!\n");
exit(1);
}
CopyToNode(item, pnew);
pnew->next=NULL;
if(QueueIsEmpty(pq))
pq->front=pnew;
else
pq->rear->next=pnew
pq->rear=pnew;
pq->items++;
return true;
}
bool DeQueue(Item * pitem, Queue * pq)
{
Node * pt;
if (QueueIsEmpty(pq))
return false;
CopyToItem(pq->front, pitem);
pt=pq->front;
pq=front=pq->front->next;
free(pt);
pq->items--;
if(pq->items==0)
pq->rear==NULL;
return true;
}
void EmptyTheQueue(Queue * pq)
{
Item dummy;
while (!QueueIsEmpty(pq))
DeQueue(&dummy, pq);
}
//局部函数
static void CopyToNode(Item item, Node * pn)
{
pn->item=item;
}
static void CopyToItem(Node * pn, Item * pi)
{
*pi= pn->item;
}
------------------
// use_q.c --测试Queue接口的驱动程序
// compile with queue.c
#include
#include "queue.h"
int main(void)
{
Queue line;
Item temp;
char ch;
InitializeQueue(&line);
puts("Testing the Queue interface. Type a to add a value, ");
puts("type d to delete a value, and type q to quit.");
while ((ch=getchar()) != 'q')
{
if(ch != 'a' && ch != 'd')
continue;
if(ch == 'a')
{
printf("Integer to add: ");
scanf("%d", &temp);
if(!QueueIsFull(&line))
{
printf("Putting %d into queue\n", temp);
EnQueue(temp, &line);
}
else
puts("Queue is full!");
}
else
{
if (QueueIsEmpty(&line))
puts("Nothing to delete!");
else
{
DeQueue(&temp, &line);
printf("Removing %d from queue\n", temp);
}
}
printf("%d items in queue\n", QueueItemCount(&line));
puts("Type a to add, d to delete, q to quit: ");
}
EmptyTheQueue(&line);
puts("Bye!");
return 0;
}
----------------------
对数组来说,可以用数组索引直接访问任意元素。这称为随机访问(random access)。
对链表来说,必须从列表头开始,逐个节点地移动到所需的节点外,这叫做顺序访问(sequential access)。
二叉搜索树(binary search tree)是一种结合了折关搜索策略的链接结构。树中的每一个节点都包含一个项目和两个指向其他节点(称为子节点,child node)的指针。
----------------------
二叉搜索树的接口
typedef SOMETHING Item;
typedef struct node
{
Item item;
struct node * left;
struct node * right;
} Node;
typedef struct tree
{
Node * root;
int size;
} Tree;
-----------------------
// tree.h二叉搜索树接口头文件
// 树中不允许有相同的项目
#ifndef _TREE_H_
#define _TREE_H_
#include
typedef struct item
{
char petname[20];
char petname[20];
} Item;
#define MAXITEMS 10
typedef struct node
{
Item item;
struct node * left;
struct node * right;
} Node;
typedef struct tree
{
Node * 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, const Tree * ptree);
bool DeleteItem(const Item * pi, Tree * ptree);
void Traverse(const Tree * ptree, void(*pfun)(Item item));
void DeleteAll(Tree * ptree);
#endif
-----------------
bool AddItem(const Item * pi, Tree * ptree)
{
Node * new_node;
if (TreeIsFull(ptree))
{
fprint(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->seize++;
if (ptree->root == NULL)
ptree->root = new_node;
else
AddNode(new_node, ptree->root);
return true;
}
static Node * MakeNode(const Item * pi)
{
Node * new_node;
new_node=(Node *)malloc(sizeof(Node));
if(new_node != NULL)
{
new_node->item=*pi;
new_node=NULL;
new_node=NULL;
}
return new_node;
}
static void AddNode(Node * new_node, Node * 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))petlind, i2->petkind)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;
}
bool InTree(const Item * pi, const Tree * ptree)
{
return (SeekItem(pi, ptree).child==NULL) ? false : true;
}
static void DeleteNode(Node **ptr)
// ptr是指向目标节点的父节点指针成员的地址
{
Node * temp;
puts((*ptr)->item.petname);
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);
}
}
bool DeleteItem(const Item * pi, Tree * ptree)
{
Pair look;
look = SeekItem(pi, 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);
}
static void InOrder(const Node * root, void (* pfun)(Item item)
{
if(root != NULL)
{
InOrder(root->left, pfun);
(*pfun)(root->item);
InOrder(root->right, pfun);
}
}
void DeleteAll(Tree * ptree)
{
if(ptree != NULL)
DeleteAllNodes(ptree->root);
ptree->root=NULL;
ptree->size=0;
}
static void DeleteAllNodes(Node * root)
{
Node * pright;
if (root != NULL)
{
pright=root->right;
DeleteAllNodes(root->left);
free(root);
DeleteAllNodes(pright);
}
}
------------------------------
------------------------------
重点完整的包
// tree.c --树类型的支持函数
#include
#include
#include
#include "tree.h"
//局部数据类型
typedef struct pair{
Node * parent;
Node * child;
} Pair;
//局部函数的原型
static Node * 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 (Node * new_node, Node * root);
static void InOrder(const Node * root, void (* pfun) (Item item));
static Pair SeekItem(const Item * pi, const Tree * ptree);
static void DeleteNode(Node ** ptr);
static void DeletAllNodes(Node * ptr);
//函数定义
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)
{
Node * 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, const 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;
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);
}
void DeleteAll(Tree * ptree)
{
if(ptree!=NULL)
DeleteAllNodes(ptree->root);
ptree->root=NULL;
ptree->size=0;
}
static void InOrder(const Node * root, void (* pfun)(Item item))
{
if(root != NULL)
{
InOrder(root->left,pfun);
(*pfun)(root->item);
InOrder(root->right,pfun);
}
}
static void DeleteAllNodes(Node * root)
{
Node * pright;
if (root != NULL)
{
pright=root->right;
DeleteAllNodes(root->left);
free(root);
DeleteAllNodes(pright);
}
}
static void AddNode (Node * new_node, Node * 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))petkind, i2->petkind)petname, i2->petname)) > 0)
return true;
else if (comp1==0 && strcmp(i1->petkind, i2->petkind) > 0)
return true;
else
return false;
}
static Node * MakeNode(const Item * pi)
{
Node * new_node;
new_node=(Node *)malloc(sizeof(Node));
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(Node **ptr)
{
Node * temp;
puts((*ptr)->item.petname);
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
#include
#include
#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);
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 clud\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 Menbership 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') //丢弃输入行的剩余部分
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)
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: ");
gets(temp.petname);
puts("Please enter pet kind: ");
gets(temp.petkind);
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;
}
puts("Please enter name of pet you wish to find: ");
gets(temp.petname);
puts("Please enter pet kind: ");
gets(temp.petkind);
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 a member.\n");
}
void droppet(Tree * pt)
{
Item temp;
if(TreeIsEmpty(pt))
{
puts("No enties!");
return;
}
puts("Please enter name of pet you wish to delete: ");
gets(temp.petname);
puts("Please enter pet kind: ");
gets(temp.petkind);
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++;
}
}
------------------------------------
一种数据类型是以如下几点为特征的:数据如何构建、如何存储,以及有什么可能的振作。抽象数据类型(ADT)以抽象方式指定构成某种类型特征的属性和操作。
从概念上讲,可以分两步将ADT翻译成一种具体的程序语言。第一步是定义编程接口。在C中,您通过在头文件中定义类型名并提供执行允许的和作的函数原型来实现。第二步是实现接口。在C中,可用源代码文件提供与原型相应的函数定义来实现。
一种数据类型是以如下几点为特征的:数据如何构建、如何存储,以及有什么可能的振作。抽象数据类型(ADT)以抽象方式指定构成某种类型特征的属性和操作。
列表、队列和二叉树是ADT的实例,在计算机程序设计中常常用到。通常用动态内存分配和链接结构来实现它们,但有时数组是实现它们的更好选择。
当使用一种特定的类型(如队列或树)进行编程的时候,要按照该类型的接口来写程序。那样的话,修改或改进实现时就无须更改使用接口的程序。