// c3-2.h 单链队列--队列的链式存储结构
typedef struct QNode // (见图3.12)
{
QElemType data;
QNode *next;
}*QueuePtr;
struct LinkQueue // (见图3.13)
{
QueuePtr front,rear; // 队头、队尾指针
};
和栈一样,队列也是操作受限的线性表,只允许在队尾插入元素,在队头删除元素。
对于链队列结构,为了便于插入元素,设立了队尾指针。这样,插入元素的操作与队列长
度无关。图314 是具有两个元素的链队列示例。
// bo3-2.cpp 链队列(存储结构由c3-2.h定义)的基本操作(9个)
void InitQueue(LinkQueue &Q)
{ // 构造一个空队列Q(见图3.15)
if(!(Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode))))
exit(OVERFLOW);
Q.front->next=NULL;
}
void DestroyQueue(LinkQueue &Q)
{ // 销毁队列Q(无论空否均可)(见图3.16)
while(Q.front)
{
Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;
}
}
void ClearQueue(LinkQueue &Q)
{ // 将Q清为空队列
QueuePtr p,q;
Q.rear=Q.front;
p=Q.front->next;
Q.front->next=NULL;
while(p)
{
q=p;
p=p->next;
free(q);
}
}
Status QueueEmpty(LinkQueue Q)
{ // 若Q为空队列,则返回TRUE;否则返回FALSE
if(Q.front->next==NULL)
return TRUE;
else
return FALSE;
}
int QueueLength(LinkQueue Q)
{ // 求队列的长度
int i=0;
QueuePtr p;
p=Q.front;
while(Q.rear!=p)
{
i++;
p=p->next;
}
return i;
}
Status GetHead(LinkQueue Q,QElemType &e)
{ // 若队列不空,则用e返回Q的队头元素,并返回OK;否则返回ERROR
QueuePtr p;
if(Q.front==Q.rear)
return ERROR;
p=Q.front->next;
e=p->data;
return OK;
}
void EnQueue(LinkQueue &Q,QElemType e)
{ // 插入元素e为Q的新的队尾元素(见图3.17)
QueuePtr p;
if(!(p=(QueuePtr)malloc(sizeof(QNode))))
// 存储分配失败
exit(OVERFLOW);
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
}
Status DeQueue(LinkQueue &Q,QElemType &e)
{ // 若队列不空,删除Q的队头元素,用e返回其值,
// 并返回OK;否则返回ERROR(见图3.18)
QueuePtr p;
if(Q.front==Q.rear)
return ERROR;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p)
Q.rear=Q.front;
free(p);
return OK;
}
void QueueTraverse(LinkQueue Q,void(*vi)(QElemType))
{ // 从队头到队尾依次对队列Q中每个元素调用函数vi()
QueuePtr p;
p=Q.front->next;
while(p)
{
vi(p->data);
p=p->next;
}
printf("\n");
}
// main3-2.cpp 检验bo3-2.cpp的主程序
#include"c1.h"
typedef int QElemType;
#include"c3-2.h"
#include"bo3-2.cpp"
void print(QElemType i)
{
printf("%d ",i);
}
void main()
{
int i;
QElemType d;
LinkQueue q;
InitQueue(q);
printf("成功地构造了一个空队列!\n");
printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q));
printf("队列的长度为%d\n",QueueLength(q));
EnQueue(q,-5);
EnQueue(q,5);
EnQueue(q,10);
printf("插入3个元素(-5,5,10)后,队列的长度为%d\n",QueueLength(q));
printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q));
printf("队列的元素依次为");
QueueTraverse(q,print);
i=GetHead(q,d);
if(i==OK)
printf("队头元素是:%d\n",d);
DeQueue(q,d);
printf("删除了队头元素%d\n",d);
i=GetHead(q,d);
if(i==OK)
printf("新的队头元素是:%d\n",d);
ClearQueue(q);
printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);
DestroyQueue(q);
printf("销毁队列后,q.front=%u q.rear=%u\n",q.front, q.rear);
}
代码运行将结果如下:
/*
成功地构造了一个空队列!
是否空队列?1(1:空0:否) 队列的长度为0
插入3个元素(-5,5,10)后,队列的长度为3
是否空队列?0(1:空0:否) 队列的元素依次为-5 5 10
队头元素是:-5
删除了队头元素-5
新的队头元素是:5
清空队列后,q.front=4794552 q.rear=4794552 q.front->next=0
销毁队列后,q.front=0 q.rear=0
*/
由c3-2.h 和c2-2.h 对比可见,单链队列和单链表的结构有相同之处。单链队列也是带有头结点的单链表,它的队头指针相当于单链表的头指针。因为队列操作是线性表操作
的子集,所以bo3-2.cpp 中的基本操作也可以用单链表的基本操作来代替。这样既可以充
分利用现有资源,减小编程工作量,又可以更清楚地看出队列和线性表的内在联系和共
性。bo3-6.cpp 是利用单链表的基本操作实现单链队列基本操作的程序。
// bo3-6.cpp 用单链表的基本操作实现链队列(存储结构由c3-2.h定义)的基本操作(9个)
typedef QElemType ElemType;
#define LinkList QueuePtr // 定义单链表的类型与相应的链队列的类型相同
#define LNode QNode
#include"bo2-2.cpp" // 单链表的基本操作
void InitQueue(LinkQueue &Q)
{ // 构造一个空队列Q
InitList(Q.front); // 调用单链表的基本操作
Q.rear=Q.front;
}
void DestroyQueue(LinkQueue &Q)
{ // 销毁队列Q(无论空否均可)
DestroyList(Q.front);
Q.rear=Q.front;
}
void ClearQueue(LinkQueue &Q)
{ // 将Q清为空队列
ClearList(Q.front);
Q.rear=Q.front;
}
Status QueueEmpty(LinkQueue Q)
{ // 若Q为空队列,则返回TRUE;否则返回FALSE
return ListEmpty(Q.front);
}
int QueueLength(LinkQueue Q)
{ // 求队列的长度
return ListLength(Q.front);
}
Status GetHead(LinkQueue Q,QElemType &e)
{ // 若队列不空,则用e返回Q的队头元素,并返回OK;否则返回ERROR
return GetElem(Q.front,1,e);
}
void EnQueue(LinkQueue &Q,QElemType e)
{ // 插入元素e为Q的新的队尾元素
QueuePtr p;
if(!(p=(QueuePtr)malloc(sizeof(QNode)))) // 存储分配失败
exit(OVERFLOW);
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
}
Status DeQueue(LinkQueue &Q,QElemType &e)
{ // 若队列不空,删除Q的队头元素,用e返回其值,并返回OK;否则返回ERROR
if(Q.front->next==Q.rear) // 队列仅有1个元素(删除的也是队尾元素)
Q.rear=Q.front; // 令队尾指针指向头结点
return ListDelete(Q.front,1,e);
}
void QueueTraverse(LinkQueue Q,void(*vi)(QElemType))
{ // 从队头到队尾依次对队列Q中每个元素调用函数vi()
ListTraverse(Q.front,vi);
}
用单链表的操作代替单链队列的操作又和代替链栈的操作情况不同。链栈和单链表的
结构完全相同,许多栈的基本操作仅是单链表基本操作改了个名。而单链队列和单链表的
结构并不完全相同,只能是在单链队列的基本操作中调用单链表的基本操作。
// main3-6.cpp 检验bo3-6.cpp的主程序
#include"c1.h"
typedef int QElemType;
#include"c3-2.h"
#include"bo3-6.cpp" // 仅此句与main3-2.cpp不同
void print(QElemType i)
{
printf("%d ",i);
}
void main()
{
int i;
QElemType d;
LinkQueue q;
InitQueue(q);
printf("成功地构造了一个空队列!\n");
printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q));
printf("队列的长度为%d\n",QueueLength(q));
EnQueue(q,-5);
EnQueue(q,5);
EnQueue(q,10);
printf("插入3个元素(-5,5,10)后,队列的长度为%d\n",QueueLength(q));
printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q));
printf("队列的元素依次为");
QueueTraverse(q,print);
i=GetHead(q,d);
if(i==OK)
printf("队头元素是:%d\n",d);
DeQueue(q,d);
printf("删除了队头元素%d\n",d);
i=GetHead(q,d);
if(i==OK)
printf("新的队头元素是:%d\n",d);
ClearQueue(q);
printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u\n",q.front,q.rear,q.front->next);
DestroyQueue(q);
printf("销毁队列后,q.front=%u q.rear=%u\n",q.front, q.rear);
}
代码运行结果:
/*
成功地构造了一个空队列!
是否空队列?1(1:空0:否) 队列的长度为0
插入3个元素(-5,5,10)后,队列的长度为3
是否空队列?0(1:空0:否) 队列的元素依次为-5 5 10
队头元素是:-5
删除了队头元素-5
新的队头元素是:5
清空队列后,q.front=5253304 q.rear=5253304 q.front->next=0
销毁队列后,q.front=0 q.rear=0
*/