目录
一、【队列】
1.1 队列的定义
队列是一种先进先出的数据结构。第一个入队列的元素将是第一个被移除的元素。可以将队列想象成一个排队的人,先到的人先得到服务。
在队列中,允许插入的一端称为队尾,而允许删除的一端称为队头。当一个元素被添加到队列时,它会被放置在队尾;而当一个元素从队列中被移除时,它会从队头被取出。
1.2 队列的基本操作
队列的基本操作包括:
-
入队(Enqueue): 将一个元素添加到队列的末尾。
-
出队(Dequeue): 移除并返回队列的头部元素。
-
查看队头(Peek/Front): 返回队列的头部元素,但不移除它。
-
查看队尾(Back): 返回队列的尾部元素,但不移除它。
-
检查队列是否为空(IsEmpty): 判断队列中是否有元素。
-
获取队列大小(Size): 返回队列中元素的数量。
-
初始化队列(Init): 初始化队列,准备使用。
-
销毁队列(Destroy): 清理队列,释放相关资源。
二、【队列的类型】
注意:本文,仅实现顺序队列的完整代码
2.1 顺序队列
2.1.1 概述
顺序队列使用数组或链表来实现队列的基本操作。通过两个指针(或索引)来跟踪队头和队尾的位置,从而进行元素的入队和出队操作。
1. 使用数组实现:
#define MAX_SIZE 100 // 队列最大容量
// 顺序队列结构
typedef struct {
int data[MAX_SIZE]; // 存储队列元素的数组
int front; // 队头索引
int rear; // 队尾索引
int size; // 当前队列大小
} ArrayQueue;
2. 使用链表实现:
// 节点结构
typedef struct Node {
int data; // 存储数据
struct Node *next; // 指向下一个节点的指针
} Node;
// 队列结构
typedef struct {
Node *front; // 队头指针
Node *rear; // 队尾指针
int size; // 当前队列大小
} Queue;
2.1.2 接口实现
顺序队列的基本操作,包括初始化、入队、出队、查看队头、查看队尾、检查队列状态、获取队列大小和销毁队列的实现。
1. 初始化队列
初始化队列,设置 front
和 rear
为 NULL,队列大小为 0。
void initQueue(SeqQueue* queue) {
queue->front = NULL;
queue->rear = NULL;
queue->size = 0;
}
2. 入队
在队列中添加元素,首先创建一个新节点。如果队列为空,则将 front
和 rear
都指向新节点;否则,将新节点添加到 rear
的后面,并更新 rear
。
bool enqueue(SeqQueue* queue, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (!newNode) {
return false; // 内存分配失败
}
newNode->data = value;
newNode->next = NULL; // 新节点的下一个节点为 NULL
if (isEmpty(queue)) {
queue->front = newNode; // 队列为空时,front 和 rear 都指向新节点
queue->rear = newNode;
} else {
queue->rear->next = newNode; // 将新节点添加到队尾
queue->rear = newNode; // 更新 rear 指针
}
queue->size++;
return true;
}
3. 出队
从队列中移除元素,首先检查队列是否为空。如果不为空,将 front
指向的节点移除,并更新 front
指针。如果队列变为空,还需要将 rear
置为 NULL。
bool dequeue(SeqQueue* queue, int* value) {
if (isEmpty(queue)) {
return false; // 队列为空
}
Node* temp = queue->front; // 保存队头节点
*value = temp->data; // 获取队头元素
queue->front = queue->front->next; // 更新 front 指针
if (queue->front == NULL) {
queue->rear = NULL; // 如果队列变为空,更新 rear 为 NULL
}
free(temp); // 释放队头节点
queue->size--;
return true;
}
4. 查看队头
查看队头元素而不移除它。
bool peekFront(SeqQueue* queue, int* value) {
if (isEmpty(queue)) {
return false; // 队列为空
}
*value = queue->front->data; // 返回队头元素
return true;
}
5. 查看队尾
查看队尾元素而不移除它。
bool peekBack(SeqQueue* queue, int* value) {
if (isEmpty(queue)) {
return false; // 队列为空
}
*value = queue->rear->data; // 返回队尾元素
return true;
}
6. 检查队列是否为空
判断队列中是否有元素。
bool isEmpty(SeqQueue* queue) {
return queue->size == 0;
}
7. 获取队列大小
返回队列中元素的数量。
int size(SeqQueue* queue) {
return queue->size;
}
8. 销毁队列
清理队列,释放相关资源。
void destroyQueue(SeqQueue* queue) {
while (!isEmpty(queue)) {
int value;
dequeue(queue, &value); // 逐个出队并释放节点
}
}
2.2 循环队列
2.2.1 概述
循环队列是一种对顺序队列的改进,通过将队列的尾部与头部连接起来,利用数组的循环特性来避免空间浪费。
1. 入队操作
在循环队列中,入队操作的步骤如下:
queue->data[queue->rear] = value; // 插入元素
queue->rear = (queue->rear + 1) % MAX_SIZE; // 更新队尾指针
- 插入元素:
queue->data[queue->rear] = value;
将新元素value
放入rear
指向的位置。 - 更新队尾指针:
queue->rear = (queue->rear + 1) % MAX_SIZE;
使用取余运算确保rear
在达到MAX_SIZE
后能够回绕到数组的开头,实现循环效果。
2. 出队操作
出队操作的步骤如下:
*value = queue->data[queue->front]; // 获取队头元素
queue->front = (queue->front + 1) % MAX_SIZE; // 更新队头指针
- 获取队头元素:
*value = queue->data[queue->front];
取出front
指向的元素。 - 更新队头指针:
queue->front = (queue->front + 1) % MAX_SIZE;
更新front
指针,以便指向下一个元素。
3. 判断队列是否满
return (queue->rear + 1) % MAX_SIZE == queue->front; // 队列满
- 在循环队列中,队列满的条件是
rear
的下一位置与front
相等,即:
(rear+1)% MAX_SIZE==front(rear+1)%MAX_SIZE==front - 这里的
MAX_SIZE
是队列的最大容量。使用取余运算可以确保rear
在数组的边界处回绕。
示例:
假设 MAX_SIZE = 5
,队列的状态如下:
队列索引 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
元素 | 1 | 2 | 3 | ||
front | 0 | ||||
rear | 1 | 2 |
- 此时
rear
是 3,front
是 0。如果rear
再增加 1,变为 4,队列仍然不满。 - 当
rear
变为 0(即(4 + 1) % 5
),此时front
仍然是 0,队列就满了。
2.3 双端队列
2.3.1 概述
双端队列允许在两端进行插入和删除操作。它可以通过数组或链表实现,提供灵活的操作方式。
1. 使用数组实现:使用一个固定大小的数组,并维护两个指针:front
(队头指针)和 rear
(队尾指针)。在实现时,需要注意数组的循环利用。
#define MAX_SIZE 100 // 定义双端队列的最大容量
typedef struct {
int data[MAX_SIZE]; // 存储双端队列元素的数组
int front; // 队头指针
int rear; // 队尾指针
} Deque;
2. 使用链表实现:使用双向链表实现双端队列,每个节点有两个指针,分别指向前一个节点和后一个节点。
typedef struct Node {
int data; // 存储数据
struct Node *next; // 指向下一个节点的指针
struct Node *prev; // 指向前一个节点的指针
} Node;
typedef struct {
Node *front; // 队头指针
Node *rear; // 队尾指针
} Deque;
2.3.2 特殊的双端队列
1.输入受限:能在一端能进行插入删除操作,另一端只能进行删除操作
2.输出受限:能在一端能进行插入删除操作,另一端只能进行插入操作
三、【完整代码】
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 节点结构
typedef struct Node {
int data; // 存储数据
struct Node *next; // 指向下一个节点的指针
} Node;
// 队列结构
typedef struct {
Node *front; // 队头指针
Node *rear; // 队尾指针
int size; // 当前队列大小
} Queue;
// 初始化队列
void init(Queue *queue) {
queue->front = NULL; // 队头指针初始化为NULL
queue->rear = NULL; // 队尾指针初始化为NULL
queue->size = 0; // 当前队列大小初始化为0
}
// 销毁队列
void destroy(Queue *queue) {
Node *current = queue->front;
while (current != NULL) {
Node *next = current->next; // 保存下一个节点
free(current); // 释放当前节点
current = next; // 移动到下一个节点
}
queue->front = NULL; // 清空队头指针
queue->rear = NULL; // 清空队尾指针
queue->size = 0; // 重置队列大小
}
// 入队(Enqueue)
bool enqueue(Queue *queue, int value) {
Node *newNode = (Node *)malloc(sizeof(Node)); // 创建新节点
if (newNode == NULL) {
return false; // 内存分配失败
}
newNode->data = value; // 设置节点数据
newNode->next = NULL; // 新节点的下一个指针设为NULL
if (queue->rear != NULL) {
queue->rear->next = newNode; // 如果队列不为空,将新节点添加到队尾
} else {
queue->front = newNode; // 如果队列为空,新节点是队头
}
queue->rear = newNode; // 更新队尾指针
queue->size++; // 更新队列大小
return true;
}
// 出队(Dequeue)
bool dequeue(Queue *queue, int *value) {
if (queue->front == NULL) {
return false; // 队列为空,无法出队
}
Node *nodeToDelete = queue->front; // 获取队头节点
*value = nodeToDelete->data; // 返回队头数据
queue->front = queue->front->next; // 更新队头指针
if (queue->front == NULL) {
queue->rear = NULL; // 如果队列为空,队尾也设为NULL
}
free(nodeToDelete); // 释放队头节点
queue->size--; // 更新队列大小
return true;
}
// 查看队头(Peek/Front)
bool peekFront(Queue *queue, int *value) {
if (queue->front == NULL) {
return false; // 队列为空,无法查看队头
}
*value = queue->front->data; // 返回队头元素
return true;
}
// 查看队尾(Back)
bool peekBack(Queue *queue, int *value) {
if (queue->rear == NULL) {
return false; // 队列为空,无法查看队尾
}
*value = queue->rear->data; // 返回队尾元素
return true;
}
// 检查队列是否为空(IsEmpty)
bool isEmpty(Queue *queue) {
return queue->size == 0; // 如果大小为0,则队列为空
}
// 获取队列大小(Size)
int size(Queue *queue) {
return queue->size; // 返回当前队列大小
}
// 测试链表实现的队列
int main() {
Queue queue;
init(&queue); // 初始化队列
// 入队操作
enqueue(&queue, 10);
enqueue(&queue, 20);
enqueue(&queue, 30);
// 查看队头和队尾
int value;
if (peekFront(&queue, &value)) {
printf("队头: %d\n", value);
}
if (peekBack(&queue, &value)) {
printf("队尾: %d\n", value);
}
// 出队操作
while (dequeue(&queue, &value)) {
printf("出队: %d\n", value);
}
// 检查队列是否为空
printf("队列是否为空: %s\n", isEmpty(&queue) ? 1 : 0);
// 销毁队列
destroy(&queue);
return 0;
}