循环队列是我们可以对队列有更深一步的理解的题目,而且可以进一步加强其他方面的知识(例如对循环数组的取模运算,指针的解引用),是个蛮不错的巩固习题,话不多说,进入正题。
链接在此:设计循环队列
强烈建议先自己做一遍,直接看的话可能会比较不知所云
目录
本题可以使用 数组或链表 来设计,本篇文章都会涉及到
做这题时会遇到很多难点
先说结论:此题的难点在于如何判断数组的 空与满 ,不管是链表还是数组,实现此问题都是难点。
在数据结构中,我们通常在解决此问题时都是选择多设置一个位置,back指向当前元素的下一个。
但多出来的位置不是不用,例如:
这样可以比较好的解决此类问题。
利用数组设计:
思路:
已经有了上述的前置知识
我们就可以比较轻易地判断空与满,数组中的front
和back
下标 指向同一个位置时是空,那么什么时候会满呢?
当back
的下一个为front
时就为满,即back+1 == front
,
但是如果back
在front
后边,就需要我们的比较灵活的运用取模运算
在上边我们说到back+1 == front
时为满,但是在上图中,我们发现back+1
并不是front
,而是超出了数组,
我们说过,会定义N+1
个空间,N
是元素个数,经过思考,我们会发现N
就是back
的下标,N+1
就是back+1
位置的下标,
那我们(back + 1)% (N + 1) == front
时就是满
代码中剩下的取模运算也都大同小异
代码实现:
注意:我将代码中的判断空或满拿到了前边,防止使用时需要声明
c
typedef struct {
int* arr;
int front;
int rear;
int N;
} MyCircularQueue;
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return (obj->front == obj->rear);
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear + 1 ) % (obj->N + 1) == obj->front;
}
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* ret = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
ret->arr = (int*)malloc(sizeof(int)*(k+1));
ret->front = 0;
ret->rear = 0;
ret->N = k;
return ret;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
{
return false;
}
obj->arr[obj->rear] = value;
obj->rear++;
//防止rear出界
obj->rear %= (obj->N + 1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
obj->front++;
//防止front出界
obj->front %= (obj->N + 1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->arr[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
//此处可以不用取模,if与else判断也可以
return obj->arr[(obj->rear-1+(obj->N+1))%(obj->N+1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->arr);
free(obj);
}
/**
* Your MyCircularQueue struct will be instantiated and called as such:
* MyCircularQueue* obj = myCircularQueueCreate(k);
* bool param_1 = myCircularQueueEnQueue(obj, value);
* bool param_2 = myCircularQueueDeQueue(obj);
* int param_3 = myCircularQueueFront(obj);
* int param_4 = myCircularQueueRear(obj);
* bool param_5 = myCircularQueueIsEmpty(obj);
* bool param_6 = myCircularQueueIsFull(obj);
* myCircularQueueFree(obj);
*/
一一一一一一一一分割线一一一一一一一一
利用链表设计:
链表的实现就比较简单,但是此题有两个坑点
- 给的是匿名结构体
- 将链表链接起来比较的繁琐
思路:
在用数组实现时,我们定义了一头一尾两个下标,就可以判断长短
我们在链表实现时也要定义头尾指针
来进行判断
但由于是匿名结构体
我们当前按下图这样的方式定义,当然定义方式会随着你的改变而改变
下图这样的方式定义意味着你的空间是这样的
c
typedef struct List
{
struct List* front;
struct List* back;
struct List* next;
int val;
}List;
typedef struct {
List* cq;
} MyCircularQueue;
插入或者删除时 ,改变cq所指向的空间中front与back即可
当front == back时为空,back->next == front
时为满
代码实现:
注意: 结构体不能定义为ListNode,因为关于链表的题目一般会默认给你定义好一个这样的结构体,这也是博主经历后才知道的(痛苦)
c
typedef struct List
{
struct List* front;
struct List* back;
struct List* next;
int val;
}List;
typedef struct {
List* cq;
} MyCircularQueue;
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->cq->front == obj->cq->back;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return obj->cq->back->next == obj->cq->front;
}
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->cq = NULL;
List* cur = NULL;
int tmp = k + 1;
while (tmp--)
{
if (obj->cq == NULL)
{
obj->cq = (List*)malloc(sizeof(List));
obj->cq->front = obj->cq->back = obj->cq->next = obj->cq;
cur = obj->cq;
}
else
{
cur->next = (List*)malloc(sizeof(List));
cur = cur->next;
}
}
if (cur == NULL)
{
return obj;
}
cur->next = obj->cq;
return obj;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj))
{
return false;
}
obj->cq->back->val = value;
obj->cq->back = obj->cq->back->next;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return false;
}
obj->cq->front = obj->cq->front->next;
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->cq->front->val;
}
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
List* cur = obj->cq->front;
while (cur->next != obj->cq->back)
{
cur = cur->next;
}
return cur->val;
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj);
}
/**
* Your MyCircularQueue struct will be instantiated and called as such:
* MyCircularQueue* obj = myCircularQueueCreate(k);
* bool param_1 = myCircularQueueEnQueue(obj, value);
* bool param_2 = myCircularQueueDeQueue(obj);
* int param_3 = myCircularQueueFront(obj);
* int param_4 = myCircularQueueRear(obj);
* bool param_5 = myCircularQueueIsEmpty(obj);
* bool param_6 = myCircularQueueIsFull(obj);
* myCircularQueueFree(obj);
*/
有问题可以及时与博主交流,25小时高强度在线