当前位置: 首页 > news >正文

数据结构篇(六):线性表——队列

前言

上一篇讲了栈——后进先出的结构。这一篇讲队列,和栈刚好相反,队列是先进先出的结构。栈用数组实现是最优选择,而队列则正好相反:链表实现是更常用、更优的选择。本文将讲清楚队列的原理、为什么队列更适合用链表实现,以及完整的代码实现。


一、什么是队列

队列(Queue)是一种只允许在一端插入数据,在另一端删除数据的线性表。

  • 插入数据的一端叫队尾(tail / rear),这个操作叫入队(push / enqueue)
  • 删除数据的一端叫队头(head / front),这个操作叫出队(pop / dequeue)

队列的核心特性是先进先出(FIFO,First In First Out):最先进队的元素,最先出队。

生活中的例子:排队买奶茶,先来的人先买到,后来的人排在后面;操作系统的任务调度队列;消息队列。


二、为什么队列更适合用链表实现

这是队列和栈很大的不同点,值得单独拎出来讲清楚。

  • 栈只在一端操作(栈顶),用数组实现时,尾插尾删都是O(1),非常合适;
  • 队列需要在两端操作:队尾入队,队头出队。如果用数组实现,队头出队意味着所有元素都要往前搬移一位,效率是O(N),非常低效(除非用循环数组或者额外维护两个下标,但实现相对复杂);
  • 而用链表实现队列,只要同时维护一个头指针和一个尾指针,头删(出队)和尾插(入队)都能做到O(1),完美契合队列的操作特点。

所以结论是:栈优先用数组实现,队列优先用链表实现


三、队列的结构定义

队列通常用单链表实现即可,因为只需要头删和尾插两种操作,不需要双向链表的能力。为了让尾插也能达到O(1)(否则每次尾插都要遍历找最后一个节点),需要额外维护一个尾指针

typedef int QDataType; // 链表节点 typedef struct QueueNode { QDataType data; struct QueueNode* next; } QueueNode; // 队列结构:维护头指针、尾指针和有效元素个数 typedef struct Queue { QueueNode* head; // 指向队头,出队在这里操作 QueueNode* tail; // 指向队尾,入队在这里操作 int size; // 有效元素个数 } Queue;

四、队列的基本操作

4.1 初始化

void QueueInit(Queue* pq) { assert(pq != NULL); pq->head = NULL; pq->tail = NULL; pq->size = 0; }

4.2 入队(Push)

新节点始终插入到tail之后,再更新tail需要单独处理队列为空的情况(此时headtail都要指向新节点)。

