本文使用c语言定义并实现list、queue、tree抽象数据类型,代码有详尽注释,可以通过代码熟悉原理并运用数据结构。

0.ADT基础知识

类型包括两类信息,属性和操作。在编程时,根据编程问题匹配合适的数据类型。

定义一个新的数据类型,有三个步骤:

  1. 提供类型属性和相关操作的抽象描述。描述不依赖于特定实现和特定编程语言。
  2. 开发一个实现ADT的编程接口。描述如何表示数据、描述实现ADT操作的函数,在c中,可以提供结构定义和操控该结构的函数原型。
  3. 编写代码实现接口。

1.list(链表)的实现

根据三个步骤,定义一个list:
1.建立抽象:

  • 类型名:简单链表。
  • 类型属性:可存储一系列项。
  • 类型操作:初始化链表为空、确定链表为空、确定链表已满、确定链表中的项数、在链表末尾添加项、遍历链表,处理链表中的项、清空链表。

2.建立接口:见list.h

建立接口后,可以使用这个接口编写程序,而不必知道具体实现细节,见film3.c

3.实现接口:见list.c

1.1 list.h

/* list.h -- header file for a simple list type */
#ifndef LIST_H_
#define LIST_H_
#include <stdbool.h>     /* C99 feature         *//* 特定程序的声明 */#define TSIZE      45    /* 存储电影名的数组大小  */
struct film
{char title[TSIZE];int rating;
};/* 一般类型定义 */typedef struct film Item;
/* 如果以后需要其他数据形式的链表,*/
/* 可以重新定义Item类型,不必更改其余的接口定义 */typedef struct node
{Item item;struct node * next;
} Node;/* 每一个链节叫做节点(Node) */typedef Node * List;
/* 指向链表开始处的指针,List作为该类型的指针名*//* 函数原型 */
/* 前提条件:调用该函数前应具备的条件 */
/* 后置条件:执行完该函数后的情况 *//* 操作:        初始化一个链表                          */
/* 前提条件:    plist 指向一个链表                     */
/* 后置条件:    链表初始化为空           */
void InitializeList(List * plist);
/* 该函数的参数是指向链表的指针,*/
/* 如果函数想要修改一个参数,那么该参数的类型应该是指向相应类型的指针 */
/* 调用时InitializeList(&movies) *//* operation:        确定链表是否为空                 */
/*                   plist 指向一个已初始化的链表        */
/* postconditions:   如果链表为空,函数返回true     */
/*                   否则返回false               */
bool ListIsEmpty(const List *plist);/* operation:        确定链表是否已满                  */
/*                   plist points to an initialized list        */
/* postconditions:   function returns True if list is full      */
/*                   and returns False otherwise                */
bool ListIsFull(const List *plist);/* operation:        确定链表中的项数          */
/*                   plist points to an initialized list        */
/* postconditions:   函数返回链表中的项数   */
unsigned int ListItemCount(const List *plist);/* operation:        在链表的末尾添加项                    */
/* preconditions:    item 是一个待添加至链表的项        */
/*                   plist 指向一个已初始化的链表        */
/* postconditions:   如果可以, 函数在链表末尾添加一个项     */
/*                   且返回True; 否则返回False    */
bool AddItem(Item item, List * plist);/* operation:        把函数作用于链表中的每一项      */
/*                   plist 指向一个已初始化的链表        */
/*                   pfun 指向一个函数,该函数接受一个    */
/*                   Item类型的参数,且无返回值      */
/* postcondition:    pfun指向的函数作用于链表中的每个项一次         */
void Traverse (const List *plist, void (* pfun)(Item item) );/* operation:        释放已分配的内存(如果有的话)            */
/*                   plist points to an initialized list        */
/* postconditions:   释放了为链表分配的所有内存 */
/*                   链表设置为空               */
void EmptyTheList(List * plist);#endif

1.2 list.c

