📅  最后修改于: 2023-12-03 15:25:37.711000             🧑  作者: Mango
循环队列是一种基于数组实现的队列,与普通队列不同的是,在队列为空时,队头指针与队尾指针相等;在队列满时,队尾指针回到数组起始位置(0),队头指针则继续向后移动。这种队列可以通过模运算实现。
本文介绍的是循环队列的第 2 组实现方式,即基于循环链表的实现。循环链表是一种特殊的链表,在普通链表的基础上,将最后一个节点指向了头节点,从而形成了环形结构。
首先,我们需要定义一个结构体,表示循环队列。该结构体包含两个指向节点的指针,分别指向队头和队尾,以及一个队列的长度。
typedef struct {
QueueNode *front; // 队头指针
QueueNode *rear; // 队尾指针
int length; // 队列长度
} CircularQueue;
接下来,我们需要定义一个节点结构体,表示链表中的一个节点。该节点包含两个属性:一个整数值(表示实际存储的数据)和一个指向下一个节点的指针。
typedef struct QueueNode {
int data; // 数据
struct QueueNode *next; // 指向下一个节点的指针
} QueueNode;
在使用循环队列之前,需要先对其进行初始化。初始化过程需要创建一个头节点,并将队头和队尾指针指向该节点。
void init(CircularQueue *queue) {
QueueNode *head = (QueueNode *) malloc(sizeof(QueueNode)); // 创建头节点
head->next = head; // 将头节点的next指针指向自己,形成环形结构
queue->front = queue->rear = head; // 初始化队头和队尾指针为头节点
queue->length = 0; // 队列长度置为0
}
入队操作需要新建一个节点,并将其插入到队尾节点的后面即可。
void enqueue(CircularQueue *queue, int x) {
QueueNode *new_node = (QueueNode *) malloc(sizeof(QueueNode)); // 创建新节点
new_node->data = x; // 为新节点赋值
new_node->next = queue->rear->next; // 将新节点的next指针指向头节点
queue->rear->next = new_node; // 将新节点插入到队尾节点的后面
queue->rear = new_node; // 将队尾指针指向新节点
queue->length++; // 队列长度+1
}
出队操作需要删除队头节点,并将队头指针指向下一个节点。
int dequeue(CircularQueue *queue) {
if (queue->front == queue->rear) { // 队列为空,无法出队
return -1;
}
QueueNode *del_node = queue->front->next; // 获取队头节点
int value = del_node->data; // 保存队头节点的data值
queue->front->next = del_node->next; // 将队头节点的下一个节点作为新的队头节点
if (queue->rear == del_node) { // 如果被删节点是队列中的唯一节点,更新队尾指针
queue->rear = queue->front;
}
free(del_node); // 释放被删除节点的内存
queue->length--; // 队列长度-1
return value;
}
获取队头元素的操作比较简单,直接返回队头节点的下一个节点的data值即可。
int getFront(CircularQueue *queue) {
if (queue->front == queue->rear) { // 队列为空,无法获取队头元素
return -1;
}
return queue->front->next->data; // 获取队头节点的下一个节点的data值
}
获取队列长度也比较简单,直接返回队列结构体中的长度属性即可。
int getLength(CircularQueue *queue) {
return queue->length;
}
循环队列是一种高效的数据结构,常用于实现缓冲池、任务队列等场景。该文章介绍了循环队列的第 2 组实现方式,即基于循环链表的实现。相比于数组实现,循环链表实现更加灵活和高效,因此在一些场合下更为适用。