本文介绍循环队列数据结构,最后我们用C代码进行实现。循环队列是正常队列的扩展,其最后元素连接第一个元素,这样形成类似环的数据结构。循环队列的应用包括CPU调度、内存管理以及流量管控等。
循环队列定义
循环队列主要为解决正常队列的限制。正常队列在执行插入和删除动作之后,会产生不可用的空间。
|
|
| :----------------------------------------------------------: |
| 正常队列信息 |
这里索引 0 和 1 位置只有重置队列(删除所有元素)才能使用,因此实际能使用的空间比队列容量小。因为正常队列增加元素是会根据尾指针位置判断是否已满,因为尾指针指向最后,所以不能增加新的元素,但实际对头还有空闲空间。
循环队列就是为了解决正常队列限制,其最后元素连接第一个元素,这样形成类似环的数据结构。
|
|
| :----------------------------------------------------------: |
| 循环队列图示 |
循环队列的工作过程是循环递增的,也就是说,当递增指针到达队列的末尾时,即从队列的开头重新开始。循环递增是通过对队列大小进行取模来实现。
if REAR + 1 == 5 (overflow!), REAR = (REAR + 1)%5 = 0 (start of queue)
循环队列操作
循环队列主要有下面约定:
- 两个指针
front
和rear
front
跟踪队列首元素rear
跟踪队尾元素- 初始设置
front
和rear
值为-1
入队操作
- 检查队列是否已满
- 如果是第一个元素,设置
front
为 0 - 循环递增
rear
(如果到达结尾,下面位置为队首) - 增加新的元素在
rear
位置
出队操作
- 检查队列是否为空
- 返回
front
位置元素 - 循环递增
front
值 - 如果是最后一个元素,重新设置
front
和rear
值为-1
但检查队列已满的条件有两种情况:
FRONT = 0 && REAR == SIZE - 1
FRONT = REAR + 1
第一种情况是增加元素,直到占满空间。第二种情况先出队,然后继续入队。如下图所示:
|
|
| :----------------------------------------------------------: |
| 循环队列入队和出队操作 |
数据结构采用不同语言差异不大,这里给出C代码实现如下:
#define SIZE 5
#include<stdio.h>
int items[SIZE];
int front = -1, rear = -1;
// Check if the queue is full
int isFull() {
if ((front == rear + 1) || (front == 0 && rear == SIZE - 1)) return 1;
return 0;
}
// Check if the queue is empty
int isEmpty() {
if (front == -1) return 1;
return 0;
}
// Adding an element
void enQueue(int element) {
if (isFull())
printf("\n Queue is full!! \n");
else {
if (front == -1) front = 0;
rear = (rear + 1) % SIZE;
items[rear] = element;
printf("\n Inserted -> %d", element);
}
}
// Removing an element
int deQueue() {
int element;
if (isEmpty()) {
printf("\n Queue is empty !! \n");
return (-1);
} else {
element = items[front];
if (front == rear) {
front = -1;
rear = -1;
}
// Q has only one element, so we reset the
// queue after dequeing it. ?
else {
front = (front + 1) % SIZE;
}
printf("\n Deleted element -> %d \n", element);
return (element);
}
}
// Display the queue
void display() {
int i;
if (isEmpty())
printf(" \n Empty Queue\n");
else {
printf("\n Front -> %d ", front);
printf("\n Items -> ");
for (i = front; i != rear; i = (i + 1) % SIZE) {
printf("%d ", items[i]);
}
printf("%d ", items[i]);
printf("\n Rear -> %d \n", rear);
}
}
int main() {
// Fails because front = -1
deQueue();
enQueue(1);
enQueue(2);
enQueue(3);
enQueue(4);
enQueue(5);
// Fails to enqueue because front == 0 && rear == SIZE - 1
enQueue(6);
display();
deQueue();
display();
enQueue(7);
display();
// Fails to enqueue because front == rear + 1
enQueue(8);
return 0;
}
总结
我们采用数组实现,因此循环队列的入队和出队操作时间复杂度为O(1) 。参考文档:https://www.programiz.com/dsa/circular-queue