/* list.c -- 支持链表操作的函数 */
#include <stdio.h>
#include <stdlib.h>
#include "list.h"/* 局部函数原型 */
/* 该函数是实现的一部分,不是接口的一部分,*/
/* 使用static存储类别说明符将其隐藏在list.c文件夹中 */
static void CopyToNode(Item item, Node * pnode);/* 接口函数   */
/* 把链表设置为空 */
void InitializeList(List * plist)
{* plist = NULL;
}/* 如果链表为空,返回true */
bool ListIsEmpty(const List * plist)
{if (*plist == NULL)return true;elsereturn false;
}/* 如果链表已满,返回True */
bool ListIsFull(const List * plist)
{Node * pt;bool full;/*  添加一个节点,分配一次内存 */pt = (Node *) malloc(sizeof(Node));if (pt == NULL)/*  系统把内存分配完了,此时表示链表满了 */full = true;elsefull = false;free(pt);return full;
}/* 返回节点的数量 */
unsigned int ListItemCount(const List * plist)
{unsigned int count = 0;Node * pnode = *plist;    /*设置链表的开始 */while (pnode != NULL){++count;pnode = pnode->next;  /* 设置下一个节点    */}return count;
}/* 创建存储项的节点 */
/* 并将其添加至由plist指向的链表末尾  */
bool AddItem(Item item, List * plist)
{Node * pnew;Node * scan = *plist;pnew = (Node *) malloc(sizeof(Node));if (pnew == NULL)return false;     /* 失败时退出函数  */CopyToNode(item, pnew);pnew->next = NULL;if (scan == NULL)          /* 空链表, 所以把 */*plist = pnew;         /* pnew 放在链表的开头 */else{while (scan->next != NULL)scan = scan->next;  /* 找到链表的末尾    */scan->next = pnew;      /* 把pnew添加到链表的末尾     */}return true;
}/* 访问每个节点并执行pfun指向的函数 */
void Traverse  (const List * plist, void (* pfun)(Item item) )
{Node * pnode = *plist;    /* 设置链表的开始   */while (pnode != NULL){(*pfun)(pnode->item); /* 把函数应用于链表中的项 */pnode = pnode->next;  /* 前进到下一项   */}
}/* 释放由malloc()分配的内存 */
/* 设置链表指针为 NULL          */
void EmptyTheList(List * plist)
{Node * psave;while (*plist != NULL){psave = (*plist)->next; /* 保存下一个节点的地址 */free(*plist);           /* 释放当前节点         */*plist = psave;         /* 前进至下一个节点      */}
}/* 局部函数定义  */
/* 把一个项拷贝到节点中 */
static void CopyToNode(Item item, Node * pnode)
{pnode->item = item;  /* 拷贝结构 */
}

1.3 film3.c

使用接口编写程序,但是不必知道具体的实现细节。

/* films3.c -- using an ADT-style linked list */
/* compile with list.c                        */
#include <stdio.h>
#include <stdlib.h>    /* prototype for exit() */
#include "list.h"      /* defines List, Item   */
void showmovies(Item item);
char * s_gets(char * st, int n);
int main(void)
{List movies;Item temp;/* 初始化       */InitializeList(&movies);if (ListIsFull(&movies)){fprintf(stderr,"No memory available! Bye!\n");exit(1);}/* 获取用户输入并存储 */puts("Enter first movie title:");while (s_gets(temp.title, TSIZE) != NULL && temp.title[0] != '\0'){puts("Enter your rating <0-10>:");scanf("%d", &temp.rating);while(getchar() != '\n')continue;if (AddItem(temp, &movies)==false){fprintf(stderr,"Problem allocating memory\n");break;}if (ListIsFull(&movies)){puts("The list is now full.");break;}puts("Enter next movie title (empty line to stop):");}/* 显示         */if (ListIsEmpty(&movies))printf("No data entered. ");else{printf ("Here is the movie list:\n");Traverse(&movies, showmovies);}printf("You entered %d movies.\n", ListItemCount(&movies));/* 清理        */EmptyTheList(&movies);printf("Bye!\n");return 0;
}void showmovies(Item item)
{printf("Movie: %s  Rating: %d\n", item.title,item.rating);
}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');   // 查找换行符if (find)                  // 如果地址不是NULL,*find = '\0';          // 在此处放置一个空字符elsewhile (getchar() != '\n')continue;          // 处理输入行的剩余内容}return ret_val;
}

2.queue(队列)的实现

队列是具有两个特殊属性的链表,1.新项只能添加到链表的末尾,2.只能从链表的开头移除项。 先进先出的数据形式。

根据三个步骤,定义一个list:
1.建立抽象:

  • 类型名:队列。
  • 类型属性:可存储一系列项。
  • 类型操作:初始化队列为空、确定队列为空、确定队列已满、确定队列中的项数、在队列末尾添加项、在队列开头删除或恢复项、清空队列。