void QueuePush(Queue* pq, QDataType x) { assert(pq != NULL); QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode)); if (newNode == NULL) { perror("malloc fail"); exit(-1); } newNode->data = x; newNode->next = NULL; if (pq->tail == NULL) { // 队列为空,新节点既是队头也是队尾 pq->head = newNode; pq->tail = newNode; } else { pq->tail->next = newNode; pq->tail = newNode; } pq->size++; }

4.3 出队(Pop)

出队从head开始删除。同样需要处理"删除后队列变空"的情况,此时要tail也置为NULL,否则会成为野指针。

void QueuePop(Queue* pq) { assert(pq != NULL); assert(pq->head != NULL); // 队列不能为空 QueueNode* next = pq->head->next; free(pq->head); pq->head = next; // 如果删除后队列为空,tail也要置空 if (pq->head == NULL) { pq->tail = NULL; } pq->size--; }

4.4 取队头 / 队尾元素

QDataType QueueFront(Queue* pq) { assert(pq != NULL); assert(pq->head != NULL); return pq->head->data; } QDataType QueueBack(Queue* pq) { assert(pq != NULL); assert(pq->tail != NULL); return pq->tail->data; }

4.5 判空

bool QueueEmpty(Queue* pq) { assert(pq != NULL); return pq->head == NULL; }

4.6 获取有效元素个数

int QueueSize(Queue* pq) { assert(pq != NULL); return pq->size; }

4.7 销毁

​void QueueDestroy(Queue* pq) { assert(pq != NULL); QueueNode* cur = pq->head; while (cur != NULL) { QueueNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; pq->size = 0; }

五、完整测试代码

int main() { Queue q; QueueInit(&q); QueuePush(&q, 1); QueuePush(&q, 2); QueuePush(&q, 3); QueuePush(&q, 4); printf("队头: %d, 队尾: %d\n", QueueFront(&q), QueueBack(&q)); // 队头: 1, 队尾: 4 while (!QueueEmpty(&q)) { printf("%d ", QueueFront(&q)); QueuePop(&q); } printf("\n"); // 1 2 3 4,和入队顺序一致 QueueDestroy(&q); return 0; }

六、时间复杂度分析

操作时间复杂度说明
入队 pushO(1)有tail指针,无需遍历
出队 popO(1)直接操作head
取队头/队尾O(1)直接访问指针
判空O(1)判断head是否为NULL

可以看到,只要正确维护了headtail两个指针,队列的所有标准操作都能做到O(1),这也印证了为什么链表是实现队列的最佳选择。


七、队列的经典应用场景

  • 广度优先遍历(BFS):无论是树的层序遍历,还是图的广度优先遍历,都需要用队列来保存"下一层待访问的节点",这是队列最经典的应用;
  • 任务调度 / 消息队列:操作系统的进程调度、生产者-消费者模型、消息中间件(如Kafka、RabbitMQ)的核心思想都基于队列的先进先出特性,保证任务按顺序被处理;
  • 缓冲区:例如打印机的打印队列、网络数据包的接收缓冲区,都需要按到达顺序依次处理;
  • 循环队列:在数据量有明确上限、且频繁出入队的场景(如环形缓冲区),会使用数组实现的循环队列,通过取模运算复用空间,避免链表频繁申请释放节点的开销。

八、队列 vs 栈 对比总结

特性队列
操作原则后进先出(LIFO)先进先出(FIFO)
操作端一端(栈顶)两端(队头出,队尾进)
常用实现方式数组链表
核心指针一个tophead + tail 两个指针
典型应用括号匹配、DFS、函数调用栈BFS、任务调度、消息队列

九、总结

队列的核心也只有一句话:先进先出。相比栈用数组实现的简单直接,队列因为需要同时在两端高效操作,更适合用链表 + 头尾双指针的方式实现,这样入队出队都能稳定做到O(1)。理解队列,尤其是配合BFS的使用场景,是后续学习树的层序遍历、图论算法的重要基础,建议实现完之后,动手写一道BFS的题目加深理解。

如果这篇文章对你有帮助,欢迎点赞收藏,后续会继续更新树、二叉树等数据结构内容!

http://www.jsqmd.com/news/1250609/

相关文章:

  • 2026 年至今,彰武正规的蛙人打捞施工厂家哪家好,揭秘:水中“蛙人”打捞的惊人真相 - 企业信息推荐【官方】
  • AI工艺自动调整项目实现第1讲:整个工艺流程;方阻相关的问题;原数据理解;项目总思路;首先要完成的项目1
  • 测力传感器10大品牌推荐,广东犸力测力传感器值得信赖,研发人员推荐之选 - 品牌速递
  • 2026简历制作网站推荐:四款在线简历制作工具模板、AI功能与收费对比 - 春日收信人
  • 2026年绘资质延续人员社保要求
  • Spring Boot 接口幂等实战:用 Token + Redis 防重复提交
  • Micrometer 系列【1】JVM 可观测门面框架全景概述
  • 江诗丹顿中国售后服务中心|电话及维修地址权威信息公告(2026年7月最新) - 江诗丹顿服务中心
  • 2026年7月专业的室内墙面装修材料厂商推荐,旧墙翻新艺术涂料/微水泥艺术涂料,室内墙面装修材料厂商口碑推荐 - 品牌推荐师
  • 20260722 4(+1) 85 18
  • 保姆级教程:MCP 工具链搭建实战——从零配置 AI 编程助手
  • 南昌觅食指南:食材新鲜的重庆火锅门店场景盘点 - 品牌2026推荐
  • Pulsar 消息同步机制
  • 矿山破碎设备液压监测采购,液压传感器十大厂家推荐,广东犸力靠谱使用寿命更长 - 品牌速递
  • APS 需求计划(Demand Planning)技术拆解
  • Go http.Client 实战:连接池复用、超时设置与 context 取消
  • HarmonyOS7 数据持久化:Preferences 和 RDB 到底选哪个?
  • 广州财税服务行业GEO优化公司选型指南丨生成式引擎优化服务商深度测评2026本地榜单解析 - 企业新闻快传
  • 59.嵌入式C语言高级宏定义实战:多行宏、字符串化与符号拼接
  • 【RT-DETR涨点改进】TGRS 2026 | 特征融合改进篇 |引入CDSF跨域协同融合模块,增强特征互补性与语义一致性,助力高光谱目标检测、遥感目标检测、多模态融合目标检测任务,高效涨点
  • NTC 热敏电阻全解:计算、曲线读数、跨品牌替换一次讲透
  • 2026年陕西学化妆选校攻略:正规权威机构甄别与避坑指南 - 产业观察报
  • 2026 年现阶段泾县评价高的膜结构张拉膜施工厂家推荐公司怎么联系,颠覆认知:膜结构张拉,比你想象的更省钱! - 行业鉴选官
  • 椰林海鲜码头企业新愿景是什么?:良正基业永昌 - 18002239949
  • 智能装备整机采购指南,2026转矩传感器品牌排行更新,广东犸力销量排名逐年攀升 - 品牌速递
  • ESC框架知识点
  • D2D商业化初期的博弈:成本、价格与增长的“三重奏”
  • 沁园春·数智潮
  • 汽车轮重检测仪品牌排名汇总揭晓,浙江润鑫头部品牌一致好评实力获市场认可 - 品牌速递
  • 老板必看!AI落地就做这两件事,ROI瞬间翻倍!