3.5.2 循环队列——队列的顺序表示和实现

队列也有两种表示形式,顺序和链式。 与顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外,尚需附设两个整形变量front和rear分别指示队列头元素及队列尾元素的位置(后面分别称为头指针和尾指针)。队列的顺序存储结构表示如下

//--------队列的顺序存储结构------------
#define MAXQSIZE 100      //队列可能达到的最大长度
typedef struct
{
    QElemType *base;     //存储空间的基地址
    int front;           //头指针[通过基地址用数组进行访问]
    int rear;            //尾指针
}SqQueue;

为了在c语言中表述方便在此约定初始化创建空队列时,令front=rear=0,每当插入新的队列尾元素时,尾指针rear增1;每当删除队列的头元素时,头指针front增1。因此在非空队列中,头指针始终指向队列的头元素,而尾指针始终指向队列尾元素的下一个位置。
《3.5.2 循环队列——队列的顺序表示和实现》 假设当前队列分配的最大空间是6,则当队列处于图3.12(d)所示的状态时不可再继续插入新的队尾元素,否则会出现溢出现象,也就是因数组越界而导致程序的非法操作错误。事实上此时队列可用空间并没有占满,所以这种现象成为“假溢出”。这是由“队尾入队,队头出队”这种受限制的操作造成的。 解决这种“假溢出”的一个方法是将顺序栈变成循环队列如图3.13。 头、尾指针以及队列的元素关系不变,只是在循环队列中,头、尾指针“依循环增1”的操作可用“模”运算来实现。通过取模,头指针和尾指针就可以在顺序表空间内以头尾衔接的方式“循环”移动。
《3.5.2 循环队列——队列的顺序表示和实现》  
《3.5.2 循环队列——队列的顺序表示和实现》 在图3.14(a)中,队头元素是J5,在元素J6入队之前,在Q.rear的值为5,当元素J6入队之后,通过“模”运算,Q.rear=(Q.rear+1)%6,得到的Q.rear的值为0,而不会出现图3.12(d)的“假溢出”状态。 在图3.14(b)中,j7,j8,j9,j10相继入队,则队列空间均被占满,此时头尾指针相同。 在图3.14(c)中,若j5和j6相继从图3.14(a)所示的队列中出队,使队列此时呈“空”的状态,头尾指针的值也是相同的。 所以,循环队列不能以头尾指针是否相同作为是否队“满”的判断依据。 所以有以下两种方法可以断定是否队满。 (1)少一个元素空间,即队列空间大小为m时,有m-1个元素认为是队满。这样判断队空的条件不变,即当头、尾指针的值相同时,则认为队空,而当尾指针在循环意义上加1后是等于头指针的,则认为队满,因此,在循环队列中队空和队满的条件是: 队空判断条件:Q.rear==Q.front 队满判断条件:(Q.rear+1)%6==Q.front 如图3.14(d),当j7,j8,j9进入图3.14(a)所示的队列后,(Q.rear+1)%MAXQSIZE的值等于Q.front,此时认为队满。 (2)另设一个标志位以区别队列是“空”还是“满”。具体描述参考【数据结构】严蔚敏 C语言 第二版 第三章习题算法设计题的第(7)题,由读者自行设计完成。 下面给出第一种方案的实现过程 1.初始化 循环队列的初始化操作就是动态分配一个预定义大小为MAXQSIZE的数组空间。 算法3.11 循环队列的初始化 【算法步骤】 ①为队列分配一个最大容量位MAXQSIZE的数组空间,base为执行数组空间的首地址 ②头指针和尾指针置为零,表示队列为空 【算法描述】

Status InitQueue(SqQueue &Q)
{
Q.base=new QElemType[MAXQSIZE];         //分配一个QElemType类型的大小为MAXQSIZE大小的数组空间
if(!Q.base) exit(OVERFLOW);            //内存分配失败
Q.front=Q.rear=0;                //头指针和尾指针相等且置零,队列置空
return OK;
}

2.求队列长度 对于非循环队列,尾指针和头指针的差值就是队列的长度,但是对于循环队列,差值可能为负数,所以应该将差值加上MAXQSIZE,然后再与MAXQSIZE求余。 算法3.12 求循环队列的长度 【算法描述】

int QueueLength(SqQueue Q)
{//返回Q的元素个数
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}

3入队 【算法步骤】 ①判断是否队满,若满返回ERROR ②将新元素加入队尾 ③队尾指针加1 【算法描述】

Status EnQueue(SqQueue &Q,QElemType e)
{//插入元素e到队列中
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;//如果队满则返回ERROR
Q.base[Q.rear]=e;//把新元素插入队列
Q.rear=(Q.rear+1)%MAXQSIZE;//队尾指针加1
return OK;
}

4出队 出队操作就是将队头元素取出并删掉 【算法步骤】 ①判断是否队空,空就返回ERROR ②取出队头元素并保存 ③队头元素加1 【算法描述】

Status DeQueue(SqQueue &Q,QElemTyoe &e)
{
if(Q.rear==Q.front) return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return OK;
}

5、取队头元素 【算法步骤】 ①判断是否队空,如果空就返回ERROR ②返回队头元素 【算法描述】

SElemType GetHead(SqQueue Q)
{//
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;
return Q.base[Q.front];
}

由上述分析可见,如果用户的应用程序中设有循环队列,则必须为它设置一个队列长度;若用户无法估计所用队列的最大长度,则易采用链队。

    原文作者:大师级小白
    原文地址: https://www.cnblogs.com/kitor/p/10123447.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