2.建立接口:见queue.h

3.实现接口:见queue.c

4.测试接口:见use_q.c

5.使用队列:见mall.c

2.1 queue.h

/* queue.h -- interface for a queue */
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include <stdbool.h>// 在这里插入Item类型的定义
// FOR EXAMPLE,
//typedef int Item;  // for use_q.c
// OR typedef struct item {int gumption; int charisma;} Item;
// OR  (for mall.c)
/**/typedef struct item{long arrive;      // 一位顾客加入队列的时间int processtime;  // 该顾客咨询时花费的时间} Item;
/**/#define MAXQUEUE 10typedef struct node
{Item item;struct node * next;
} Node;typedef struct queue
{Node * front;  /* 指向队列首项的指针  */Node * rear;   /* 指向队列尾项的指针   */int items;     /* 队列中的项数   */
} Queue;/* operation:        初始化队列                       */
/* 前提条件:     pq 指向一个队列                       */
/* 后置条件:     队列被初始化为空        */
void InitializeQueue(Queue * pq);/* operation:        检查队列是否已满                    */
/* precondition:     pq 指向之前被初始化的队列           */
/* postcondition:    如果队列已满则返回true,否则返回false   */
bool QueueIsFull(const Queue * pq);/* operation:        检查队列是否为空                    */
/* precondition:     pq 指向之前被初始化的队列  */
/* postcondition:    如果队列为空则返回true,否则返回false */
bool QueueIsEmpty(const Queue *pq);/* operation:        确定队列中的项数         */
/* precondition:     pq points to previously initialized queue  */
/* postcondition:    返回队列中的项数          */
int QueueItemCount(const Queue * pq);/* operation:        在队列末尾添加项                  */
/* precondition:     pq 指向之前被初始化的队列  */
/*                   item是要被添加在队列末尾的项      */
/* postcondition:    如果队列不为空,item将被添加在队列的末尾,   */
/*                   函数返回true;         */
bool EnQueue(Item item, Queue * pq);/* operation:        从队列的开头删除项            */
/* precondition:     pq 指向之前被初始化的队列  */
/* postcondition:    如果队列不为空,     */
/*                   队列首端的item将被拷贝到*pitem中, */
/*                   并被删除,且函数返回True   */
/*                   如果该操作使队列为空,  */
/*                   则重置队列为空   */
/*                   如果队列在操作前为空,     */
/*                   该函数返回false                     */
bool DeQueue(Item *pitem, Queue * pq);/* operation:        清空队列                            */
/* precondition:     pq指向之前被初始化的队列  */
/* postconditions:   队列被清空                         */
void EmptyTheQueue(Queue * pq);#endif

2.2 queue.c

/* queue.c -- the Queue type implementation*/
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"/* local functions */
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;           /* 项位于队列首端     */elsepq->rear->next = pnew;      /* 链接到队列尾端   */pq->rear = pnew;                /* 记录队列尾端位置 */pq->items++;                    /* 队列项数加1 */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;//首指针和尾指针都置为NULLreturn true;
}/* 清空队列                */
void EmptyTheQueue(Queue * pq)
{Item dummy;while (!QueueIsEmpty(pq))DeQueue(&dummy, pq);
}/* Local functions                 */static void CopyToNode(Item item, Node * pn)
{pn->item = item;
}static void CopyToItem(Node * pn, Item * pi)
{*pi = pn->item;
}

2.3 use_q.c

/* use_q.c -- driver testing the Queue interface */
/* compile with queue.c                          */
#include <stdio.h>
#include "queue.h"  /* defines Queue, Item       */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')   /* ignore other input */continue;if ( ch == 'a'){printf("Integer to add: ");scanf("%d", &temp);if (!QueueIsFull(&line)){printf("Putting %d into queue\n", temp);EnQueue(temp,&line);}elseputs("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;
}

2.4 mall.c

有一个提供快乐的摊位,顾客可以购买1分钟、2分钟或3分钟的快乐。为确保交通顺畅,每个摊位前排队等待的顾客最多为10人。假设顾客随机出现,并且他们花费的时间也是随机选择的(1\2\3分钟),那么该摊位平均每小时接待多少名顾客?每位顾客平均花多长时间?排队等待的顾客平均有几人?

