目录
循环队列的概念
循环队列是队列的顺序表示和实现,采用循环链表是为了解决顺序队列中的溢出现象。和顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队头到队尾的元素之外,还需要设两个变量front,rear分别表示队头元素和队尾元素的位置。
当循环队列为空时,front =rear =0,每当插入一个数据时,尾指针加1;每当删除队头数据时,头指针加1。所以,在非空队列中,头指针front始终指向队头元素,尾指针rear始终指向队尾元素的下一个位置。
假设队列分配的最大空间为6,但是我们只用5个空间存储数据。
(1)空队列
(2)插入一个数据,rear+1,rear将指向队尾元素的下一个
(3)插入足够数据,使队列为满,rear指向下标为5的位置
(4)删除数据,front+1,front始终指向队头元素
(5)再次插入1个数据,此时rear已经指向数组的最后位置,由于我们使用的是循环队列,rear应该移动至下标为0处。【rear=(rear+1)%6】
(6)再次插入一个数据,队列再次为满
通过(3)(6)可以看出,循环队列为满时满足rear的下一位置为front,即(rear+1)%(k+1)== front
代码实现
1.结构体的定义
typedef struct {
int* data; //用于队列存放数据
int front; //队头指针
int rear;//队尾指针
int k;//队列空间大小
} MyCircularQueue;
2.函数功能实现
(1)MyCircularQueue* myCircularQueueCreate(int k); //初始化
(2)bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断是否为空
(3)bool myCircularQueueIsFull(MyCircularQueue* obj)//判断是否为满
(4)bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);//入队
(5)bool myCircularQueueDeQueue(MyCircularQueue* obj);//出队
(6)int myCircularQueueFront(MyCircularQueue* obj);//获取队头元素
(7)int myCircularQueueRear(MyCircularQueue* obj);//获取队尾元素
(8)void myCircularQueueFree(MyCircularQueue* obj);//销毁
2.1初始化循环队列并返回队列指针
其中k是循环队列容量
MyCircularQueue* myCircularQueueCreate(int k) {
int i = 0;
MyCircularQueue* obj;
obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->front = 0;
obj->rear = 0;
obj->k = k;
obj->data = (int*)malloc(sizeof(int) * (obj->k + 1));//队列长为k,开辟k+1个空间
return obj;
}
2.2判断队列是否为空
当队列为空时,front == rear
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return obj->front == obj->rear;
}
2.3判断队列是否为满
根据之前的分析,队列为满要考虑两种情况,条件是**(rear+1)%(k)=front**
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
return (obj->rear + 1) % (obj->k) == obj->front;
}
2.4入队
入队时首先要判断循环队列是否为满,满的话不能再执行入队操作了。若不为满,由于rear指向队尾的下一位置,所以先在rear处插入数据,再移动rear。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj))
{
return false;
}
else
{
obj->data[obj->rear] = value;
obj->rear++;
obj->rear %= (obj->k + 1);
return true;
}
}
2.5出队
执行出队操作时首先要判断循环队列是否为空,空的话不能再执行该操作了。若不为空,不用删除队头数据,直接移动front即可,因为数组的有效范围是front到rear间的数据。
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return false;
}
else
{
obj->front++;
obj->front %= (obj->k + 1);
return true;
}
}
2.6获取队头元素
front所指位置就是队头元素,所以直接返回数组中下标为front的值就可以了,前提是循环队列不为空。
int myCircularQueueFront(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
return obj->data[obj->front];
}
}
2.7获取队尾元素
rear指向队尾元素的下一位置,所以逻辑上下标为rear-1的数据便是队尾元素。但是该队列是循环队列,所以rear可能指向下标为0处,所以应该先给rear+k,再减去1,便指向了队尾元素。但rear还可能指向其他位置,为了避免出错,给rear+k-1对k取余即可。
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
return obj->data[obj->rear + obj->k] % (obj->k + 1);
}
}
2.8销毁
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->data);
free(obj);
}
以上就是我对循环队列的理解,如有错误希望大家能够指正~