队列(Queue)
一、基本概念
1.1 定义
队列是只允许在一端进行插入(队尾),在另一端进行删除(队头)的线性表。
-
队尾(Rear):允许插入的一端
-
队头(Front):允许删除的一端
特点:先进先出(FIFO - First In First Out)
1.2 主要用途
-
数据缓冲:解决生产者和消费者之间的速度不匹配问题
-
任务调度:操作系统中的进程调度
-
广度优先搜索:图算法中的实现基础
二、顺序队列(循环队列)
2.1 基本结构
typedef int DATATYPE;
typedef struct queue {
DATATYPE *ptr; // 指向队列数组的指针
int tlen; // 队列总容量
int head; // 队头指针
int tail; // 队尾指针
} SeqQueue;
2.2 关键问题与解决方案
-
问题:顺序队列中head和tail一直递增会导致越界
-
解决方案:循环队列(模运算)
head = (head + 1) % size;
tail = (tail + 1) % size;
2.3 队空与队满的判断
-
队空条件:head == tail
-
队满条件:(tail + 1) % size == head
说明:牺牲一个存储单元来区分队空和队满
2.4 基本操作接口
// 创建队列
SeqQueue *CreateSeqQueue(int len);
// 销毁队列
int DestroySeqQueue(SeqQueue *queue);
// 入队
int EnterSeqQueue(SeqQueue *queue, DATATYPE data);
// 出队
DATATYPE QuitSeqQueue(SeqQueue *queue);
// 判断队空
int IsEmptySeqQueue(SeqQueue *queue);
// 判断队满
int IsFullSeqQueue(SeqQueue *queue);
三、链式队列
3.1 数据结构定义
数据节点结构
typedef struct person {
char name[32];
char sex;
int age;
int score;
} DATATYPE;
typedef struct quenode {
DATATYPE data;
struct quenode *next;
} LinkQueNode;
队列管理结构
typedef struct {
LinkQueNode *head; // 队头指针
LinkQueNode *tail; // 队尾指针
int clen; // 队列当前长度
} LinkQue;
3.2 链式队列特点
-
无需预先分配固定空间
-
不会出现假溢出
-
动态扩展,更加灵活
-
需要额外的指针存储空间
3.3 基本操作接口
// 创建队列
LinkQue *CreateLinkQue();
// 入队
int EnterLinkQue(LinkQue *lq, DATATYPE *newnode);
// 出队
int QuitLinkQue(LinkQue *lq);
// 获取队头元素
DATATYPE *GetHeadLinkQue(LinkQue *lq);
// 获取队列大小
int GetSizeLinkQue(LinkQue *lq);
// 判断队空
int IsEmptyLinkQue(LinkQue *lq);
// 销毁队列
int DestroyLinkQue(LinkQue *lq);
四、两种队列对比
|-------|----------|--------|
| 特性 | 顺序队列(循环) | 链式队列 |
| 存储方式 | 连续内存数组 | 离散内存节点 |
| 空间限制 | 固定大小 | 动态扩展 |
| 溢出问题 | 可能假溢出 | 无假溢出 |
| 实现复杂度 | 简单 | 较复杂 |
| 适用场景 | 数据量固定/已知 | 数据量变化大 |
| 内存使用 | 效率高 | 有指针开销 |
五、应用场景总结
缓冲区管理
-
网络数据包缓冲
-
打印机任务队列
-
消息队列系统
算法实现
-
广度优先搜索(BFS)
-
缓存淘汰算法(FIFO策略)
系统调度
-
CPU进程调度
-
磁盘I/O请求调度
数据流处理
-
实时数据流缓冲
-
生产者-消费者模式
六、重要注意事项
-
循环队列的空满判断必须使用(tail+1)%size == head判断队满
-
链式队列销毁需要遍历释放所有节点内存
-
线程安全:在多线程环境下使用队列需要考虑同步机制
-
边界处理:所有操作都要检查队列为空/满的情况
七、性能考虑
-
时间复杂度:两种队列的入队、出队操作都是O(1)
-
空间效率:顺序队列更优,链式队列有指针开销
-
缓存友好性:顺序队列的连续内存访问更适合CPU缓存