// mall.c -- use the Queue interface
// compile with queue.c
#include <stdio.h>
#include <stdlib.h>    // for rand() and srand()
#include <time.h>      // for time()
#include "queue.h"     // change Item typedef
#define MIN_PER_HR 60.0bool newcustomer(double x);   // 是否有新顾客来?
Item customertime(long when); // 设置顾客参数int main(void)
{Queue line;Item temp;                // 新的顾客数据int hours;                // 模拟的小时数int perhour;              // 每小时平均多少位顾客long cycle, cyclelimit;   // 循环计数器、 计数器的上限long turnaways = 0;       // 因队列已满被拒的顾客数量long customers = 0;       // 加入队列的顾客数量long served = 0;          // 在模拟期间到访过摊位的顾客数量long sum_line = 0;        // 累计的队列总长int wait_time = 0;        // 从当前到摊位空闲所需时间double min_per_cust;      // 顾客到来的平均时间long line_wait = 0;       // 队列累计的等待时间 InitializeQueue(&line);srand((unsigned int) time(0)); // rand()随机初始化puts("Case Study: Sigmund Lander's Advice Booth");puts("Enter the number of simulation hours:");scanf("%d", &hours);cyclelimit = MIN_PER_HR * hours;puts("Enter the average number of customers per hour:");scanf("%d", &perhour);min_per_cust = MIN_PER_HR / perhour;for (cycle = 0; cycle < cyclelimit; cycle++)//每次迭代对应一分钟的行为{if (newcustomer(min_per_cust))//如果1分钟内有顾客到来{if (QueueIsFull(&line))turnaways++;//被拒的顾客数量else{customers++;temp = customertime(cycle);/*设置temp结构中的arrive和processtime成员*/EnQueue(temp, &line);}}if (wait_time <= 0 && !QueueIsEmpty(&line)){//如果队列不为空且前面的顾客没有咨询,则删除队列首端的项DeQueue (&temp, &line);wait_time = temp.processtime;//该顾客咨询时花费的时间line_wait += cycle - temp.arrive;//到目前为止,队列中所有顾客的等待总时间served++;//咨询过的顾客数量}if (wait_time > 0)//完成当前顾客的需求所需的时间大于0wait_time--;sum_line += QueueItemCount(&line);//目前为止统计的队列长度}if (customers > 0){printf("customers accepted: %ld\n", customers);printf("  customers served: %ld\n", served);printf("       turnaways: %ld\n", turnaways);printf("average queue size: %.2f\n",(double) sum_line / cyclelimit);printf(" average wait time: %.2f minutes\n",(double) line_wait / served);}elseputs("No customers!");EmptyTheQueue(&line);puts("Bye!");return 0;
}// x = 顾客到来的平均时间(单位:分钟)
// 如果1分钟内有顾客到来,则返回true
bool newcustomer(double x)
{if (rand() * x / RAND_MAX < 1)return true;elsereturn false;
}// when是顾客到来的时间
// 该函数返回一个Item结构,该顾客到达的时间设置成when
// 咨询时间设置为1 - 3的随机值
Item customertime(long when)
{Item cust;cust.processtime = rand() % 3 + 1;cust.arrive = when;return cust;
}

3.tree(二叉查找树)的实现

访问元素有两种,随机访问:使用数组下标直接访问该数组中的任意元素;顺序访问:对于链表而言,必须从链表首节点开始,逐个节点移动到要访问的节点。
用数组可以实现二分查找,因为可以使用数组下标确定数组中任意部分的中点。但是链表只支持顺序访问,不提供跳至中间节点的方法,所以链表中不能使用二分查找。

有一种数据形式的需求,既支持频繁插入和删除项,又支持频繁查找。此时需要二叉查找树。

根据三个步骤,定义一个二叉树:

该定义假设二叉树不包含相同的项。

1.建立抽象:

类型名:二叉查找树。

类型属性:

  • 二叉树要么是空节点的集合(空树),要么是有一个根节点的节点集合。
  • 每个节点都有两个子树,叫做左子树和右子树
  • 每个子树本身也是一个二叉树,也可能是空树
  • 二叉查找树是一个有序的二叉树,每个节点包含一个项。
  • 左子树的所有项都在根节点项的前面,右子树所有项都在根节点项的后面

类型操作:初始化树为空、确定树是否为空、确定树是否已满、确定树中的项数、在树中添加一个项、在树中删除一个项、在树中查找一个项、在树种访问一个项、清空树。

