队列(queue)是一种先进先出 (first in first out缩写FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除。类似食堂排队打饭。允许插入的一端叫做队尾(rear),允许删除的一端叫队头(front)
如图3.8所示:
双端队列是限定插入和删除操作在表的两端进行的线性表。一端叫端点1,一端叫端点2
如下图所示:
用链表表示的队列简称链队列。
一个链队列显然需要两个分别指示队头和队尾指针,才能确定。
下图就很好说明了这个例子:
下面是单链队列的结构体
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct LinkQueue{
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
P·S:
在xxx.c文件里面要用typedef来简化机构体,但在.cpp文件里面就不用了。可以直接用
下面是构建一个空队列Q
Status InitQueue(LinkQueue &Q)
{
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front)
exit(OVERFLOW); //存储分配失败
Q.front->next = NULL;
return Ok;
}
Status DesrtoyQueue(LinkQueue &Q)
{
while (Q.front)
{
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
return Ok;
}
这个函数的思路就是用Q.rear保存Q.front->next,然后不停的释放Q.front的空间
下面是插入元素e为Q的新的队尾元素
Status EnQueue(LinkQueue &Q, QElemType e)
{
QNode *p = (QueuePtr)malloc(sizeof(QNode));
if (!p)
exit(OVERFLOW);
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return Ok;
}
此函数思路:把新结点的next赋值为NULL,在把链队列的尾部的next从以前的next赋值为新结点的地址。最后把尾部后移成新节点的地址。
下面是删除Q的队头元素,用e返回其值,并返回OK
Status DeQueue(LinkQueue& Q, QElemType& e)
{
QNode *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;
}
这里Q.front==Q.rear就是图3.11(a)中的空队列
第二个if里面Q.rear==p是说明如果这里面没有结点了,就说明为空队列时,就Q.rear=Q.front。
循环队列-队列的顺序表示和实现
这个有个经典题目-圆桌问题
循环队列如图所示:
下面是循环队列头尾指针在不同情况下的不同:
下面是队列的顺序存储结构:
#define MAXQSIZE 100
typedef struct{
QElemType *base; //初始化的动态分配存储空间
int front; //头指针,若队列不空,指向队列头元素
int rear; //尾指针,若队列不空,指向下一个元素
}SqQueue;
Status InitQueue(SqQueue &Q)
{
Q.base = (QElemType *)malloc(MAXQSIZE*sizeof(QElemType));
if (!Q.base)
exit(OVERFLOW);
Q.front = Q.rear = 0;
return Ok;
}
int QueueLength(SqQueue Q)
{
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}
分析下:
下面是插入元素e为Q的新的队尾元素:有人会问为什么要(Q.rear-Q.front+MAXQSIZE)%MAXQIZE
举个例子,因为MAXQSIZE的大小为100,当进了110个数,又走了20个数的时候,这时候Q.rear指到10(如果下标从0开始),而Q.front指向20,那么Q.rear-Q.front=-10,这时加上MAXQSIZE后就是90再%MAXQSIZE取余后得90,所以这个(Q.rear-Q.front+MAXQSIZE)%MAXQIZE是计算环形长度的一个好方法。
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.rear+1)%MAXQSIZE比如队列大小是5,那么他们的标号为01234号,任何一个数对5取余都是这5个数,比如01234队列都满了,你要插入的话插在4的后边(5),可是4后边没有地方了,这时候对5取余,就得到了0,你就可以插到0的位置了。
第一个if:同理当发现要插入的那个位置正好是Q.front的位置,那么就证明队列满了。
Status DeQueue(SqQueue& Q, QElemType& e)
{
//e返回其值
if (Q.front == Q.rear)
return ERROR;
e = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXQSIZE;
return Ok;
}
同理上面的(Q.front+1)%MAXQSIZE与上面的(Q.rear+1)%MAXQSIZE一模一样。在此不再解释