上次我们提到,队列的顺序存储结构是完全可以优化的,从而避免不必要的空间上的浪费,今天,我们就来学习,并且实现一下。
解决这个问题的主要办法就是把队列的头和尾连接起来,成为一个环形的队列,我们称它为环形队列或循环队列。环形队列首尾相连后,当队尾指针rear==MaxSize-1后,再前进一个位置就到达0,于是就可以使用另一端的空位置存放队列元素了。实际上存储器的地址总是连续编号的,因此我们可以使用数学上的取余运算%来实现。
为此我们可以把原来的front或rear自增换成front=(front+1)%MaxSize;rear=(rear+1)%MaxSize;至于什么意思,你品,你细品......就有内味儿了吧。环形队列的队头指针front和队尾指针rear初始化时都置为0,即front=rear=0。在进队和出队时,队头指针和队尾指针分别循环增1。那么,环形队列的队空和队满如何设置呢?显然队空条件是rear==front。但是当进队的速度快于出队的速度时,队尾指针会追赶上队首的指针,此时看出环形队列的队满条件也是rear==front,这就产生了矛盾。那么怎样区分队空和队满呢?我们改为“队尾指针循环增1时等于队头指针”作为队满条件,也就是说尝试进队一次,若达到队头,就认为满了,不能再进队。这样循环队列少用一个元素的空间,即该队列中在任何时刻最多只能有MaxSize-1个元素。因此,在循环队列中设置队空的条件是rear==front;队满条件是(rear+1)%MaxSize==front。如下是循环队列的操作示意图:
在这样设计的循环队列中,我们可以使用如下方法实现循环队列:
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define MaxSize 50
typedef char ElemType;
typedef struct{
ElemType data[MaxSize];
int front,rear;//front指向队头,rear指向队尾。
}SqQueue;
void InitQueue(SqQueue* q){//初始化队列。
q->front=q->rear=0;//设为初始状态,即front==rear==0。
}
void DestroyQueue(SqQueue* q){//销毁队列。
free(q);//释放队列q占用的存储空间。
}
bool QueueEmpty(SqQueue* q){//判断队列是否为空。
return q->front==q->rear;//队空条件:front==rear。
}
bool enQueue(SqQueue* q,ElemType e){//加入队列。
if((q->rear+1)%MaxSize==q->front)return false;//若队列满了,则返回false。
q->rear=(q->rear+1)%MaxSize;//队尾循环增1。
q->data[q->rear]=e;//元素e插入队尾位置。
return true;
}
bool deQueue(SqQueue* q,ElemType* e){//出队。
if(q->front==q->rear)return false;//若队列为空,则返回false。
q->front=(q->front+1)%MaxSize;//队头循环+1。
*e=q->data[q->front];//取出队头元素。
return true;
}
这样,我们就可以充分利用数组的空间,不用造成浪费了。循环队列到此结束,欢迎点赞,打赏,留言交流!