2.建立接口:见tree.h

3.实现接口:见tree.c

4.使用二叉树:见petclub.c

3.1 tree.h

/* tree.h -- binary search tree 二叉查找树                */
/*           树中不允许有重复的项 */
#ifndef _TREE_H_
#define _TREE_H_
#include <stdbool.h>/* 根据情况重新定义Item */
#define SLEN 20
typedef struct item
{char petname[SLEN];char petkind[SLEN];
} Item;#define MAXITEMS 10typedef struct trnode
{Item item;struct trnode * left;  /* 指向左分支的指针  */struct trnode * right; /* 指向右分支的指针  */
} Trnode;typedef struct tree
{Trnode * root;         /* 指向根节点的指针  */int size;              /* 树的项数  */
} Tree;/* 函数原型 *//* 操作:       把树初始化为空          */
/* 前提条件:   ptree 指向一个树        */
/* 后置条件:   树被初始化为空          */
void InitializeTree(Tree * ptree);/* operation:      确定树是否为空          */
/* preconditions:  ptree points to a tree              */
/* postconditions: 如果树为空,函数返回true    */
/*                 否则返回false      */
bool TreeIsEmpty(const Tree * ptree);/* operation:      确定树是否已满           */
/* preconditions:  ptree points to a tree              */
/* postconditions: function returns true if tree is    */
/*                 full and returns false otherwise    */
bool TreeIsFull(const Tree * ptree);/* operation:      确定树的项数                         */
/* preconditions:  ptree points to a tree              */
/* postconditions: 返回树的项数                         */
int TreeItemCount(const Tree * ptree);/* operation:      在树中添加一个项               */
/* preconditions:  pi 是待添加项的地址   */
/*                 ptree 指向一个已初始化的树 */
/* postconditions: 如果可以添加,该函数将在树中添加一个项  */
/*                 并返回true,否则返回false          */
bool AddItem(const Item * pi, Tree * ptree);/* operation:      在树中查找一个项                   */
/* preconditions:  pi指向一个项                */
/*                 ptree 指向一个已初始化的树 */
/* postconditions: 如果在树中找到指定项,该函数返回true */
/*                 否则返回false    */
bool InTree(const Item * pi, const Tree * ptree);/* operation:      从树中删除一个项          */
/* preconditions:  pi 是删除项的地址 */
/*                 ptree 指向一个已初始化的树 */
/* postconditions: 如果在树中成功删除一个项,该函数返回true */
/*                 否则返回false*/
bool DeleteItem(const Item * pi, Tree * ptree);/* operation:      把函数应用于树中的每一项               */
/* preconditions:  ptree 指向一个树              */
/*                 pfun 指向一个函数            */
/*                 该函数接受一个Item类型的参数,并无返回值       */
/* postcondition:  pfun指向的这个函数为树中的每一项执行一次 */
void Traverse (const Tree * ptree, void (* pfun)(Item item));/* operation:      删除树中的所有内容       */
/* preconditions:  ptree指向一个已初始化的树 */
/* postconditions: 树为空                       */
void DeleteAll(Tree * ptree);#endif

3.2 tree.c

