题目要求我们设计循环队列,其特点是容量固定,队列循环,如图所示:
这里的队列我们以链表队列举例,对于循环,只需要把尾节点的指针指向头节点。重点是队列的容量固定:如何确定队列是否已满和空,我们可能第一时间想到的是普通队列的尾指针因为扩容导致的移动,即下面的图片:当队尾节点的next指针指向的是队头指针的时候说明是容量已满状态
即:Queue->tail->next == Queue->head;
但是这么做会有问题,就是当只有一个元素的时候(k=1),这时队列头尾指针都指向这一个节点,所以无论是否存储,队尾节点的next指针都指向队头,还有,当出队列一直出到最后一个节点,这时也无法判断是否为空,因为这时 Queue->tail == Queue->head存在,空和有一个节点,判断条件都不会变化。
那我们就来解决无法判断空 和当 k=1 一个节点时无法判断是否为满的情况,让判断条件在不同情况有差异:
首先是区分判断空的两种情况,按照出队列的规律,剩一个数据和空的区别就是队头指针向后移动一个单位,(因为循环队头指针相当于循环了一圈)所以可以说当Queue->tail->next == Queue->head 的时候为空,这正好也是容量满的条件,为了区分这两种情况,我们可以新增一个节点(总共 k+1 个节点)不存储有效数据,区分这两种情况:
即Queue->tail->next == Queue->head 时说明容量已经满,
Queue->tail->next->next == Queue->head 时说明空。
第一张图片是已经满的情况,第二张图片是一直出队列直到为空的情况,分别对应两种情况,新增节点同样可以解决 k=1 的情况。
通俗的来解释就是新增了一个节点,当队尾节点两次next后才找到队头节点说明容量满了,当队尾节点一次next后就找到队头节点就说明(队头节点一直出队列甚至把队尾节点出掉)队列空了。
接下来是代码部分,首先是创建循环队列:
cpp
struct QueueNode {
int val;
struct QueueNode* next;
};
typedef struct {
struct QueueNode* head;
struct QueueNode* tail;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
int count = k;
struct QueueNode* cur_next = NULL;
q->head = (struct QueueNode*)malloc(sizeof(struct QueueNode));
struct QueueNode* cur = q->head;
for (count; count > 0; count--) {
cur_next = (struct QueueNode*)malloc(sizeof(struct QueueNode));
cur->next = cur_next;
cur = cur_next;
}if (cur_next != NULL) {
q->tail = cur_next;
cur_next->next = q->head;
}
return q;
}
注意creat函数创建了 k+1 个节点,并且队头队尾指针指向同一节点。
接着是判断容量满和空的函数:
cpp
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
if (obj->tail->next == obj->head) {
return true;
}
else {
return false;
}
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
if (obj->tail->next->next == obj->head) {
return true;
}
else {
return false;
}
}
和刚才解释的条件一样。
然后是检查是否插入或删除成功的函数:
cpp
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj)) {
return false;
}
obj->tail = obj->tail->next;
obj->tail->val = value;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj)) {
return false;
}
obj->head=obj->head->next;
return true;
}
这里删除函数我是直接把队头指针向后next操作一下。
最后是返回队头函数和返回队尾元素函数:
cpp
int myCircularQueueFront(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj)) {
return -1;
}
return obj->head->val;
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj)) {
return -1;
}
return obj->tail->val;
}
直接返回即可。
最后不要忘记写free函数,这就是文章的全部内容了,解释的很烂还请谅解,如有错误欢迎指出。