目录
设计循环队列
题目链接:https://leetcode.cn/problems/design-circular-queue/description/
思路:假设一个队列里面从队头到队尾分别为1 2 3 4,若删除数据,则会在队头位置删除,假设队列已经排满,则队头位置我们记作front,队尾位置我们记为rear,则我们出队列的之前,front和rear在同一个位置,即在1的位置,出数据的时候,front++,而rear不变。如果我们用链表来实现这个队列的话我们需要设计一个单向循环链表,这个方法比较麻烦,所以我们用顺序表来实现这个队列,有:循环队列为空,front=rear=0,在下标为0的位置进行插入数据,rear++,直至存储满。但是,这个时候出现了一个问题:front=rear=0,如何判断这个队列到底是满了还是空的呢?我们知道,顺序表在实现的时候有个所能存储元素的最大为capacity,这个capacity为最大下标+1,我们在实现顺序表之前需要申请capacity个空间,如果在这上面下功夫,比如我们可以申请capacity+1个空间,剩下一个空间不存储任何数据,而是和之前说的头结点一样成为一个哨兵位,但是这个哨兵位不一定都是下标为capacity位置,可能时刻发生变动,所以当(rear+1)%(capacity+1)==front则空间满了。若front==rear则队列为空。所以有:(rear位置没有存储任何数据)
1.循环队列的创建
typedef struct
{
int* arr;
int front;//队头
int rear;//队尾
int capacity;//循环队列的空间大小
}MyCircularQueue;
2.队列的初始化
和顺序表初始化差不多,有:
//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
pq->arr = (int*)(sizeof(int) * (k + 1));//申请k+1个空间
pq->front = pq->rear = 0;
pq->capacity = k;
return pq;
}
3.队列判空
直接返回obj->front=obj->rear;即可:
//循环队列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front == obj->rear;
}
4.队列判满
//循环队列判满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->rear + 1) % (obj->capacity + 1) == obj->front;
}
5.向循环队列插入一个元素
先需要判断循环队列是否满了,然后再进行数据的插入,需要先++,再进行取模操作,有:
//向循环队列中插入一个元素
bool myCircularQueueEnQueue(MyCircularQueue* obj,int value)
{
if (myCircularQueueIsFull(obj))
{
return false;
}
obj->arr[obj->rear++] = value;
obj->rear %= obj->capacity + 1;
return true;
}
6.出队列
需要先进行判空操作,然后只需要把front++,再和队列最大的下标进行取模操作即可:
//出队列
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return false;
}
++obj->front;
obj->front %= obj->capacity + 1;
return true;
}
7.取队头
若为空返回-1,反正返回队头,有:
//取队头
int myCircularQueueFront(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->arr[obj->front];
}
8.取队尾
由于队尾可能在那个空位置,也有可能在空间没有满的时候,下标为0的位置,所以我们应该分情况讨论,如果队尾在下标为0的位置我们需要返回的是下标为capacity位置;如果是在其他位置则需要返回的是下标为rear-1位置的数据,有:
//取队尾
int myCircularQueueRear(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
int prev = obj->rear - 1;
if (obj->rear == 0)
{
prev = obj->capacity;
}
return obj->arr[prev];
}
9.销毁队列
我们需要先进行判断是否数组申请了空间,才能进行释放操作,有:
//销毁队列
void myCircularQueueFree(MyCircularQueue* obj)
{
if (obj->arr)
{
free(obj->arr);
}
free(obj);
obj = NULL;
}
10.最终代码
//循环队列的创建
typedef struct
{
int* arr;
int front;//队头
int rear;//队尾
int capacity;//循环队列的空间大小
}MyCircularQueue;
//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
pq->arr = (int*)malloc(sizeof(int) * (k + 1));
pq->front = pq->rear = 0;
pq->capacity = k;
return pq;
}
//循环队列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front == obj->rear;
}
//循环队列判满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->rear + 1) % (obj->capacity + 1) == obj->front;
}
//向循环队列中插入一个元素
bool myCircularQueueEnQueue(MyCircularQueue* obj,int value)
{
if (myCircularQueueIsFull(obj))
{
return false;
}
obj->arr[obj->rear++] = value;
obj->rear %= obj->capacity + 1;
return true;
}
//出队列
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if (myCircularQueueIsEmpty(obj))
{
return false;
}
++obj->front;
obj->front %= obj->capacity + 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;
}
int prev = obj->rear - 1;
if (obj->rear == 0)
{
prev = obj->capacity;
}
return obj->arr[prev];
}
//销毁队列
void myCircularQueueFree(MyCircularQueue* obj)
{
if (obj->arr)
{
free(obj->arr);
}
free(obj);
obj = NULL;
}
总结:栈和队列比较简单,但是仍然需要我们花时间去学习更多的知识来完成这些事情。下一讲将讲解二叉树_堆,下讲再见。喜欢的可以一键三连哦。