/* tree.c -- tree support functions */
#include <string.h>
#include <stdio.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 * ptr);/* 函数定义 */
void InitializeTree(Tree * ptree)
{ptree->root = NULL;ptree->size = 0;
}bool TreeIsEmpty(const Tree * ptree)
{if (ptree->root == NULL)return true;elsereturn false;
}bool TreeIsFull(const Tree * ptree)
{if (ptree->size == MAXITEMS)return true;elsereturn 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)      /* 情况 1: 树为空  */ptree->root = new_node;   /* 新节点为树的根节点  */else                          /* case 2: 树不为空      */AddNode(new_node,ptree->root); /* 在树中添加新节点  */return true;                  /* 成功返回      */
}/*在树中查找一个项*/
bool InTree(const Item * pi, const Tree * ptree)
{/*SeekItem函数返回一个结构,该函数可以与结构成员运算符一起使用*/return (SeekItem(pi, ptree).child == NULL) ? false : true;
}bool DeleteItem(const Item * pi, Tree * ptree)
{Pair look;/*SeekItem返回一个结构,*//*内含两个指针,一个指针look.parent指向父节点,*//*一个指针look.child指向包含特定项的节点*/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);/*第三种情况,待删除节点是父节点的右子节点*/elseDeleteNode(&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);/*释放内存*//*处理Tree类型的结构,重置Tree类型结构成员,表明该树为空*/ptree->root = NULL;ptree->size = 0;
}/* local functions */
/*可以有不同的处理顺序*/
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;/*存储root->right,使其在释放根节点后仍然可用*/DeleteAllNodes(root->left);free(root);DeleteAllNodes(pright);}
}/*确定新节点的位置,添加新节点*/
static void AddNode (Trnode * new_node, Trnode * root)
{/*如果新项应放在左子树中,ToLeft()函数返回true*/if (ToLeft(&new_node->item, &root->item)){if (root->left == NULL)      /* 空子树       */root->left = new_node;   /* 把节点添加到此处    */else/*把新项和左子节点中的项作比较,*//*以确定新项该放在该子节点的左子树还是右子树*/AddNode(new_node, root->left);/* 否则处理该子树*/}/*如果新项应放在右子树中,ToRight()函数返回true*/else if (ToRight(&new_node->item, &root->item)){if (root->right == NULL)root->right = new_node;elseAddNode(new_node, root->right);}else                         /* 不允许有重复项 */{fprintf(stderr,"location error in AddNode()\n");exit(1);}
}/*ToLeft和ToRight函数依赖于Item类型的性质。*/
/*成员名按字母排序,如果名字相同,按其种类排序*/
/*如果种类相同,属于重复项*/
static bool ToLeft(const Item * i1, const Item * i2)
{int comp1;/*第一个字符串在第二个字符串前面,strcmp返回负数*//*两个字符串相同,strcmp返回0*/if ((comp1 = strcmp(i1->petname, i2->petname)) < 0)return true;else if (comp1 == 0 &&strcmp(i1->petkind, i2->petkind) < 0 )return true;elsereturn 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;elsereturn 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;
}/*查找特定项*/
/*返回的结构包含两个指针,*/
/*一个指针指向包含项的节点,如果未找到指定项则为NULL*/
/*一个指针指向父节点(如果该节点为根节点,没有父节点,则为NULL)*/
static Pair SeekItem(const Item * pi, const Tree * ptree)
{Pair look;look.parent = NULL;/*开始时,设置look.child指向树的根节点*/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;//如果没有找到匹配的项,look.child将被设置为NULL}else if (ToRight(pi, &(look.child->item))){look.parent = look.child;look.child = look.child->right;}else       /* 如果前两种情况都不满足,则必定是相等的情况    */break; /* look.child 目标项的节点 */}return look;                       /* 成功返回   */
}static void DeleteNode(Trnode **ptr)
/* ptr是指向待删除节点指针的地址  */
/*那么*ptr就是把ptr地址里面的值取出来,就是指向待删除节点的指针*/
/*要修改的指针本身是Trnode*类型,指向Trnode的指针*/
/*该函数的参数是该指针的地址,所以参数的类型是Trnode** */
{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    /* 被删除的节点有两个子节点 */{/* 找到重新连接右子树的位置 *//*沿左子树的右支查找到第1个空位*/for (temp = (*ptr)->left; temp->right != NULL;temp = temp->right)continue;/*把待删除节点的右子树与该空位连接*/temp->right = (*ptr)->right;/*处理完之后重置待删除节点的地址*//*重置之前先将*ptr存储在temp中,释放待删除节点的内存*/temp = *ptr;*ptr =(*ptr)->left;free(temp); }
}

3.3 petclub.c

/* petclub.c -- use a binary search tree */
#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')  /* 处理输入行的剩余内容 */continue;ch = tolower(ch);if (strchr("alrfndq",ch) == NULL)puts("Please enter an a, l, f, n, d, or q:");elsebreak;}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:");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!");elseTraverse(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");elseprintf("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");elseprintf("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');   // 查找换行符if (find)                  // 如果地址不是 NULL,*find = '\0';          // 在此处放置一个空字符elsewhile (getchar() != '\n')continue;          // 处理输入行的剩余内容}return ret_val;
}

