队列
队列(Queue)——像排队一样简单
什么是队列?
队列是一种先进先出(FIFO,First In First Out)的数据结构。就像我们在生活中排队买票一样,先来的人先买到票,后来的人排在队尾。
在计算机中,队列的应用非常广泛:
- 操作系统的任务调度
- 打印机任务排队
- 消息队列
- 广度优先搜索(BFS)
队列的基本操作
一个标准的队列通常支持以下操作:
| 操作 | 说明 |
|---|---|
initQueue() |
初始化队列 |
enQueue() |
入队,在队尾添加元素 |
deQueue() |
出队,移除队首元素 |
front() |
获取队首元素 |
rear() |
获取队尾元素 |
isEmpty() |
判断队列是否为空 |
isFull() |
判断队列是否已满 |
队列的两种实现方式
1. 顺序队列(数组实现)
顺序队列使用数组来存储数据,需要两个指针:front 指向队首,rear 指向队尾。
基本代码实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_SIZE 100
typedef struct {
int data[MAX_SIZE];
int front; // 队首索引
int rear; // 队尾索引
} Queue;
// 初始化队列
void initQueue(Queue *q) {
q->front = 0;
q->rear = 0;
}
// 判断队列是否为空
bool isEmpty(Queue *q) {
return q->front == q->rear;
}
// 判断队列是否已满
bool isFull(Queue *q) {
return q->rear == MAX_SIZE;
}
// 入队操作
bool enQueue(Queue *q, int value) {
if (isFull(q)) {
printf("队列已满,无法入队!\n");
return false;
}
q->data[q->rear] = value;
q->rear++;
return true;
}
// 出队操作
bool deQueue(Queue *q) {
if (isEmpty(q)) {
printf("队列为空,无法出队!\n");
return false;
}
q->front++;
return true;
}
// 获取队首元素
int getFront(Queue *q) {
if (isEmpty(q)) {
printf("队列为空!\n");
return -1;
}
return q->data[q->front];
}
// 获取队尾元素
int getRear(Queue *q) {
if (isEmpty(q)) {
printf("队列为空!\n");
return -1;
}
return q->data[q->rear - 1];
}
// 打印队列所有元素
void printQueue(Queue *q) {
if (isEmpty(q)) {
printf("队列为空\n");
return;
}
printf("队列元素:");
for (int i = q->front; i < q->rear; i++) {
printf("%d ", q->data[i]);
}
printf("\n");
}
// 测试代码
int main() {
Queue q;
initQueue(&q);
// 入队
enQueue(&q, 10);
enQueue(&q, 20);
enQueue(&q, 30);
printQueue(&q); // 输出:10 20 30
// 获取队首元素
printf("队首元素:%d\n", getFront(&q)); // 输出:10
// 出队
deQueue(&q);
printQueue(&q); // 输出:20 30
// 获取队尾元素
printf("队尾元素:%d\n", getRear(&q)); // 输出:30
return 0;
}
顺序队列的问题:假溢出
上面的实现有一个问题:当 rear 到达数组末尾时,即使队列前面还有空闲位置,也无法再入队了。这就是假溢出现象。
解决方案:使用循环队列。
2. 循环队列(数组实现)
循环队列将数组看作一个环,当 rear 到达数组末尾时,如果数组前面有空位,就绕回到数组开头。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_SIZE 100
typedef struct {
int data[MAX_SIZE];
int front;
int rear;
} CirQueue;
// 初始化循环队列
void initCirQueue(CirQueue *q) {
q->front = 0;
q->rear = 0;
}
// 判断循环队列是否为空
bool isEmpty(CirQueue *q) {
return q->front == q->rear;
}
// 判断循环队列是否已满
bool isFull(CirQueue *q) {
return (q->rear + 1) % MAX_SIZE == q->front;
}
// 入队操作
bool enCirQueue(CirQueue *q, int value) {
if (isFull(q)) {
printf("队列已满,无法入队!\n");
return false;
}
q->data[q->rear] = value;
q->rear = (q->rear + 1) % MAX_SIZE; // 循环移动
return true;
}
// 出队操作
bool deCirQueue(CirQueue *q) {
if (isEmpty(q)) {
printf("队列为空,无法出队!\n");
return false;
}
q->front = (q->front + 1) % MAX_SIZE; // 循环移动
return true;
}
// 获取队首元素
int getFront(CirQueue *q) {
if (isEmpty(q)) {
printf("队列为空!\n");
return -1;
}
return q->data[q->front];
}
// 打印循环队列
void printCirQueue(CirQueue *q) {
if (isEmpty(q)) {
printf("队列为空\n");
return;
}
printf("队列元素:");
int i = q->front;
while (i != q->rear) {
printf("%d ", q->data[i]);
i = (i + 1) % MAX_SIZE;
}
printf("\n");
}
// 测试代码
int main() {
CirQueue q;
initCirQueue(&q);
// 入队
enCirQueue(&q, 1);
enCirQueue(&q, 2);
enCirQueue(&q, 3);
printCirQueue(&q); // 输出:1 2 3
// 出队
deCirQueue(&q);
printCirQueue(&q); // 输出:2 3
// 继续入队
enCirQueue(&q, 4);
enCirQueue(&q, 5);
printCirQueue(&q); // 输出:2 3 4 5
return 0;
}
3. 链式队列(链表实现)
链式队列使用链表存储数据,不受数组大小的限制,可以动态扩展。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 节点结构
typedef struct Node {
int data;
struct Node *next;
} Node;
// 队列结构
typedef struct {
Node *front; // 队首指针
Node *rear; // 队尾指针
} LinkQueue;
// 初始化链式队列
void initLinkQueue(LinkQueue *q) {
q->front = NULL;
q->rear = NULL;
}
// 判断链式队列是否为空
bool isEmpty(LinkQueue *q) {
return q->front == NULL;
}
// 入队操作
void enLinkQueue(LinkQueue *q, int value) {
// 创建新节点
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
printf("内存分配失败!\n");
return;
}
newNode->data = value;
newNode->next = NULL;
// 如果队列为空
if (isEmpty(q)) {
q->front = newNode;
q->rear = newNode;
} else {
q->rear->next = newNode;
q->rear = newNode;
}
printf("%d 入队成功\n", value);
}
// 出队操作
bool deLinkQueue(LinkQueue *q) {
if (isEmpty(q)) {
printf("队列为空,无法出队!\n");
return false;
}
Node *temp = q->front;
int value = temp->data;
q->front = q->front->next;
// 如果队列只有一个节点
if (q->front == NULL) {
q->rear = NULL;
}
free(temp);
printf("%d 出队成功\n", value);
return true;
}
// 获取队首元素
int getFront(LinkQueue *q) {
if (isEmpty(q)) {
printf("队列为空!\n");
return -1;
}
return q->front->data;
}
// 打印链式队列
void printLinkQueue(LinkQueue *q) {
if (isEmpty(q)) {
printf("队列为空\n");
return;
}
printf("队列元素:");
Node *cur = q->front;
while (cur != NULL) {
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
// 释放队列内存
void freeQueue(LinkQueue *q) {
Node *cur = q->front;
while (cur != NULL) {
Node *temp = cur;
cur = cur->next;
free(temp);
}
q->front = NULL;
q->rear = NULL;
}
// 测试代码
int main() {
LinkQueue q;
initLinkQueue(&q);
enLinkQueue(&q, 100);
enLinkQueue(&q, 200);
enLinkQueue(&q, 300);
printLinkQueue(&q); // 输出:100 200 300
printf("队首元素:%d\n", getFront(&q)); // 输出:100
deLinkQueue(&q);
printLinkQueue(&q); // 输出:200 300
// 释放内存
freeQueue(&q);
return 0;
}
三种实现方式对比
| 特性 | 顺序队列 | 循环队列 | 链式队列 |
|---|---|---|---|
| 实现难度 | 简单 | 中等 | 中等 |
| 空间利用 | 有假溢出问题 | 空间利用率高 | 动态扩展,无浪费 |
| 性能 | 入出队 O(1) | 入出队 O(1) | 入出队 O(1) |
| 内存限制 | 固定大小 | 固定大小 | 动态分配 |
| 适用场景 | 数据量小且固定 | 数据量固定 | 数据量不确定 |
队列的常见应用场景
1. 生产者-消费者模型
// 简单的生产者-消费者模型示例
#include <stdio.h>
#include <unistd.h> // sleep函数
#define BUFFER_SIZE 5
// 假设这里是共享队列
// 生产者不断产生数据入队
// 消费者不断从队列取出数据处理
void producer(Queue *q) {
int item = 0;
while (1) {
if (!isFull(q)) {
enQueue(q, item++);
printf("生产者生产了:%d\n", item);
}
sleep(1);
}
}
void consumer(Queue *q) {
while (1) {
if (!isEmpty(q)) {
int item = getFront(q);
deQueue(q);
printf("消费者消费了:%d\n", item);
}
sleep(1);
}
}
2. 广度优先搜索(BFS)
在图的遍历中,队列是BFS的核心数据结构:
// BFS伪代码
void BFS(Graph *g, int start) {
Queue q;
initQueue(&q);
visited[start] = true;
enQueue(&q, start);
while (!isEmpty(&q)) {
int v = getFront(&q);
deQueue(&q);
printf("访问节点:%d\n", v);
for (每个与v相邻的节点w) {
if (!visited[w]) {
visited[w] = true;
enQueue(&q, w);
}
}
}
}
总结
- 队列的核心思想:先进先出(FIFO)
- 两种实现方式:
- 数组实现:简单高效,但有大小限制
- 链表实现:动态扩展,更灵活
- 循环队列:解决了顺序队列的假溢出问题
- 应用广泛:从操作系统到算法设计都能看到队列的身影
掌握队列这个基础数据结构,对学习更复杂的数据结构和算法非常有帮助。希望这篇文章能帮助你理解队列的原理和实现!
浙公网安备 33010602011771号