目录
0.引言
队列:顾名思义,具有与日常生活中排队的模式的特点,新排队的人在整个队伍的尾巴加入,然后排在队伍前面的人先买票或者进入目的地(在没有插队的理想情况下)。那么我们就要以这种形式去实现队列的这样一种数据结构。
1.定义
队列是具有两个特殊属性的链表(队列与链表紧密联系)。第一,新项只能添加到链表的末尾。第二,只能从链表的开头移除项。 队列采取的是“先进先出”(first in first out)的模式。
2.队列结构模型
第一种实现方式:既然按照先进先出原则,第一位离开时,所有后面的人全部向前挪动一个单位。
我们在做可变数组的时候其实也遇到了这个问题:如果数据很大时,复制所花费的时间是很多的,这样的做法很不经济。所以我们将用第二种方法。
第二种方法:直接改变头和尾
但是我们发现这种情况下,原来结构有八个储存空间,但是改变头之后只剩7个了。内存空间减少了,为了处理这个问题我们就可以把整个储存空间变成环形,便可以充分利用了。
3.代码细节实现
3.1节点结构和队列结构
//节点结构 typedef struct node { int item; struct node *next; } Node; //队列结构 typedef struct queue { Node *front; Node *rear; int items; }Queue;
首先定义出节点的结构用于储存数据和指向下一个节点(和链表中的节点完全一致),然后定义出的队列结构:里面包含了(首端(front),尾端(rear),当前队列的节点个数)
3.2初始化队列
//初始化队列 void InitializeQueue(Queue *pq) { pq->front=pq->rear=NULL; pq->items=0; }
定义一个队列,传进此函数进行初始化:另front 和 rear都为NULL,并且节点个数为0
3.3检查队列是否已满/为空
//检查队列是否已满 bool QueueIsFull(const Queue *pq) { return pq->items==MAXQUEUE; } //检查队列是否为空 bool QueueIsEmpty(const Queue *pq) { return pq->items==0; }
这里MAXQUEUE是你预想定义的最大环形队列节点个数(使用宏定义即可实现)bool函数返回的只有1或者0,用于判断。对于QueueIsFull函数,如果达到最大项数,则返回1(true)。对于QueueIsEmpty函数,如果项数为0,则返回1(true)。
3.4队列的项数
//确定队列中的项数 int QueueItemCount(const Queue *pq) { return pq->items; }
这里采用了函数进行队列项数的封装(一种保护措施)
3.5队列中项的添加
//在队列末尾添加项 void EnQueue(int item,Queue *pq) { Node *pnew; pnew=(Node*)malloc(sizeof(Node)); pnew->item=item; pnew->next=NULL; if(QueueIsEmpty(pq)) { pq->front=pnew; }else{ pq->rear->next=pnew; } pq->rear=pnew; pq->items++; }
如果对链表的制作非常熟悉的话,这里其实是跟链表的添加是一样的,整体思路是创造一个新的节点,然后将这个节点给补到结构中去,这里为了满足队列的性质自然只能补在尾巴上了。这里存在一个细节:要先判断队列是不是一开始项数为0,如果一开始是空队列,就让头(pq->front=pnew)
3.6队列中项的删除
//从队列首端删除项 void DeQueue(int *pitem,Queue *pq) { Node *pt; *pitem=pq->front->item; pt=pq->front; pq->front=pq->front->next; free(pt); pq->items--; if(pq->items==0) { pq->rear=NULL; } }
这个函数含有两个参数:一个是在队列中首端的数的指针,一个是队列的指针。里面创造一个新节点pt,目的是让pq->front=pq->front->next之后再free原来的那个pq->front。同时也是让首端的数自动变为新首端的数。这里同样要注意清除之后,队列是否被清空了,如果项数为0,要另尾端为NULL;
3.7清空队列
//清空队列 void EmptyTheQueue(Queue *pq) { int dummy; while(!QueueIsEmpty(pq)) DeQueue(&dummy,pq); }
一直到队列清空,项数为0时,退出循环。
4.测试队列
输入a:添加项
输入d:删除项
输入p:退出
#include<stdio.h> #include<stdlib.h> #include<stdbool.h> #define MAXQUEUE 10; //节点结构 typedef struct node { int item; struct node *next; } Node; //队列结构 typedef struct queue { Node *front; Node *rear; int items; }Queue; //初始化队列 void InitializeQueue(Queue *pq) { pq->front=pq->rear=NULL; pq->items=0; } //检查队列是否已满 bool QueueIsFull(const Queue *pq) { return pq->items==MAXQUEUE; } //检查队列是否为空 bool QueueIsEmpty(const Queue *pq) { return pq->items==0; } //确定队列中的项数 int QueueItemCount(const Queue *pq) { return pq->items; } //在队列末尾添加项 void EnQueue(int item,Queue *pq) { Node *pnew; pnew=(Node*)malloc(sizeof(Node)); pnew->item=item; pnew->next=NULL; if(QueueIsEmpty(pq)) { pq->front=pnew; }else{ pq->rear->next=pnew; } pq->rear=pnew; pq->items++; } //从队列首端删除项 void DeQueue(int *pitem,Queue *pq) { Node *pt; *pitem=pq->front->item; pt=pq->front; pq->front=pq->front->next; free(pt); pq->items--; if(pq->items==0) { pq->rear=NULL; } } //清空队列 void EmptyTheQueue(Queue *pq) { int dummy; while(!QueueIsEmpty(pq)) DeQueue(&dummy,pq); } int main(void) { Queue line; int temp; char ch; InitializeQueue(&line); puts("Testing the Queue interface.Type a to add a value"); puts("type d to delete a value,and type q to quit."); while((ch=getchar())!='q') { if(ch!='a'&& ch!='d') continue; if(ch=='a') { printf("Integer to add: "); scanf("%d",&temp); if(!QueueIsFull(&line)) { printf("Putting %d into queue\n",temp); EnQueue(temp,&line); }else{ puts("Queue is full!"); } } else{ if (QueueIsEmpty(&line)) puts("Nothing to delete!"); else{ DeQueue(&temp,&line); printf("Removing %d from queue\n",temp); } } printf("%d items in queue\n",QueueItemCount(&line)); puts("Type a to add,d to delete,q to quit:"); } EmptyTheQueue(&line); puts("Bye!"); return 0; }
运行结果:
5.总结
队列其实就是一种特殊的链表,如果能够灵活使用链表,队列自然也就是a piece of cake。
但是队列也具有它独有的特殊性(first in first out)所以在添加删除的时候,需要考虑这种独有的数据结构。