读注释,敲代码,学习数据结构——循环队列

Gilana ·
更新时间:2024-11-15
· 627 次阅读

小萌新今天和大家聊聊什么是循环队列,话不多说,开门见山吧!

如果说可以用循环队列解决队列的虚假满的状态,那么什么是虚假满状态?

假设当前顺序队列分配的最大空间是6,当队尾指针从5下标指向6下标时(6下标实际不存在),说明此时队列已满,然而依然可以进行出队的操作,顺序队不能像顺序栈那样进行存储再分配扩大数组空间,所以队列的实际可用空间并未占满。

循环队列就是将顺序队列构造成为一个环状的队列空间,如图:
在这里插入图片描述

注意:

此时当指针front=指针rear时,无法判断队列空间是满还是空,有两种解决思路:
1.另设一个标志位区分队列空间是满还是空;
2.少用一个空间,约定以“队列头指针在队列尾指针的下一位置”作为队列满的状态标志。

来吧!举个栗子吧,如图:
【情况1】
在这里插入图片描述
初始化,队头指针front,队尾指针rear都指向0下标;
当存入数据元素1后,队尾指针rear从0下标指向1下标;
当存入数据元素2后,队尾指针rear从1下标指向2下标;
当存入数据元素3后,队尾指针rear从2下标指向3下标;
当存入数据元素4后,队尾指针rear从3下标指向4下标;

当存入数据元素8后,队尾指针rear从7下标指向0下标;(队列已满)
如何判断队头指针front与队尾指针rear重合时,队列是空还是满呢?我们假如用flag来标记队头指针front与队尾指针rear的重合状态,于是规定当flag=0时:队列为空;当flag=1时,队列为满。

【情况2】
在这里插入图片描述
我们把7下标的空间空出不存放数据,当数据元素7进为6下标的空间后,队尾指针rear就指向了为7下标的空间(此时7下标空间是空的),理论上8个空间只用了7个空间,但在逻辑上我们规定此时队列已满。当rear+1后,队头指针front与队尾指针rear重合,说明此时队列空间逻辑已满,我们先将队头指针所指空间的元素出队,队头指针前移指向1下标,此时再将元素进下标为7的空间,队尾指针rear前移(指向0下标),当rear+1后,队头指针front与队尾指针rear又重合,说明此时队列空间逻辑又已满,依次循环往复,出队—>队头指针front前移——>后面入队——>队尾指针rear前移(判断条件是rear+1后,队头指针front与队尾指针rear是否重合,不重合则可继续入队)。

但是,计算机是没有环状的存储空间的,依然是以线性存储的方式,那计算机如何判断什么时候队尾指针再次与队头指针重合呢?

—————OK!那就是:进行模运算—————

具体模运算是怎么实现的,文字显得苍白无力,还是那句话,把代码跑起来,慢慢体会!

#include #include #include #define MAXSIZE 8//定义队列初始化存放8个数据元素 typedef int ElmeType; /*给出顺序队列的结构*/ typedef struct Queue { ElmeType *base;//指针base指向有效的队列空间 int front;//队头指针 int rear;//队尾指针指向下一个有效的空间 }Queue; /*初始化顺序队列*/ void InitQueue(Queue *Q) { Q->base=(ElmeType *)malloc(sizeof(ElmeType)*MAXSIZE);//开辟队列空间 assert(Q->base!=NULL);//断言——是否开辟空间成功 Q->front=Q->rear=0;//队头指针和队尾指针都指向队列的0下标,此时队列为空 } /*入队*/ void EnQueue(Queue *Q,ElmeType x) { //数据入队后,队尾指针从当前空间指向下一个有效的空间,此时称队尾指针是伪指针; //当伪指针所指下标+1后正好等于队列空间容量时,此时我们希望伪指针可以重新指向队头,而不是出界,于是进行模运算; //(Q->rear+1)%MAXSIZE——若模为0,则伪指针恰好指向队列的最后一个有效空间,我们需要让此时的伪指针重新指向0下标而不是最后一个有效空间; //(始终都要将队列最后一个有效空间空出)循环开始: if((Q->rear+1)%MAXSIZE==Q->front)//若伪指针所指下标+1与队头指针指向相同的下标,此时判断为队列逻辑已满 return;//返回,理论上队列保留了队列最后一个有效空间 Q->base[Q->rear]=x;//否则队列逻辑不满,继续在队尾指针所指下标进行入队,入队完成后,队尾指针又从当前空间指向下一个有效的空间 Q->rear=(Q->rear+1)%MAXSIZE;//当逻辑空间满后,模运算实现队尾指针重新指向0下标而不是最后一个有效空间; } /*展示顺序队列元素*/ void ShowQueue(Queue *Q) { printf("顺序队列中存放的元素:"); for(int i=Q->front;i!=Q->rear;) { printf("%d ",Q->base[i]);//依次打印队头指针所指下标中的数据到队尾指针所指下标中的数据 i=(i+1)%MAXSIZE;//循环打印,7下标不能打印,重新回到0下标(循环时,队尾下标-队头下标=-1) } printf("\n"); } /*出队*/ void DeQueue(Queue *Q) { //出队一个元素,队头指针指向下一个有效的数据元素 if(Q->front==Q->rear)//队头队尾指向相同,队列为空 return; Q->front=(Q->front+1)%MAXSIZE;//队头指针循环,模运算实现队头指针重新指向0下标而不是最后一个有效空间; } /*取队头元素*/ void GetHead(Queue *Q,ElmeType *v)//指针v带回队头元素 { //要获取队头,前提是队列不空 if(Q->front==Q->rear)//队列为空 return; *v=Q->base[Q->front];//必须在base所指的空间里取元素 } /*顺序队列的长度*/ int Length(Queue *Q) { return (Q->rear - Q->front); //下标0开始存放数据,进队后,队尾指针指向下一个有效的新空间 //队列中元素的个数正好是队尾队头所指的下标之差 //但是当队列逻辑空间满后,再存储数据需要先出队,再进行入队,此时队尾队头所指的下标之差为-1 } /*清除顺序队列*/ void ClearQueue(Queue *Q) { Q->front=Q->rear=0;//队列置为空 } /*销毁顺序队列*/ void DestroyQueue(Queue *Q) { free(Q->base);//释放base所指的队列空间 Q->base=NULL;//预防野指针 } /**/ void main() { Queue Q; ElmeType e;//定义队头元素 InitQueue(&Q);//&Q是传入队列的地址 printf("将1,2,3,4,5,6,7,8依次入队\n"); for(int i=1;i<=8;++i) { EnQueue(&Q,i); } ShowQueue(&Q); printf("顺序队的长度为:%d\n",Length(&Q)); printf("\n"); printf("进行出队\n"); DeQueue(&Q); ShowQueue(&Q); GetHead(&Q,&e); printf("队头元素为:%d\n",e); printf("\n"); printf("将元素10入队\n"); EnQueue(&Q,10); ShowQueue(&Q); printf("\n"); printf("进行出队\n"); DeQueue(&Q); ShowQueue(&Q); GetHead(&Q,&e); printf("队头元素为:%d\n",e); printf("\n"); printf("将元素20入队\n"); EnQueue(&Q,20); ShowQueue(&Q); printf("\n"); ClearQueue(&Q); DestroyQueue(&Q); }

运行结果:
在这里插入图片描述


作者:@AS_constancy



队列 数据 循环 学习 循环队列 数据结构

需要 登录 后方可回复, 如果你还没有账号请 注册新账号