您已经看到如何把这种方法应用于简单列表。现在,将其应用于一个更复杂的数据类型:队列。 17.4.1定义队列抽象数据类型队列 ADT
正如您所看到的,用抽象数据类型方法进行c语言编程包含下面三个步骤:
1. 以抽象、通用的方式描述一个类型,包括其操作。
2. 设计一个函数接口来表示这种新类型。
3. 编写具体代码以实现这个接口。
队列(queue)是具有两个特殊属性的列表。第一,新的项目只能被添加到列表结尾处,在这方面, 队列与简单列表类似。第二,项目只能从列表开始处被移除。可以将队列看成是一队买电影票的人。 您在队尾加入队列,在买完票后从队首离开。队列是一种“先进先出(First In, First Out, FIFO) ” 的数据形式,就像买电影票的队伍一样(如果没有人插队〉。让我们仍然建立一个非正式的抽象定义, 如表17.2所示:
定义接口
接口定义将包含在queue.h文件中。我们将使用C的typedef工具创建两个类型名:Item和Queue。相 应结构的确切实现应该是queue.h文件的-部分,但从概念上讲,结构的设计属于具体实现阶段。现在, 我们假设已经定义了这些类型,集中考虑函数原型。
首先考虑初始化。这将改变一个Queue类型的变量,所以函数应把一个Queue变量的地址作为 参数:
void initializeQueue (Queue * pq):
接下来,确定队列为空或满涉及到返回真或假值的函数。这里我们假设C99的stdboolh头文件可 用。如果该文件不可用,可以使用int类型或自定义一个bool类型。因为函数不改变队列,所以它可 以接受一个Queue参数。但另一方面,如果只传递Queue的地址,可能会更快一些并可以节省内存, 这取决于Queue类型对象的大小。这次我们尝试这种方法。另一个好处是,这样所有的函数都将把地 址作为参数,而不像List例子中的情况。为了表明函数不改变队列,您可以而且也应该使用const限 定词:
bool QueuelsFull (const Queue * pq):
bool QueuelsEmpty (const Queue * pq):
解释一下,指针pq指向一个不能通过pq改变的Queue数据对象。可以为返回队列中项目数的函数定 义一个类似的原型。
int QueueItemCount (const Queue * pq):
向队列的尾端添加项目需要表示项目和队列。这种情况下将改变队列,所以必须(而不是可选)使 用指针。函数可以是void类型,或者您可以使用返回值来指示添加项目操作是否成功。我们采用第二种方法:
bool EnQueue (Item item, Queue * pq):
最后,删除项目可以有多种做法。如果把项目定义为一个结构或基本类型之一,可以由函数返回项目。 函数参数可以是Queue或者指向Queue的指针。因此,一种可能的原型如下:
Item DeQueue (Queue q);
但是,下面的原型更为通用:
bool DeQueue (Item * pitem, Queue * pq):
把从队列中删除的项目存放在由pitem指针指向的位置,并且用返回值指示操作是否成功。
用于清空队列的函数所需的惟一参数是队列的地址,可以使用下面的原型:
void EmptyTheQueue (Queue * pq):
实现接口的数据表示:
第一步是决定队列使用哪种C数据形式。一种可能是数组。数组的优点是便于使用,并且向数组中已 有数据的末尾添加项目很简单。但从队列首端删除项目会导致问题。在买票队伍的模型中,从队列首端删 除一个项目包括复制数组首元素的值,然后将数组中剩下的每一项都向前移动一个元素。编程实现这个过 程很简单,但是这会浪费计算机的大量时间(请参见图17.6)。
另一种解决数组实现中删除问题的方法是保持剩下的元素不动,并改变队列首端的位置(请参见图17.7)。 这种方法的问题在于空出的元素变成盲区,所以队列中的可用空间将不断减少。
解决盲区问题的一种聪明方法是使队列成为环形(circular)。这意味着将数组的首尾相连。即数组首 元素直接跟在末元素后面,这样当到达数组末尾时,如果首元素空出,就可以开始把新添加的项目存放到这些空出的元素中。(请参见图17.8)可以设想在一 张条形纸上画出数组,然后将数组的首尾粘起来形成 一个环。当然,现在应做一些有趣的标记来确保队列 尾端没有超过首端。
另一种方法是使用链表。其优点是删除首项时 不需要移动其他所有项,只须重置首指针以指向新 的首元素。因为我们已经讨论过链表,所以将遵循 这个思路。为了测试我们的想法,我们将从一个整 数队列开始。
typedef int Item;
链表由节点组成,所以下一步定义节点:
typedef struct node {
Item item; struct node * next;
} Node;
对于队列来说,需要保存首尾项的地址,这可以 通过使用指针来实现。也可以使用一个计数器来保存 队列中的项目个数。因此,该结构需要有两个指针成 员和一个im类型的成员。
typedef struct queue {
Node * front: /*指向队列首的指针 V
Node * rear; /*指向队列尾的指针 */
int items; /*队列中项目的个数 */
} Queue:
注意Queue是含有3个成员的结构,所以前面使用指向队列的指针代替整个队列来作为函数参数的决 定节省了时间和空间。
下面考虑队列的大小。链表的大小由可用内存的数量限制,但是往往比这小得多的链表更符合实际情 况。比如,您可以使用队列模拟飞机等待在机场着陆。如果等待的飞机数太多,新到的飞机就应该改在其 他机场着陆。我们把队列最大长度设为10。程序清单17.6含有队列接口的定义和原型。它把Item类型的 确切定义留给用户。在使用该接口时,您可以为您的特定程序插入合适的定义。
/* queue.h --队列接口 */
#pragma c9x on //#pragma c9x on 开启c99
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include<stdbool.h>
/* INSERT ITEM TYPE HERE */
/* FOR EXAMPLE, */
/* use the following for use_q.c */
/* typedef int Item; */
/* OR typedef struct item {int gumption; int charisma;} Item; */
/* use the following for mall.c */
//在此处插入Item的类型定义
//例如typedef int Item;
//或者 typedef struct item {int gumption;int charisma;}
typedef struct item
{
long arrive; /* the time when a customer joins the queue */
int processtime; /* the number of consultation minutes desired */
} Item;
#define MAXQUEUE 10
typedef struct node
{
Item item;
struct node *next;
}Node;
typedef struct queue{
Node *front; /*指向队列首的指针*/
Node *rear; /*指向队列尾的指针*/
int items; /*队列中项目的个数*/
}Queue;
/*操作:初始化队列 */
/*操作前:pq指向一个队列 */
/*搡作后:该队列被初始化为空队列*/
void InitializeQueue(Queue *pq);
/*搡作:检査队列是否己满 */
/*操作前:pq指向一个先前已初始化过的队列*/
/*操作后:如果该队列己满,則返回True;否则返回False*/
bool QueueIsFull(const Queue *pq);
/*操作:检査队列是否为空*/
/*操作前:pq指向一个先前已初始化过的队列*/
/*操作后:如果该队列为空,則返回True:否则返回False*/
bool QueueIsEmpty(const Queue *pq);
/*搡作:确定队列中项目的个数*/
/*搡作前:pq指向一个先前己初始化过的队列*/
/*操作后:返回队列中项目的个数*/
int QueueItemCount(const Queue *pq);
/*操作:向队列尾端添加項目 */
/*搡作前:pq指向一个先前己初始化过的队列 */
/* item是要添加到认列尾端的项目*/
/*操作后:如果队列未满,item被添加到 */
/*队列尾部,函数返回True:否则,*/
/* 不改变队列,函数返回False*/
bool EnAddQueue(Item item,Queue *pq);
/*搡作:从队列首端删除项目*/
/*搡作前:pq指向一个先前己初始化过的队列 */
/*搡作后:如果队列非空,队列首端的项目*/
/* 被复制到*piteni,并被从队列中删除,*/
/* 函数返回Tnie:如果这个搡作*/
/* 使队列为空,把队列重置为空队列*/
/* 如果队列开始时为空,*/
/* 不改变队列,函数返回False*/
bool DeQueue(Item *pitem,Queue *pq);
/*操作:清空队列*/
/*操作前:pq指向一个先前己初始化过的队列*/
/*操作后:队列被清空*/
void EmptyTheQueue(Queue *pq);
#endif