链队列存储结构
链队列的结点为结构体QNode;队列结构体LinkQueue,用来管理链队列,包含两个结点指针front和rear。
typedef int ElemType; //队列元素类型
typedef struct QNode { //结点
ElemType data;
QNode* next;
}QNode,*QueuePtr;
struct LinkQueue {
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
};
注意:
链队列包含一个头结点,是为了方便队列的操作,头结点没有有效数据,在队列初始化时就分配的头结点;
队头始终指向头结点,头结点指向首结点;队尾指向最后一个结点。当队列为空时,front和rear都指向头结点。
尾结点后为空,可以以NULL作为结束条件,遍历到后面包括尾结点的所有结点。
基本操作
1.初始化
分配一个头结点,将队列的front和rear指针指向头结点,头结点下一个结点为NULL。
void InitQueue(LinkQueue& Q) {
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front) exit(-1);
Q.front->next=NULL;
}
2.销毁
销毁后包括头结点在内的链表都被释放,只有队列结构体LinkQueue,将里面的数据清空。
队尾元素后为空,所以以为空作为结束的条件。
void DestroyQueue(LinkQueue& Q) {
while (Q.front) {
Q.rear = Q.front->next;
free(Q.front);
Q.front = Q.rear;
}
}
void DestroyQueue(LinkQueue& Q) {
QueuePtr p;
while (Q.front != Q.rear) {
p = Q.front->next;
free(Q.front);
Q.front= p;
}
free(Q.rear);
Q.front = Q.rear = NULL;
}
3.清空
队列清空时,还有头结点,队列的front和rear指针都指向头结点。
void ClearQueue(LinkQueue& Q) {
QueuePtr p = Q.front->next,q;//p指向首结点
Q.rear=Q.front;
Q.front->next = NULL;
while (p) {
q = p->next;
free(p);
p = q;
}
}
void ClearQueue(Queue &Q) {
QueuePtr p=Q.front->next,q; //p指向首结点
while (p) {
q = p->next;
free(p);
p = q;
}
Q.rear = Q.front;
Q.rear->next = NULL;
}
4.队空
队首后不为空时或者队首等于队尾时为空
bool QueueEmpty(LinkQueue Q) {
if (Q.front->next==NULL)
return true;
else
return false;
}
bool QueueEmpty(Queue Q) {
if (Q.rear == Q.front)
return true;
else
return false;
}
5.长度
由队首遍历到队尾并计数。
int QueueLength(LinkQueue Q) {
int n = 0;
QueuePtr p = Q.front;
while (p != Q.rear) {
p=p->next;
++n;
}
return n;
}
6.队头
队列不为空时,返回队头元素。队头为front指针的下一个位置。
bool GetHead(LinkQueue Q,ElemType &e) {
if (Q.front==Q.rear) //队空
return false;
e = Q.front->next->data;
return true;
}
7.入队
在队尾Q.rear后插入元素,注意最后要改变尾指针指向。
void EnQueue(LinkQueue& Q, ElemType e) {
QueuePtr p = (QueuePtr)malloc(sizeof(QNode));
if (!p) exit(-1);// 存储分配失败
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear=p; //改变尾指针指向
}
8.出队
队列不为空时,释放头结点后的节点;注意,如果只有一个结点时,出队后队列为空,需要将队尾指针指向头结点。
bool DeQueue(LinkQueue& Q, ElemType& e) {
if (Q.front == Q.rear) return false; //队空
QueuePtr p = Q.front->next; //p指向首结点
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)//队列只有一个元素要改变尾指针指向
Q.rear = Q.front;
free(p);
return true;
}
9.遍历
从队首遍历到队尾。
void QueueTraverse(LinkQueue Q,void(*vi)(ElemType)) {
QueuePtr p = Q.front->next; //p指向首结点
while (p) {
vi(p->data);
p = p->next;
}
printf("\n");
}
测试函数
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
void visit(ElemType e)
{
printf("%d ", e);
}
int main() {
ElemType n;
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, visit);
if (GetHead(q, n))
printf("队头元素是:%d\n", n);
DeQueue(q, n);
printf("删除了队头元素%d\n", n);
if (GetHead(q, n))
printf("新的队头元素是:%d\n", n);
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);
}
测试结果