与栈相反,队列(queue) 是一种先进先出 (first in first out,缩写为FIFO )的线性表。它只允许在表的一端进行插入,而在另一端删除元素。在队列中,允许插入的一端叫做队尾 (rear),允许删除的一端则称为 队头(front)。
1、链队列–队列的链式表示和实现
用链表表示的队列简称为链队列 ,一个链队列需要两个分别指示队头和队尾的指针(分别称为头指针和尾指针)才能唯一确定。我们给链队列添加一个头结点,并令头指针指向头结点。因此,空的链队列的判决条件为头指针和尾指针均指向头结点。如下图所示。
链队列的实现(部分)
(1)单链队列–队列的链式存储结构
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode, *QueuePtr;
typedef struct{
QueuePtr front; // 队头指针
QueuePtr rear; // 队尾指针
}LinkQueue;
(2)基本操作的算法描述(部分)
// 构造一个空队列(初始化)
Status InitQueue(LinkQueue &Q){
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if(!Q.front) exit(OVERFLOW); // 存储分配失败
Q.front->next = NULL;
return OK;
}
// 销毁队列Q
Status DestroyQueue(LinkQueue &Q){
while(Q.front){
Q.rear = Q.front -> next;
free(Q.front);
Q.front = Q.rear;
}
return OK;
}
// 插入元素e为Q的新的队尾元素
Status EnQueue(LinkQueue &Q, QElemType e){
p = (QueuePtr)malloc(sizeof(QNode));
if(!p) exit(OVERFLOW); // 存储分配失败
p->data = e;
p->next = NULL;
Q->rear->next = p;
Q.rear = p;
return OK;
}
// 若队列不空,则删除Q的队头元素,用e返回其值,并返回OK;
// 否则返回ERROR
Status DeQueue(LinkQueue &Q, QElemType &e){
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;
}
2、循环队列–队列的顺序表示和实现
将顺序队列臆造为一个环状的空间,如下图所示,称为循环队列。图(a)所示循环队列中,队列头元素是 J 3 J_3 J3 ,队列尾元素是 J 6 J_6 J6 ,之后 J 7 、 J 8 、 J 9 和 J 10 J_7、J_8、J_9和J_{10} J7、J8、J9和J10 相继插入,则队列空间均被占满,如图(b)所示,此时 Q . f r o n t = Q . r e a r Q.front=Q.rear Q.front=Q.rear ;反之,若 J 3 、 J 4 、 J 5 和 J 6 J_3、J_4、J_5和J_{6} J3、J4、J5和J6 相继从图(a)的队列中删除,使队列呈“空”的状态,如图(c)所示。此时也存在关系式 Q . f r o n t = Q . r e a r Q.front=Q.rear Q.front=Q.rear ,由此可见,只凭等式 Q . f r o n t = Q . r e a r Q.front=Q.rear Q.front=Q.rear 无法判别队列空间是“空”还是“满”。可有两种处理方法:(1)、 另设一个标志以区别队列是“空”;(2)、 少用一个元素空间,约定以“队列头指针在队列尾指针的下一位置(指环状的下一位置)上”作为队列呈“满”状态的标志。
从上述分析可见,在 C C C 语言中不能用动态分配的一维数组来实现循环队列。如果程序中设有循环队列,则必须为它设定一个最大队列长度;若无法预估所用队列的最大长度,则宜采用链队列。
循环队列类型的模块说明如下:
(1)循环队列–队列的顺序存储结构
#define MAXQSIZE 1000 // 最大队列长度
typedef struct{
QElemType *base; // 初始化的动态分配存储空间
int front; // 头指针,若队列不空,指向队列头元素
int rear; // 尾指针,若队列不空,指向队列尾元素的下一个位置
}SqQueue;
(2)循环队列的基本操作的算法描述
// 构造一个空队列Q
Status InitQueue(SqQueue &Q){
Q.base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType));
if(!Q.base) exit(OVERFLOW); // 存储分配失败
Q.front = Q.rear = 0;
return OK;
}
// 返回Q的元素个数,即队列的长度
int QueueLength(SqQueue Q){
return(Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}
// 插入元素e为Q的新的队尾元素
Status EnQueue(SqQueue &Q, QElemType e)
{
if((Q.rear + 1)%MAXQSIZE == Q.front) return(ERROR); // 队列满
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1)%MAXQSIZE;
return(OK);
}
// 若队列不空,则删除Q的队头元素,用e返回其值,并返回OK
// 否则返回ERROR
Status DeQueue(SqQueue &Q, QElemType &e)
{
if(Q.front == Q.rear) return(ERROR);
e = Q.base[Q.front];
Q.front = (Q.front + 1)%MAXQSIZE;
return(OK);
}