第一次尝试
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
- 方法:在真实的系统中,是不存在循环的内存的,这里所说的循环指的是在逻辑上循环,在物理上实现循环的特点,这里我用两种方式来实现;
- 用数组创建;设置两个标志位 front 和 rear,在一开始 front = rear =0 下标,而判空正是使用这个条件;但是判满就不能使用 front == rear,所以我们可以在申请空间时多申请一个结点,开辟 k + 1 个空间,因此判满的条件就是 rear + 1 == front,但是这样子很容易超出数组访问权限,所以条件变为 (rear + 1)%(k + 1)== front;
下面的接口在知道了判空和判满的条件之后,实现他们就会变简单了,需要注意的是,在返回尾结点元素的时候,因为当前的 rear 空间是空的,所以返回的是 rear - 1,但是若 rear = 0,就会下标越界,此处就需要判断一下,防止出错;下面画出了循环队列的几种情况:
- 用链表创建;因为链表是存储一个数据就开辟一个空间,所以和上面的做法不太一样,我们创建头结点,在其中设置头指针 front,尾指针 rear,有效个数 size,最大个数 capacity,当 size == 0,队列为空;当 size == capacity,队列为满;
我们还需要创建一个链表结点的结构体以及一个开辟节点空间的函数,然后本题需要写的接口基本上和队列的创建没啥区别,就是需要在入队出队是注意 size 的变化即可;
//使用数组创建循环队列########################################################
typedef struct {
//首元素下标
int _front;
//尾元素下标
int _rear;
//动态开辟数组空间
int* _date;
//数组大小
int _k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
//创建结构体变量
MyCircularQueue* mq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
//数组大小为所能存储最大元素个数加一
mq->_date = (int*)malloc(sizeof(int) * (k + 1));
//数组大小 _k 就为 k + 1;
mq->_k = k + 1;
mq->_front = mq->_rear = 0;
return mq;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//参数检验,如果队列满了,就返回false
if((obj->_rear + 1) % obj->_k == obj->_front){
return false;
}
//插入元素,尾下标变化
obj->_date[obj->_rear] = value;
obj->_rear = (obj->_rear + 1) % obj->_k;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//参数检查,如果队列为空,就返回 false
if(obj->_front == obj->_rear){
return false;
}
//因为这是数组,所以就不需一个一个释放空间,只需将头下表变化即可
obj->_front = (obj->_front + 1) % obj->_k;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
//参数检查,队列为空就返回 -1
if(obj->_front == obj->_rear){
return -1;
}
return obj->_date[obj->_front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
//参数检查,队列满了就返回 -1
if(obj->_front == obj->_rear){
return -1;
}
//此处需要注意,如果 _rear == 0,那么就不能返回 _rear -1 下标的元素,而是数组的最后一个元素,也就是 _k - 1 下标的元素
if(obj->_rear == 0){
return obj->_date[obj->_k - 1];
}
return obj->_date[obj->_rear - 1];
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
//如果 front == rear 就说明队列为空
if(obj->_front == obj->_rear){
return true;
}
return false;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
//如果 (rear + 1) % k == front 就说明队列满了
if((obj->_rear + 1) % obj->_k == obj->_front){
return true;
}
return false;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->_date);
free(obj);
}
//使用链表创建循环队列#########################################################
//链表结点
typedef struct MyQueue {
int _date;
struct MYQueue* _next;
}MyQueue;
typedef struct {
//指向第一个结点
MyQueue* _front;
//指向最后一个节点,设置该指针是为了获取尾结点方便
MyQueue* _rear;
//有效元素个数
int _size;
//最大元素个数
int _capacity;
} MyCircularQueue;
//开辟链表结点空间
MyQueue* myQueueCreate(int value) {
MyQueue* mq = (MyQueue*)malloc(sizeof(MyQueue));
mq->_date = value;
mq->_next = NULL;
return mq;
}
MyCircularQueue* myCircularQueueCreate(int k) {
//动态开辟头结点
MyCircularQueue* mq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
mq->_front = mq->_rear = NULL;
//一开始有效元素设置为 0,最大元素为给的 k
mq->_size = 0;
mq->_capacity = k;
return mq;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//参数检查,如果队列满了,就返回false
if(obj->_capacity == obj->_size){
return false;
}
//分两种情况向队列中插入,空队列 front 和 rear 都指向插入的节点
if(obj->_size == 0){
obj->_front = obj->_rear = myQueueCreate(value);
obj->_size++;
}
else{
//非空队列在最后一个节点后插入,更新 rear 指针指向
obj->_rear->_next = myQueueCreate(value);
obj->_rear = obj->_rear->_next;
obj->_size++;
}
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//参数检查,空队列就返回 false
if(obj->_size == 0){
return false;
}
//分两种情况插入,如果队列只有一个元素时,删除该节点,然后 front 和 rear 同时置NULL
if(obj->_size == 1){
free(obj->_front);
obj->_front = obj->_rear = NULL;
obj->_size = 0;
}
//如果元素不唯一,就删除第一个结点,然后修改 front 指向
else{
MyQueue* node = obj->_front;
obj->_front = node->_next;
free(node);
obj->_size--;
}
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(obj->_size == 0){
return -1;
}
return obj->_front->_date;
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(obj->_size == 0){
return -1;
}
return obj->_rear->_date;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
if(obj->_size == 0){
return true;
}
return false;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
if(obj->_size == obj->_capacity){
return true;
}
return false;
}
void myCircularQueueFree(MyCircularQueue* obj) {
while(obj->_size){
MyQueue* node = obj->_front;
obj->_front = node->_next;
free(node);
obj->_size--;
}
obj->_rear = obj->_front = NULL;
free(obj);
}