数据结构 -- 队列
队列的核心定义
队列是受限线性表,仅允许在一端(队尾)插入元素、另一端(队头)删除元素,遵循 “先进先出(FIFO,First In First Out)” 原则。
队列的结构与操作端
- 队尾(Rear):允许执行插入操作(入队,Enqueue )的一端。
- 队头(Front):允许执行删除操作(出队,Dequeue )的一端。
队列的实现与分类
- 顺序队列:基于数组实现,需处理 “假溢出” 问题(队尾满但队头有空闲 )。
- 循环队列:对顺序队列的优化,将数组逻辑处理为环形,通过队头、队尾指针模运算复用空间,解决假溢出。
队列的核心操作
- 入队(Enqueue):在队尾添加元素,若队列未满则执行,队尾指针后移。
- 出队(Dequeue):移除并返回队头元素,若队列非空则执行,队头指针后移。
队列的应用
队列的 “先进先出” 特性,核心解决 “速度不匹配” 场景:如网络通信中,收发数据速率不同(发送快、接收慢 ),用队列暂存数据做缓冲;或操作系统中,多个进程请求同一资源(如打印机 ),队列保证请求按顺序处理。简单说,队列就是用 “先进先出” 规则,解决 **“速度不匹配、需有序处理”** 问题的线性表,分顺序队列、循环队列
队列的操作函数
#include "seqque.h" // 包含顺序队列的头文件,定义了SeqQue结构体和DATATYPE类型
#include <stdio.h> // 标准输入输出库
#include <stdlib.h> // 标准库,包含malloc、free等内存管理函数
#include <string.h> // 字符串处理库,包含memcpy等内存拷贝函数/*** 创建一个顺序队列(循环队列)* @param len 队列的最大容量(可存储的元素个数)* @return 成功返回创建的队列指针,失败返回NULL*/
SeqQue* CreateSeqQue(int len)
{// 为队列控制结构体分配内存SeqQue* sq = malloc(sizeof(SeqQue));if (NULL == sq) // 内存分配失败检查{perror("CreateSeqQue malloc"); // 打印错误信息return NULL;}// 为队列的数组缓冲区分配内存,可存储len个DATATYPE类型元素sq->array = malloc(sizeof(DATATYPE) * len);if (NULL == sq->array) // 内存分配失败检查{perror("CreateSeqQue malloc2"); // 打印错误信息free(sq); // 释放已分配的队列结构体内存,防止内存泄漏return NULL;}// 初始化队列指针:头指针和尾指针都从0开始sq->head = 0;sq->tail = 0;sq->tlen = len; // 记录队列的总容量return sq; // 返回创建成功的队列指针
}/*** 入队操作:向队列尾部添加元素* @param sq 队列指针* @param data 要入队的数据指针* @return 0表示成功,1表示失败(队列已满)*/
int EnterSeqQue(SeqQue* sq, DATATYPE* data)
{// 检查队列是否已满if (IsFullSeqQue(sq)){printf("queue is full\n"); // 提示队列已满return 1; // 返回失败状态}// 将数据拷贝到队尾位置(tail指向的位置)memcpy(&sq->array[sq->tail], data, sizeof(DATATYPE));// 尾指针后移,使用取模运算实现循环(当到达数组末尾时回到开头)sq->tail = (sq->tail + 1) % sq->tlen;return 0; // 返回成功状态
}/*** 出队操作:移除队列头部的元素* @param sq 队列指针* @return 0表示成功,1表示失败(队列已空)*/
int QuitSeqQue(SeqQue *sq)
{// 检查队列是否为空if (IsEmptySeqQue(sq)){printf("queue is empty\n"); // 提示队列已空return 1; // 返回失败状态}// 头指针后移,使用取模运算实现循环(逻辑上移除队头元素)sq->head = (sq->head + 1) % sq->tlen;return 0; // 返回成功状态
}/*** 获取队头元素(不移除元素)* @param sq 队列指针* @return 队头元素的指针,若队列为空则返回的指针无效*/
DATATYPE* GetHeadSeqQue(SeqQue* sq)
{// 直接返回头指针指向的元素地址return &sq->array[sq->head];
}/*** 判断队列是否为空* @param sq 队列指针* @return 1表示为空,0表示非空*/
int IsEmptySeqQue(SeqQue* sq)
{// 当头指针和尾指针指向同一位置时,队列为空return sq->head == sq->tail;
}/*** 判断队列是否已满* @param sq 队列指针* @return 1表示已满,0表示未满*/
int IsFullSeqQue(SeqQue* sq)
{// 尾指针的下一个位置等于头指针时,队列已满// 这种判断方式会浪费一个元素的空间,用于区分空队列和满队列return (sq->tail + 1) % sq->tlen == sq->head;
}/*** 销毁队列,释放占用的内存* @param sq 队列指针* @return 0表示成功*/
int DestroySeqQue(SeqQue* sq)
{free(sq->array); // 释放数组缓冲区内存free(sq); // 释放队列结构体内存return 0;
}
代码核心说明:
数据结构设计:
- 采用循环队列(环形缓冲区)的设计,通过数组实现
- 使用
head
(头指针)和tail
(尾指针)标识队列的边界 - 牺牲一个元素的存储空间,用于区分队列空和满的状态
关键特性:
- 队列的实际可用容量为
tlen - 1
(因为要留一个位置区分空满) - 通过取模运算
% sq->tlen
实现指针的循环移动,避免数组越界 - 所有操作的时间复杂度均为 O (1),效率很高
- 队列的实际可用容量为
注意事项:
- 代码中
SeqQue
结构体和DATATYPE
类型的定义在seqque.h
头文件中 - 入队和出队操作只移动指针,不实际删除数据(覆盖式更新)
- 销毁队列时需要先释放数组内存,再释放结构体内存,避免内存泄漏
- 代码中