前言:本章基于《大话数据结构》和王卓老师的视频内容,为刚接触数据结构的初学者提供一些帮助。
💕如果我的文章对你有帮助,点赞、收藏、留言都是对我最大的动力
🐱👤我的上一篇文章:【栈与队列】之栈的链式存储(图文详细介绍!!)_﹏陈、词的博客-CSDN博客
4.10 队列的定义
队列:只允许在一端进行插入操作,而在另一端进行删除操作的线性表(头删尾插)。
特点:先进先出,允许插入的一段称为队尾,允许删除的一端称为队头。
4.11 队列的抽象数据类型
4.12 循环队列的实现与操作
在正式介绍循环队列之前,我们先来简单了解一下“真溢出”和“假溢出”。
“真溢出”:若front=0,rear=MAXQSIZE时,再继续入队的话,就要发生真溢出了。
“假溢出”:就是front不等于0,rear=MAXQSIZE时,再入队,就会发生假溢出。我们可以看到下图还有许多空出来的位置没用被利用。
那该如何解决假溢出呢?第一种方法,就是将队中的元素依次先队头方向移动,而这种方法会浪费大量的时间,所以我们采取第二种方法,循环队列法。
4.12.1 循环队列的定义
循环队列:头尾相接的顺序存储结构的队列。如base[0]接在base[MAXQIZE-1]之后,若rear+1==M,就令rear=0;
那循环队列是如何实现的呢?我们接着往下看
主要的实现方法,就是利用模(mod,C语言中:%)运算
插入元素:Q.base[Q.rear]=x; Q.rear=(Q.rear+1)%MAXQSIZE;
删除元素:x=Q.base[Q.front]; Q.front=(Q.front+1)%MAXQSIZE;
这是我们需要注意的一个问题又出现了,就是队空和队满的条件都是front==rear,这又应该如何解决呢,这里有三个解决方法,
①另外设一个标志以区别队空、队满
②另设一个变量,记录元素个数
③少用一个元素空间
这里我们重点介绍第三种方法,少用一个元素空间。
以上图为例:MAXQSIZE=6,front=4,rear=3,(3+1)%6=4,所以此时的队列是满的。
介绍完队满队空的情况后,我们简单来介绍一下队列的长度。
第一个图的长度为:Q.rear-Q.front
第二个图的长度为:MAXQSIZE-Q.front
第三个图的长度为:MAXQSIZE-Q.front+Q.rear+0
因此通用的计算队列长度的公式为:
(rear-front+MAXQSIZE)%MAXQSIZE;
好了到这里我们已经知道什么是循环队列了,然后我们再来介绍一下队列的一些操作。
4.12.2 循环队列的操作
结构定义
typedef struct {
QElemType* base;//初始化的动态分配存储空间
int front;//头指针
int rear;//尾指针
}SqQueue;
队列的初始化
Status lnitQueue(SqQueue& Q)
{
Q.base = new QElemType[MAXQSIZE];//分配数组空间
// Q.base=(QElemType*)malloc(MAXQSIZE(QElemType));
if (!Q.base)exit(OVERFLOW);//存储分配失败
Q.front = Q.rear = 0;//头指针尾指针置为0,队列为空
}
队列的长度
int QueueLength(SqQueue Q)
{
return ((Q.rear - Q.front + MAXQSIZE) % MAXQSIZE);
}
队列的入队
Status EnQueue(SqQueue& Q, QElemType e)
{
if ((Q.rear + 1) % MAXQSIZE == Q.front)return ERROR;//队满
Q.base[Q.rear] = e;//新元素加入队尾
Q.rear=(Q.rear + 1) % MAXQSIZE;//队尾指针+1
return OK;
}
队列的出队
Status DeQueue(SqQueue& Q, QElemType& e)
{
if (Q.front == Q.rear)
return ERROR;//队空
e = Q.base[Q.front];//保存队头元素
Q.front = (Q.front + 1) % MAXQSIZE;//队头指针+1
return OK;
}
取队头元素
QElemType GetHead(SqQueue Q)
{
if (Q.front != Q.rear)//队列不为空
return Q.base[Q.front];//返回队头指针元素的值,队头指针不变
}