![](https://i-blog.csdnimg.cn/blog_migrate/54aa792ceaa2666d0489940f642c2964.png)
利用数组的特性可以方便在代码实现的。
这里我们想一个问题,如何判断空与满。
front从顺时针到rear的数据才为有效数据
如果开辟K个空间,保存K个有效数据。
队满情况
入数据,rear向后移动,当rear==front时,环形队列为满
![](https://i-blog.csdnimg.cn/blog_migrate/383dd1ed23a53e761e028a774531a74c.png)
队空情况
出数据,front向后移动,直到与rear相等时为空
![](https://i-blog.csdnimg.cn/blog_migrate/bc30a36c333078285667674209d42f0c.png)
继续pop
![](https://i-blog.csdnimg.cn/blog_migrate/b0bfa7b4071d9f0a8a593c096810e761.png)
观察判空与判满,会发现无论时空还是满 都是rear==front
所以我们需要多开辟一段空间,用来表示队列的尾部,不存储有效数据
如果开辟K+1个空间,保存K个有效数据。
![](https://i-blog.csdnimg.cn/blog_migrate/9911c746803732b03edb99905019e6e0.png)
队满情况
![](https://i-blog.csdnimg.cn/blog_migrate/e1e898f7ecc5629811a84f727c6870f9.png)
这时候满的情况为rear下一位置==front就为满情况。
当rear+1==front时(特殊情况后面讲)队列为满情况,不在插入数据。
队空情况
出队列,front开始移动
![](https://i-blog.csdnimg.cn/blog_migrate/94d7ce0687c8d1314f87def5a2473925.png)
继续pop,继续移动
![](https://i-blog.csdnimg.cn/blog_migrate/de58dc2cd991f8828bdc4a47df5e403b.png)
当front移动到rear指向的空间时,队列为空,不可再删除
各个接口实现讲解
声明结构体类型
typedef struct {
int*a;
int front;
int rear;
int k;
} MyCircularQueue;
a为数组,front队头位置,rear队尾位置,k为可使用空间数量
初始化Init
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));//1.
obj->front=obj->rear=0;//1.
obj->k=k;//1.
obj->a=(int*)malloc(sizeof(int)*(k+1));//2.
return obj;
}
开辟一个结构体空间,初始化front与rear位置=0,参数k赋值到有效空间k中;
开辟队列所需数组,参数给的k为存放有效所需的空间个数,但我们要多开辟一个空间当作尾。
![](https://i-blog.csdnimg.cn/blog_migrate/4a0be9a745afed4a794e9dba9ec07390.png)
入列Push
这里的入列,我们前提条件为队列不为满的情况,进行分类讨论
看以下代码
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear++]=value;
return true;
}
是对的吗?不是
将数据放入以rear索引的空间后,rear向后移动一位将下一个位置定位尾
![](https://i-blog.csdnimg.cn/blog_migrate/1ebedbafdf9124cf8ac0715c522978ee.png)
这样看似没有问题,但是如果再push前已经出现pop过,代码就会可能出问题
![](https://i-blog.csdnimg.cn/blog_migrate/e1e686f9fe81de079df2b23fe2f73cc3.png)
push (5);
![](https://i-blog.csdnimg.cn/blog_migrate/57bde422b90a169ddcec239f3d36535a.png)
这样看似乎没有什么问题
但是数组rear+1超出了数组的范围,越界访问
真实情况是
![](https://i-blog.csdnimg.cn/blog_migrate/77e83f051a21edbc72a0899299d71b24.png)
push(5);
![](https://i-blog.csdnimg.cn/blog_migrate/2ecd88e08602fb818803b9d032215b02.png)
超出范围
如何改该bug呢?
将rear模上k+1的值就是rear下一步
![](https://i-blog.csdnimg.cn/blog_migrate/2b510edf2b6fe53d3a89384589300798.png)
正确代码:
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear++]=value;
obj->rear%=(obj->k+1);//当rear超出范围模上k+1,就可以归为0,如果rear为超出k+1就不受影响
return true;
}
出列Pop
提示:front开始向右,到rear的才是有效数据,rear如果在front前也是从front向右,到数组底后,然后从索引0到rear,这2段位有效数据。
先声明数据不为空,
在入列时我们已经知道了存在特殊情况,所以我们现在,直接分类
普通情况front+1不超出数组范围
![](https://i-blog.csdnimg.cn/blog_migrate/146bf2a4083dc5c08cc882da8902f09c.png)
pop():front向后移动一位,
![](https://i-blog.csdnimg.cn/blog_migrate/37fcce7dca0262f2b808497bc1e6f471.png)
完成
特殊情况front+1超出数组范围
![](https://i-blog.csdnimg.cn/blog_migrate/d30cab83ac3323e9ab1a8a77d59e964f.png)
pop():front向后移动一位,
![](https://i-blog.csdnimg.cn/blog_migrate/7f8fff97459d9921b5d6007ec7b0b302.png)
这样的front也要进行取模操作
完整Pop代码
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
++obj->front;
obj->front%=(obj->k+1);//和rear原理一样
return true;
}
读取队尾
先确定不为队列不为空
分类讨论,如果尾索引为0,与尾索引不为0
不为0
![](https://i-blog.csdnimg.cn/blog_migrate/26ee724920d6b17a37a063ee22f326bd.png)
rear-1就是队尾元素值:1
不为0
![](https://i-blog.csdnimg.cn/blog_migrate/f21f8c6ec8a27f77b2389037edefb26f.png)
这时候队尾元素为数组索引k值:9
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
//分类讨论
if(obj->rear==0)
{
return obj->a[obj->k];
}
else
{
return obj->a[obj->rear-1];
}
//减一加k+1模k+1
//return obj->a[(obj->rear-1+obj->k+1)%(obj->k+1)]
}
读取队头
先确定不为队列不为空
数组front索引就是对头元素
代码为
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->front];
}
队列判空
之前提到过,简单描述:如果front==rear时直接为空;
看代码
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front==obj->rear;
}
队列判满
根据前面的情况,分类讨论,当尾为数组最后一个元素,不为最后一个元素
不为最后一个元素
![](https://i-blog.csdnimg.cn/blog_migrate/5f95c1c322fd2029bfb58d75f7137a75.png)
直接判断rear+1==front
为最后一个元素
![](https://i-blog.csdnimg.cn/blog_migrate/4a2b8a2034b45ab690167531079adbcd.png)
需要判断0==front;
接口代码:
bool myCircularQueueIsFull(MyCircularQueue* obj) {
//直接分类讨论
if(obj->rear+1==obj->k+1)
{
if(0==obj->front)
{
return true;
}
}
else
{
if(obj->rear+1==obj->front)
{
return true;
}
}
return false;
//取模后判断
//return (obj->rear+1)%(obj->k+1)==obj->front;
}
销毁Free
先销毁结构体内指针指向的数组,在销毁结构体。
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
谢谢观看