循环队列概念和结构
文章目录
- 1. 循环队列的概念
- 2. 循环队列的分类
- 2.1 数组实现(3种判定)
- 1)增加一个 `size`
- 2)多开一个位置
- 2.2 链表实现
- 3. 循环队列实现
- 3.1 接口设计
- 3.2 接口实现
- 1)创建队列
- 2)实现判定空或满
- 3)入队和出队
- 4)获取队首队尾数据
- 5)销毁
- 3.3 从”获取队尾数据方法“引申 “计算循环队列数据个数”
- 4. 完成力扣题目
- 4.1 完整代码
- 4.2 提交截图
1. 循环队列的概念
在实际中,有时还会使用一种队列叫循环队列。
循环队列是一种线性数据结构,其操作表现基于 FIFO( 先进先出 )原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
2. 循环队列的分类
要想实现一个循环队列,除了要满足先进先出的特性,还要能判断循环队列是否满或者空,同时能拿到队头队尾的数据。下面主要讲解循环队列满或者空如何实现
2.1 数组实现(3种判定)
数组的插入就是在尾部 back
下标插入然后将其加一;而删除就是将数组头 front
往前走一步即可。下面将讲解如何进行循环队列满或者空的判定方法
1)增加一个 size
当 size == 0
就是空, size != 0
就是满
2)多开一个位置
空间开 k+1
个,当 front == back
就是空;当 (back+1)%(k+1) == front
就是满。这里的 % 是防止数组越界。
2.2 链表实现
单链表的其他操作都很好处理,但唯独很难处理去队尾数据。
这里可以通过 增加一个 backprev
指针 或者 改成双向链表 就可以解决
看上去链表比较容易实现,但难在初始化以及更多的判断操作以及空间上的开销。因此,下面将 用数组实现,并使用第二种判定方法 。
3. 循环队列实现
下面将以力扣题目 622. 设计循环队列 - 力扣(LeetCode) 进行讲解。
3.1 接口设计
typedef struct {
int*a; // 数组
int front; // 队列头
int back; // 队列尾
int k; // 数组大小
} MyCircularQueue;
// 创建循环队列,给出
MyCircularQueue* myCircularQueueCreate(int k);
// 判断是否空
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
// 判断是否满
bool myCircularQueueIsFull(MyCircularQueue* obj);
// 入队,成功则返回true
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);
// 出队,成功则返回true
bool myCircularQueueDeQueue(MyCircularQueue* obj);
// 获取队首数据,如果队列为空,返回 -1
int myCircularQueueFront(MyCircularQueue* obj);
// 获取队尾数据,如果队列为空,返回 -1
int myCircularQueueRear(MyCircularQueue* obj);
// 销毁
void myCircularQueueFree(MyCircularQueue* obj);
3.2 接口实现
1)创建队列
这里需要开辟 k+1
个空间,从而进行第二种判定方法
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int) * (k + 1));
obj->front = 0;
obj->back = 0;
obj->k = k;
return obj;
}
2)实现判定空或满
首先实现这两个函数,有利于后续函数复用,简化代码
// 相等则为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->back;
}
// 因为要防止数组越界,所以要进行%操作
// 要注意开辟的空间是k+1,因此要%obj->k+1
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->back+1)%(obj->k+1)==obj->front;
}
3)入队和出队
入队和出队就相对简单了,要注意数组不要越界即可
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->back]=value;
obj->back++;
obj->back%=(obj->k+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
// 直接加1即可,不需要覆盖数据
obj->front++;
obj->front%=(obj->k+1);
return true;
}
4)获取队首队尾数据
对于队首数据 :首先判定是否为空,然后直接获取
对于队尾数据 :首先也是判定是否为空,然后获取,这里要注意数据越界。此处可以直接用 if else
进行判定,但还有更简单的办法: (obj->back-1+obj->k+1)%(obj->k+1)
,简化过后就是 (obj->back+obj->k)%(obj->k+1)
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
//return obj->a[(obj->back-1+obj->k+1)%(obj->k+1)];
return obj->a[(obj->back+obj->k)%(obj->k+1)];//简化
}
5)销毁
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
3.3 从”获取队尾数据方法“引申 “计算循环队列数据个数”
正常清空就是 back-front
,但会出现越界;这时采用上述方法,就可以写成: (back + (k+1) - front) % (k+1)
,具体代码可以自己进行实现
4. 完成力扣题目
4.1 完整代码
typedef struct {
int*a;
int front;
int back;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int) * (k + 1));
obj->front = 0;
obj->back = 0;
obj->k = k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->back;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->back+1)%(obj->k+1)==obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->back]=value;
obj->back++;
obj->back%=(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))
return -1;
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[(obj->back-1+obj->k+1)%(obj->k+1)];
return obj->a[(obj->back+obj->k)%(obj->k+1)];//简化
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
4.2 提交截图
至此,队列的学习完全结束!