生命不是要等待风暴过去,而是要学会在风暴中跳舞。 ——卡莉尔·吉布朗
目录
🌺前言:
🍁一.循环队列是什么?
🍏二.循环队列有什么作用?
🍀三.OJ题之设计循环队列
1.创建循环队列的结构体
🏵️1.MyCircularQueue创建一个结构体指针
🍊3.myCircularQueueIsEmpty判断循环队列为空
🍀4.myCircularQueueIsFull判断循环队列为满
🍊5.myCircularQueueEnQueue往循环队列里面入元素
🍂6.myCircularQueueDeQueue循环队列出元素
🍁7.myCircularQueueFront返回对列头的元素
🍑8.myCircularQueueRear返回队列尾的元素
🍀9.myCircularQueueFree销毁循环队列
🍋四.完整代码
🌺前言:
之前我们学习了队列,队列是先进先出的一个结构,而且也写了几个OJ题了,想必对队列也是很熟悉了,之前也一直留了一个东西没有讲,那就是循环队列。今天就让我们一起来学习一下循环队列吧。
🍁一.循环队列是什么?
循环队列顾名思义,就是一个队列它是循环的,之前我们学习的单链表的队列,那么循环队列就可以是单向循环的单链表。
我们知道队列的结构可以链式的结构,也可以是顺序的结构。今天我们要讲的就是顺序结构的循环队列,顺序结构就是一个数组。
普通的顺序结构的队列:我们使用队头和队尾来记录元素的变化。
1.刚开始front和rear都指向数组的第一个位置,也就是下标为0的位置,初始化front和rear都为0,所以当front和rear相等的时候,那么即队列此时为空。
2. 此时我们可以入队列,也就是往队列里面入元素。入一个元素,rear就往后面走一步。
3.然后我们可以出队列,出一个元素,front就往后面加1。
如果我还要继续入队列就不行了,因为rear已经走到尾了,但是数组还没有满,这种情况我们就叫假溢出,然后循环队列就横空出世了,就是为了解决假溢出的问题。
🍏二.循环队列有什么作用?
循环队列就是为了解决队列假溢出的问题,可以更好的利用空间。
循环队列的逻辑结构:(注意逻辑结构是我们假象出来)
注意这里空的情况是front==rear,但是当我们入队列,把队列入满了之后,rear还是跑到了front的位置,这样就没法区分空和满的情况, 所以我们把队列的一个位置给空出来,不放元素使用,就是拿来区分满和空的情况,这种情况满了就rear+1==front。
知道了循环队列的用途,我们就来一个OJ题来练练手。
🍀三.OJ题之设计循环队列
题目描述:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
示例:
MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1); // 返回 true
circularQueue.enQueue(2); // 返回 true
circularQueue.enQueue(3); // 返回 true
circularQueue.enQueue(4); // 返回 false,队列已满
circularQueue.Rear(); // 返回 3
circularQueue.isFull(); // 返回 true
circularQueue.deQueue(); // 返回 true
circularQueue.enQueue(4); // 返回 true
circularQueue.Rear(); // 返回 4
做题链接:循环队列
接口函数:
typedef struct {
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
}
int myCircularQueueFront(MyCircularQueue* obj) {
}
int myCircularQueueRear(MyCircularQueue* obj) {
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
}
void myCircularQueueFree(MyCircularQueue* obj) {
}
1.创建循环队列的结构体
typedef struct {
int*a;//数组
int front;//队列头
int rear;//队列尾
int k; //记录的队列的长度
} MyCircularQueue;
🏵️1.MyCircularQueue创建一个结构体指针
同样这里不是传参的方式,而是返回一个结构体的指针。
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a=(int*)malloc(sizeof(int)*(k+1));
//记住这里数组长度是k,但是我们开k+1个空间,剩下的一个空间就拿来判断满的
obj->front=obj->rear=0;
obj->k=k;
return obj;
}
🍊3.myCircularQueueIsEmpty判断循环队列为空
判断循环队列尾空,那可就太简单了。rear==front即为循环队列为空。
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->rear==obj->front;
}
🍀4.myCircularQueueIsFull判断循环队列为满
对于判断队列满了,还得仔细斟酌一下,难道就是rear+1==front就为满了吗,其实不是这样的,因为我们开始画的是循环队列的逻辑结构,只是为了我们好理解。实际情况可不这样的,循环队列实际就一个普通的数组,循环就体现在一些特殊的手段上面,这个手段就是取余。
这个确实是rear+1==front就满了,但是如果是下面的情况呢
就无法通过rear+1==front来判断满了,所以我们要使用取余来使得rear的下标和front重合。
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear+1)%(obj->k+1)==obj->front;//取余操作使得下标重合
}
🍊5.myCircularQueueEnQueue往循环队列里面入元素
直接往队列为入元素即可,满了就无法入元素了。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))//当队列为满时,那么久无法入队列了,直接返回false
return false;
obj->a[obj->rear]=value;
obj->rear++;
obj->rear%=(obj->k+1);//同样是取余,达到循环的目的
return true;
}
🍂6.myCircularQueueDeQueue循环队列出元素
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))//队列为空,那么久无法出元素了,直接返回false
return false;
obj->front++;//队列头往后走,即是出元素
obj->front%=(obj->k+1);
return true;
}
🍁7.myCircularQueueFront返回对列头的元素
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))//为空,返回-1
return -1;
return obj->a[obj->front];
}
🍑8.myCircularQueueRear返回队列尾的元素
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[(obj->rear+obj->k)%(obj->k+1)];//这里是尾,同样要使用取余来循环
}
🍀9.myCircularQueueFree销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
🍋四.完整代码
typedef struct {
int*a;//数组
int front;//队列头
int rear;//队列尾
int k; //记录的队列的长度
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a=(int*)malloc(sizeof(int)*(k+1));
//记住这里数组长度是k,但是我们开k+1个空间,剩下的一个空间就拿来判断满的
obj->front=obj->rear=0;
obj->k=k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->rear==obj->front;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear+1)%(obj->k+1)==obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))//当队列为满时,那么久无法入队列了,直接返回false
return false;
obj->a[obj->rear]=value;
obj->rear++;
obj->rear%=(obj->k+1);//同样是取余,达到循环的目的
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj))
return false;
obj->front++;
obj->front%=(obj->k+1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))//为空,返回-1
return -1;
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[(obj->rear+obj->k)%(obj->k+1)];//这里是尾,同样要使用取余来循环
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}