队列(queue)是一种先进先出的线性表。它只允许在表的一端进行插入,而在另一端删除元素。允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)。
队列的顺序存储结构
//------队列的顺序存储结构------
#define MAXQSIZE 100//队列可能达到的最大长度
typedef struct
{
QElemType *base;//存储空间的基地址
int front;//头指针
int rear;//尾指针
}SqQueue;
在初始化创建空队列时,令front=rear=0,每当插入新的队列元素时,尾指针rear增1;每当删除队列头元素时,头指针front增1。因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置。
补充:由“队尾入队,队头出队”这种受限制的操作会造成“假溢出”。
在顺序队列中,由于数组空间不够而产生的溢出叫真溢出;顺序队列因多次入队列和出队列操作后出现的有存储空间但不能进行入队列操作的溢出称为假溢出;假溢出是由于队 rear的值和队头 front的值不能由所定义数组下界值自动转为数组上界值而产生的,解决的办法是把顺序队列所使用的存储空间构造成一个逻辑上首尾相连的循环队列,因此,顺序队列通常都采用顺序循环队列结构。
设顺序存储队列用一维数组q[m]表示,其中m为队列中元素个数,队列中元素在向量中的下标从0到m-1。设队头指针为front,队尾指针是rear,约定 front指向队头元素的前一位置 rear 指向队尾元素。当front等于-1时队空,rear等于m-1时为队满。由于队列的性质(“删除”在队头而“插入”在队尾),所以当队尾指针rear等于m-1 时,若 front 不等于-1,则队列中仍有空闲单元,所以队列并不是真满。这时若再有入队操作,会造成假“溢出”。
解决办法:将队列看成首尾相连,即循环队列(0…m-1)。在循环队列下,仍定义front=rear时为队空,而判断队满则用“牺牲一个单元”,即 rear+1=front(准确记是(rear+1)%m=front,,m 是队列容量)时为队满。
循环队列中对队空和队满的条件:
循环队列的初始化
【算法步骤】
为队列分配一个最大容量为MAXQSIZE的数组空间,base指向数组空间的首地址。
头指针和尾指针置为零,表示队列为空。
Status InitQueue(SqQueue &Q)
{//构造一个空队列Q
Q.base=new QElemType[MAXQSIZE];//队列分配一个最大容量为MAXQSIZE的数组空间
if(!Q.base) exit(OVERFLOW);//存储分配失败
Q.front=Q.rear=0;//头指针和尾指针置为零,表示队列为空
return OK;
}
求循环队列长度
int QueueLength(SqQueue Q)
{//返回Q的元素个数,即队列的长度
return(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}
循环队列入队
【算法步骤】
判断队列是否满,若满则返回ERROR。
将新元素插入队尾。
队尾指针加1。
Status EnQueue(SqQueue &Q,QElemType e)
{//插入元素为的新的对尾元素
if((Q.rear+1)%MAXQSIZE==Q.front)//尾指针在循环意义上加1后等于头指针,表明队满
return ERROR;
Q.base[Q.rear]=e;//新元素插入队尾
Q.rear=(Q.rear+1)%MAXQSIZE;//队尾指针加1
return OK;
}
循环队列的出队
【算法步骤】
判断队列是否空,若空则返回ERROR。
保存队头元素。
队头指针加1。
Status DeQueue(SqQueue &Q,QElemType &e)
{//删除Q的队头元素,用e返回其值
if(Q.front==Q.rear) return ERROR;//队空
e=Q.base[Q.front];//保存队头元素
Q.front=(Q.front+1)%MAXQSIZE;//队头指针加1
return OK;
}
取循环队列的队头元素
SElemType GetHead(SqQueue Q)
{//返回Q的队头元素,不修改队头指针
if(Q.front!=Q.rear)//队列非空
return Q.base[Q.front];//返回队头元素的值,队头指针不变
}
链式存储结构
//------队列的链式存储结构------
typedef struct QNode
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr
typedef struct
{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
链队的初始化
【算法步骤】
生成新结点最为头节点,队头和队尾指针指向此结点。
头结点的指针域置空。
Status InitQueue(LinkQueue &QC)
{//构造一个空队列Q
Q.front=Q.rear=new QNode;//生成新结点作为头结点,队头和队尾指针指向此结点
Q.front->next=NULL;//头结点的指针域置空
return OK;
}
链表入队
【算法步骤】
为入队元素分配结点空间,用指针p指向。//
将新结点数据域置为e。
将新结点插入到队尾。
修改队尾指针为p。
Status EnQueue(LinkQueue &Q,QElemType e)
{//插入元素e为Q的新的队尾元素
p=new QNode;//为入队元素分配结点空间,用指针p指向
p->data=e;//将新结点数据域置为e
p->next=NULL;Q.rear->next=p;//将新结点插入到队尾
Q.rear=p;//修改队尾指针为p
return OK;
}
链队的出队
【算法步骤】
判断队列是否为空,若空则返回ERROR。
临时保存队头元素的空间,以备释放。
修改头结点的指针域,指向下一个结点。
判断出队元素是否为最后一个元素,若是,则将队尾指针重新赋值,指向头结点。
释放原队头元素的空间。
Status DeQueue(LinkQueue & Q,QElemType &e)
{//删除Q的队头元素,用e返回其值
if(Q.front==Q.rear) return ERROR;//若空则返回ERROR
p=Q.front->next; //p指向队头元素
e=p->data;//e保存队头元素的值
Q.front->next=p->next;//修改头结点的指针域
if(Q.rear==p) Q.rear=Q.front; //最后一个元素被删除,队尾指针指向头结点
delete p;//释放原队头元素空间
return oK;
}
取链队的队头元素
SElemType GetHead(LinkQueue Q)
{//返回Q的队头元素,不修改队头指针
if(Q.front!=Q.rear)//队列非空
return Q.front->next->data;//返回队头元素的值,队头指针不变
}