从真题中理解循环队列

题目概述

如果希望循环队列中的元素都能得到利用,则可设置一个标志域tag,并以tag值为0或1来区分尾指针和头指针相同时的队列状态是“空”还是“满”,其数据结构如下:

typedef struct {
	ElemType *elem;
	int front;
	int rear;
	int tag;
	int maxSize;
}CTagQueue;

1)写出此结构对应的队空判断条件表达式。
2)试编写与此结构对应的入队列算法 Status EnCQueue(CTagQueue &Q,ElemType x),插入元素为x,其返回值为 ERROR或OK.

题目解答

1)

(Q.tag==0)&&(Q.rear==Q.front)

2)

Status EnCQueue(CTagQueue &Q,QElemType x)
{
    if((Q.tag==1)&&(Q.rear==Q.front))//队满
        return ERROR;
    Q.elem[Q.rear]=x;//新元素插入队尾
    Q.rear=(Q.rear+1)%MAXQSIZE;//队尾指针加1
    if(Q.tag==0) Q.tag=1;//标志改1,表示队列非空
    return OK;
}

相关知识

背景问题

为了在C语言中描述方便起见,在此约定:初始化创建空队列时,令 front = rear = 0 , 每当插入新的队列尾元素时,尾指针 rear 增 1; 每当删除队列头元素时 , 头指针 front 增 1 。因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向队列尾元素的下一个位置,如图所示。
在这里插入图片描述
假设当前队列分配的最大空间为 6, 则当队列处于上图(d) 所示的状态时不可再继续插入新的队尾元素,否则会出现溢出现象,即因数组越界而导致程序的非法操作错误。事实上,此时队列的实际可用空间并未占满,所以这种现象称为“假溢出”。这是由“队尾入队,队头出队”这种受限制的操作造成的。

循环队列

怎样解决这种“假溢出”问题呢?一个较巧妙的办法是将顺序队列变为一 个环状的空间,如图所示,称之为循环队列
在这里插入图片描述
头、 尾指针以及队列元素之间的关系不变,只是在循环队列中,头、 尾指针“依环状增1”的操作可用“模”运算来实现。 通过取模,头指针和尾指针就可以在顺序表空间内以头尾衔接的方式“循环”移动。

在下图(a)中,队头元素是 J 5 J_5 J5, 在元素 J 6 J_6 J6入队之前,在Q.rear 的值为 5 5 5,当元素 J 6 J_6 J6入队之后,通过“模”运算, Q . r e a r = ( Q . r e a r + 1 ) Q.rear = (Q.rear + 1)%6 Q.rear=(Q.rear+1), 得到 Q.rear 的值为0, 而不会出现前面普通队列“假溢出”状态。
在这里插入图片描述
在图(b)中, J 7 、 J 8 、 J 9 、 J 10 J_7、J_8、J_9、J_{10} J7J8J9J10 相继入队, 则队列空间均被占满, 此时头、尾指针相同。
在图( c )中, J 5 、 J 6 J_5、J_6 J5J6 相继从图(a)所示的队列中出队, 使队列此时呈“空”的状态, 头、尾指针的值也是相同的。

由此可见,对于循环队列不能以头、尾指针的值是否相同来判别队列空间是“满”还是“空” 。在这种情况下, 如何区别队满还是队空呢?

通常有以下两种处理方法。

  • 少用一个元素空间, 即队列空间大小为m时,有m-1个元素就认为是队满。这样判断队空的条件不变, 即当头、 尾指针的值相同时, 则认为队空;而当尾指针在循环意义上加1后是等于头指针, 则认为队满。因此, 在循环队列中队空和队满的条件是:
    • 队空的条件: Q . f r o n t = Q . r e a r Q.front = Q.rear Q.front=Q.rear
    • 队满的条件: ( Q . r e a r + 1 ) % M A X Q S I Z E = Q . f r o n t (Q.rear+ 1)\%MAXQSIZE = Q.front (Q.rear+1)%MAXQSIZE=Q.front

如图(d)所示, 当 J 7 、 J 8 、 J 9 J_7、J_8 、J_9 J7J8J9进入图(a)所示的队列后, ( Q . r e a r + 1 ) % M A X Q S I Z E = Q . f r o n t (Q.rear+ 1)\%MAXQSIZE = Q.front (Q.rear+1)%MAXQSIZE=Q.front, 此时认为队满。

  • 另设一个标志位 T a g Tag Tag以区别队列是“空”还是“满” 。具体描述参考本文题目解答

完整代码

#include<stdlib.h>
#include<stdio.h>
#define MAXQSIZE 100
#define OK 1
#define ERROR -1
#define OVERFLOW -1
typedef int QElemType;
typedef int Status;
typedef struct
{
    QElemType *elem;
    int front;
    int rear;
    int tag;
    int maxSize;
}CTagQueue;

Status InitQueue(CTagQueue &Q)
{
    Q.elem=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
    if(!Q.elem)
        exit(OVERFLOW);//存储分配失败
    Q.front=Q.rear=0;//头尾指针置为0
    Q.tag=0;//标志初始化为0,队列为空
    return OK;
}
int QueuenLength(CTagQueue Q)
{
    return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;//返回Q的元素个数,即队列长度 
}

//入队
Status EnCQueue(CTagQueue &Q,QElemType x)
{
    if((Q.tag==1)&&(Q.rear==Q.front))//队满
        return ERROR;
    Q.elem[Q.rear]=x;//新元素插入队尾
    Q.rear=(Q.rear+1)%MAXQSIZE;//队尾指针加1
    if(Q.tag==0) Q.tag=1;//标志改1,表示队列非空
    return OK;
}
//出队
Status DeQueue(CTagQueue &Q,QElemType &e)
{
    if((Q.tag==0)&&(Q.rear==Q.front))//队空
        return ERROR;
    e=Q.elem[Q.front];//保存队头元素
    Q.front=(Q.front+1)%MAXQSIZE;//对头指针加1
    if(Q.tag==1) Q.tag=0;//标志改0,表示队列非满
    return OK;
}
//打印队列
void PrintQueue(CTagQueue Q)
{
    while (((Q.rear+1)%MAXQSIZE==Q.front)||Q.front!=Q.rear)
    {
        printf("%d ",Q.elem[Q.front]);
        Q.front++;
    }
    printf("\n");
}


int main()
{
    CTagQueue Q;
    QElemType m_e;
    InitQueue(Q);
    printf("入队顺序:\n");
    for(int i=0;i<10;i++)
        EnCQueue(Q,i);
    for(int i=0;i<10;i++)
        printf("%d ",Q.elem[i]);
    printf("\n");
    printf("出队顺序:\n");
    for(int i=0;i<10;i++)
    {
        DeQueue(Q,m_e);
        printf("%d ",m_e);
    }
    printf("\n");
    system("pause");
    return 0;
}

输出结果

在这里插入图片描述

参考文献

[1] 严蔚敏,吴伟民. 数据结构(C语言版). 北京: 清华大学出版社,2020
[2] 严蔚敏,李冬梅,吴伟民. 数据结构(C语言版)(第二版). 北京: 人民邮电出版社,2021
[3] 王道论坛. 2022数据结构考研复习指导. 北京:电子工业出版社,2021

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FriendshipT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值