// 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(" "); }
// 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("成功地构造了一个空队列! "); printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q)); printf("队列的长度为%d ",QueueLength(q)); EnQueue(q,-5); EnQueue(q,5); EnQueue(q,10); printf("插入3个元素(-5,5,10)后,队列的长度为%d ",QueueLength(q)); printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q)); printf("队列的元素依次为"); QueueTraverse(q,print); i=GetHead(q,d); if(i==OK) printf("队头元素是:%d ",d); DeQueue(q,d); printf("删除了队头元素%d ",d); i=GetHead(q,d); if(i==OK) printf("新的队头元素是:%d ",d); ClearQueue(q); printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u ",q.front,q.rear,q.front->next); DestroyQueue(q); printf("销毁队列后,q.front=%u q.rear=%u ",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("成功地构造了一个空队列! "); printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q)); printf("队列的长度为%d ",QueueLength(q)); EnQueue(q,-5); EnQueue(q,5); EnQueue(q,10); printf("插入3个元素(-5,5,10)后,队列的长度为%d ",QueueLength(q)); printf("是否空队列?%d(1:空0:否) ",QueueEmpty(q)); printf("队列的元素依次为"); QueueTraverse(q,print); i=GetHead(q,d); if(i==OK) printf("队头元素是:%d ",d); DeQueue(q,d); printf("删除了队头元素%d ",d); i=GetHead(q,d); if(i==OK) printf("新的队头元素是:%d ",d); ClearQueue(q); printf("清空队列后,q.front=%u q.rear=%u q.front->next=%u ",q.front,q.rear,q.front->next); DestroyQueue(q); printf("销毁队列后,q.front=%u q.rear=%u ",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 */