队列的表示和实现
<队列是仅在表尾进行插入操作,在表头进行删除操作的线性表;
<表尾即an端,表头即a1端;(也称作队尾队头)
<它是一种先进先出的(FIFO)线性表
<例如Q(a1,a2,a3,......,an),a1为队头,an为队尾;
<插入元素称作入队,删除元素称作出队;
<队列的存储结构为顺序队或者链队;
队列的顺序表示
#define MAXQSIZE 1000 //最大的队列长度
Typedef struct{
QElemType *base; //初始化队列动态分配存储空间
int front; //头指针
int rear; //尾指针
}SqQueue;
队列在内存中的存储情况如图1:
图1 队列在内存中的存储情况
这里的front和rear虽然是int类型,但是其功能与指针类似。
下面简单介绍一下队列的入队:
front==0且rear==MAXQSIZE;再入队,真溢出;
front!=0且rear==MAXQSIZE;再入队,假溢出;
//解决假溢出的方法
/*<1>将队中元素依次向队头方向移动
(缺点:浪费时间,每移动一次,队中元素都要移动)
<2>将队空间想象成一个循环表
即分配给队列的m个存储单元可以循环使用,
当rear为MAXQSIZE时,若队列的开始端空着,又可以从头使用空着的空间
当front为MAXSIZE时,若队列的开始端空着,又可以从头使用空着的空间
<3>引入循环队列*/
循环队列的操作
/*
base[0]接在base[MAXQSIZE-1]之后,相当于一个圈
若rear+1==MAXQSIZE,令rear=0;
插入元素:Q.base[Q.rear]=x;
Q.rear=(Q.rear+1)%MAXQSIZE;
删除元素:x=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
*/
循环队列的存储情况如图2所示:
图2 循环队列在内存中的存储情况
Q.rear=(Q.rear+1)%MAXQSIZE;
Q.front=(Q.front+1)%MAXQSIZE;
因为内存下标是从0开始的,所以要加一
那么循环队列中队空、队满的标志是什么呢?
有没有发现在循环队列中队空、队满的标志都是front==rear;
那么我们该如何区分队空、队满呢?
其实很简单,只需要空出一个元素空间即可,如图3:
图三 空出一个元素空间后的存储情况
队空:front==rear;
队满:(rear+1)%MAXQSIZE==front;
循环队列的操作
队列的初始化(相当建立一个空的队列)
//队列的初始化
Status InitQueue(SqQueue Q){
Q.base=(QElemType *)malloc(sizeof(QElemType)*MAXQSIZE);
if(!Q.base){
return OVERLOW;
}
else{
Q.front=Q.rear=0; //front和rear相当于指针
return OK;
}
}
求队列长度
int QueueLength(SqQueue Q){
return ((Q.rear-Q.front+MAXQSIZE)%MAXQSIZE);
}
若rear-front<0,那么rear-front的绝对值表示该表中元素个数与MAXQSIZE的差值,即空格个数,
rear-base+MAXQSIZE可以表示该表中元素个数
若rear-front>0,那么rear-front可以直接表示该表中元素个数
循环队列在内存中的几种存储情况如图4所示:
图4 循环队列中的几种存储情况
循环队列入队
Status EnQueue(SqQueue Q,QElemType e){
if((Q.rear+1)%MAXQSIZE==0){
return ERROR; //队满
}
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXSIZE;
return OK;
}
队列入队只在队尾进行,相较于单链表的插入简单很多
但要注意,要判断是否队满
循环队列出队
Status DEQueue(SqQueue Q,QElemType e){
if(Q.front==Q.rear){
return ERROR; //队空
}
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return OK;
}
队列出队只在队头进行
应判断是否队空
取队头元素
SElemType GetHead(SqQueue Q){
if(Q.front!=Q.rear){ //队列不为空
return Q.base[Q.front];
}
}
可以直接访问头指针所指向的元素
//注意:返回的只是队头元素的值,队头指针不变
队列的链式表示和实现
链队列的类型定义
#define MAXQSIZE 100
typedef struct Qnode{
QElemType data;
struct Qnode * next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front; //队头指针
QueuePtr rear; //队尾指针
}LinkQueue;
这里的front相当于单链表里的头结点
链队列的初始化
Status InitQueue(LinkQueue Q){
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
Q.front->next=Q.rear->next=NULL;
return OK;
}
相当于建立一个空队
链队列的销毁
Status DestoryQueue(LinkQueue Q){
while(Q.front){
p=Q.front->next;
free(Q.front);
Q.front=p;
}
return OK;
}
销毁与单链表类似,表中所有东西都不存在,包括头结点
链队列的入队
Status EnQueue(LinkQueue Q,QElemType e){
p=(QueuePtr)malloc(sizeof(QNode));
if(!p){
return OVERLOW;
}
p->data=e;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return OK;
}
队列只能在队尾入队,相比于单链表,链队多了一个尾指针,所以操作起来很简单
链队列出队
Status DeQueue(LinkQueue Q,QElemType e){
if(Q.front==Q.rear){
return ERROR;
} //队空
QNode * p;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p){ //如果队中只有一个元素,删除后需要修改指针
Q.rear=Q.front;
}
free (p);
return OK;
}
}
队列的出队只能在队头进行,相当于删除用第一个数据元素所在的结点
其详细过程如图5所示:
图5 链队出队过程详解
取队头元素
Status GetHead(LinkQueue Q,QElemType e){
if(Q.front==Q.rear){
return ERROR;
}
e=Q.front->next->data;
return OK;
}
取队头元素可以直接访问头指针