C Primer Plus学习 六十一 队列 ADT(一)

来源:互联网 发布:福建广电网络缴费 编辑:程序博客网 时间:2024/05/21 11:48

队列 ADT

正如您所看到的,用抽象数据类型方法进行c语言编程包含下面三个步骤:

1. 以抽象、通用的方式描述一个类型,包括其操作。
2. 设计一个函数接口来表示这种新类型。
3. 编写具体代码以实现这个接口。
       您已经看到如何把这种方法应用于简单列表。现在,将其应用于一个更复杂的数据类型:队列。 17.4.1定义队列抽象数据类型

        队列(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


0 0
原创粉丝点击