队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,称之为链队列。
为了操作上的方便,我们将队头指针指向链队列的头结点,将队尾指针指向终端结点,如下图所示:
空队列时,头尾指针都指向头结点,如下图所示:
链队列的结构为:
typedef int QElemType; /* QElemType 类型根据实际情况而定 */
typedef struct QNode /* 结点结构 */
{
QElemType data;
struct QNode *next;
}QNode, *QueuePtr;
typedef struct /* 队列的链表结构 */
{
QueuePtr front,rear; //队头队尾指针
}LinkQueue;
链队列的入列操作:
入队操作时,其实就是在链表尾部插入结点,如图所示:
代码如下:
/* 插入元素e为Q的新的队尾元素 */
Stauts EnQueue (LinkQueue *Q, QElemType e)
{
QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
if(!s) /* 存储分配失败 */
exit(OVERFLOW);
s->data = e;
s->next = NULL;
Q->rear->next = s; // 把新结点s赋值给原队尾结点的后继
Q->rear = s; // 把当前的s设为队尾结点,rear指向s
return OK;
}
链栈的出队操作:
出队操作时,就是头结点的后继结点出队。若链表除头结点外只剩一个元素时,则需将rear指向头结点,如图所示:
/* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
Status DeQueue(LinkQueue *Q, QElemType *e)
{
QueuePtr p;
if(Q->front == Q->rear)
return ERROR;
p = Q->front->next; //将欲删除的队头结点暂存给p
*e = p->data; //将欲删除的队头结点的值赋给e
Q->front->next = p->next; //将原结点后继赋给头结点后继
if(Q->rear == p) //若队头是队尾,则删除后将rear指向头结点
Q->rear = Q->front;
free(p);
return OK;
}
循环队列与链队列比较,时间上,它们的基本操作都是O(1);对于空间上来说,循环队列必须有一个固定的长度,所以可能存在空间浪费的问题。因此,在可以预估队列长度时,建议用循环队列。如果无法预估,建议使用链队列。