c语言 数据结构 list、queue、tree抽象数据类型的定义与实现 详尽代码和注释相关推荐

  1. 数据结构与算法——6. 抽象数据类型:无序表与有序表及其链表实现

    文章目录 一.无序表(unordered list)抽象数据类型 1. 无序表的定义 2. 采用链表实现无序表 (1)链表 (2)链表节点 (3)python实现链表节点 (4)python实现链表 ...

  2. 抽象数据类型的三元组C语言,试仿照三元组的抽象数据类型分别写出数据类型复数和有理数的定义?...

    满意答案 hweiwei420 2015.10.10 采纳率:50%    等级:13 已帮助:14203人 a. 抽象数据类型复数complex: ADT complex{ 数据对象:D={e1,e ...

  3. 数据结构:设计实现抽象数据类型“有理数”内容

    题目:设计实现抽象数据类型"有理数" 内容:设计并上机实现抽象数据类型"有理数",有理数的基本操作包括:两个有理数的加.减.乘.除等(包括有理数的创建和输出). ...

  4. c语言写报告抽象数据类型,数据结构(C语言版)第一章 抽象数据类型的表示与实现...

    //文件名:Triplet.c //因为C语言没有引用,所以用指针代替引用 //函数的头文件 #include #include //函数结果状态码 #define TRUE 1 #define FA ...

  5. 数据结构15: 有序表抽象数据类型

    目录 一.什么是有序表orderedlist ? 二.orderedlist定义的操作 1.search方法 2.add方法 3.所有代码 四.链表实现的算法分析 一.什么是有序表orderedlis ...

  6. [数据结构-严蔚敏版]P71串的抽象数据类型的定义

    代码如下: #include <iostream> #include <string> using namespace std;typedef struct {char *ch ...

  7. (笔记)数据结构--抽象数据类型的定义

    例子

  8. c语言抽象数据类型的定义,C++问题,定义“有理数”的抽象数据类型

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include using namespace std; class Complex{ private: int numerator; int deno ...

  9. C语言实现《别碰白块》小游戏!全部代码+思路注释

    今天我们将用C语言实现一个小球跳跃躲避方块的游戏. 就像闯关游戏一样,小球闯关类游戏是休闲游戏的一种,玩家在游戏中需要控制各种不同的小球展开不同的挑战,尽情的闯过重重的关卡,努力的操作小球躲避障碍物以 ...

最新文章

  1. 海思AI芯片(Hi3519A/3559A)方案学习(十四)JPEG图片转换成bgr文件
  2. GE 携手哈电国际开启国际合作新篇章
  3. upload_labs_pass17_二次渲染
  4. matlab cat函数_如何用Matlab编写贪吃蛇游戏?(持续更新)
  5. 求一个整数的阶乘结果中后缀0的个数
  6. twisted系列教程十一 — 一个twisted 的服务端
  7. [置顶]别羡慕别人的舒服,静下心来坚持奋斗!!!
  8. 【323天】每日项目总结系列061(2017.12.25)
  9. 读《scikiit-learn机器学习》黄永昌第五章
  10. gd库多点画图 php_用 PHP 实现身份证号码识别
  11. 电脑整蛊关机html代码,恶搞关机的脚本小程序 -电脑资料
  12. 模拟用户操作购物车添加商品修改数量
  13. vmware虚拟机删除光驱启动报错解决办法
  14. android alert
  15. MD5加密(MD5Util )
  16. 在线直播系统源码,多图加载成动画的形式如何实现
  17. 11.Unity ShaderGraph实例(LWRP+PBRMaster节点制作全息效果的精灵)
  18. Maven安装配置全教程
  19. css第二课:外部样式link和import的运用及行内样式的介绍
  20. 嵌入式开发使用python开发 +项目实践

热门文章

  1. PyQt5 Python主对话框创建关于对话框源代码
  2. 十三、PyQt5的QFileDialog文件打开、文件保存、文件夹选择对话框
  3. html5 java 图片上传_java实现图片上传至服务器并显示,如何做?希望要具体的代码实现...
  4. 利用JQuery插件CleverTabs实现多页签打开效果
  5. matlab平稳随机过程的功率谱密度,平稳随机过程的功率谱密度
  6. python csdn博客_GitHub - 1783955902/CSDNBlogBackup: Python实现CSDN博客的完整备份
  7. c语言sort函数排序二维数组,js 二维数组排序sort()函数
  8. python中ttk和tkinter_python tkinter中ttk组件如何使用?
  9. 中职学校计算机教学探讨,中职学校计算机专业教学探讨
  10. 钉钉项目任务怎么添加审批表单