阿拉尔市网站建设_网站建设公司_SQL Server_seo优化
2026/1/16 16:47:46 网站建设 项目流程

🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、游戏、规划、程序人生
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

  • 栈与队列之队列入门攻略:从核心概念到链表实现✨
    • 文章摘要
    • 一、知识回顾:栈 vs 队列的核心差异
    • 二、队列的核心概念:什么是“先进先出”?📚
      • 1. 队列的定义
      • 2. 队列的关键操作术语
      • 3. 队列的实现方式对比:数组 vs 链表
    • 三、队列的工程化实现:链表版队列完整代码💻
      • 1. 头文件定义(queue.h):接口声明
      • 2. 源文件实现(queue.c):核心逻辑
    • 四、核心易错点解析:边界场景处理⚠️
    • 五、写在最后

栈与队列之队列入门攻略:从核心概念到链表实现✨

你好!欢迎来到线性表系列栈与队列篇的第二篇内容~

在上一篇中,我们吃透了“后进先出”的栈结构,而今天的主角——队列,恰恰是栈的“反向搭档”,它遵循“先进先出”原则,在日常开发和算法解题中同样不可或缺,比如消息队列、任务排队、二叉树层序遍历等场景都能看到它的身影。

继栈的数组实现后,我们这篇将聚焦队列的核心知识,从概念到实现,手把手带你搞定这个“排队专用”的线性表!


文章摘要

本文为线性表系列栈与队列篇第二篇入门内容,聚焦队列这一受限线性表。通俗讲解队列“先进先出”的核心特性,对比数组与链表两种实现方式的优劣并选定单向链表为最优方案。通过工程化的头文件+源文件结构,完整实现队列的初始化、销毁、入队、出队等核心操作,详解队头队尾指针的边界处理细节,补充空队列、单节点队列等特殊场景的处理逻辑,零基础吃透队列的底层实现。

阅读时长:约20分钟
阅读建议

  1. 基础薄弱者:先理解队列“先进先出”特性,再对照代码梳理指针逻辑
  2. 工程开发者:重点关注内存释放、队空/队满判断等健壮性细节
  3. 面试备考者:牢记队列的特性、实现方式及典型应用场景
  4. 查漏补缺者:直接查看出队操作中单节点队列的特殊处理逻辑

一、知识回顾:栈 vs 队列的核心差异

在学习队列之前,我们先对比栈和队列的核心区别,快速建立认知:

数据结构核心特性操作端最优实现方式
后进先出(LIFO)仅栈顶一端数组(尾插尾删)
队列先进先出(FIFO)队尾入、队头出链表(头删尾插)

简单总结:栈是“一口进出”,队列是“一头进、另一头出”!


二、队列的核心概念:什么是“先进先出”?📚

1. 队列的定义

队列是一种特殊的线性表,只允许在一端插入元素(队尾),在另一端删除元素(队头),遵循先进先出(FIFO = First In First Out)原则。

生活中的队列例子随处可见:

  • 排队买奶茶:先排队的人先买到奶茶 → 完美契合“先进先出”
  • 打印机打印任务:先提交的任务先打印 → 典型的队列应用
  • 消息队列:先产生的消息先被消费 → 工程中最常用的队列场景

2. 队列的关键操作术语

操作名称说明操作位置
入队/进队向队列中添加元素队尾(唯一入队端)
出队/离队从队列中删除元素队头(唯一出队端)
队头允许删除元素的一端-
队尾允许插入元素的一端-

3. 队列的实现方式对比:数组 vs 链表

队列同样有两种实现方式,我们详细分析优劣,选择最优方案:

实现方式核心思路优点缺点
数组队尾入队(尾插)、队头出队(头删)实现简单、随机访问快队头出队需挪动所有元素,时间复杂度O(N),效率极低
链表(推荐)单向链表:队尾入队(尾插)、队头出队(头删)
(用tail指针记录队尾,避免找尾)
① 入队/出队效率O(1) ② 按需分配内存,无需扩容需额外维护队头/队尾指针,处理空队列/单节点队列边界

结论:单向链表实现队列是最优选择!

  • 链表头删、尾插的时间复杂度都是O(1),完美匹配队列的操作需求
  • 只需维护head(队头)和tail(队尾)两个指针,即可高效完成所有操作
  • 避免了数组出队时挪动元素的性能损耗

三、队列的工程化实现:链表版队列完整代码💻

我们依旧采用“头文件+源文件”的工程化结构实现队列,保证代码规范性。

1. 头文件定义(queue.h):接口声明

#pragmaonce#include<stdio.h>#include<stdbool.h>#include<assert.h>#include<stdlib.h>// 定义队列存储的数据类型typedefintQDataType;// 队列节点结构体(单向链表节点)typedefstructQueueNode{structQueueNode*next;// 指向下一个节点的指针QDataType data;// 节点存储的数据}QNode;// 队列结构体(维护队头、队尾指针)typedefstructQueue{QNode*head;// 队头指针(出队端)QNode*tail;// 队尾指针(入队端)}Queue;// 队列核心操作接口voidQueueInit(Queue*pq);// 初始化队列voidQueueDestroy(Queue*pq);// 销毁队列(释放内存)voidQueuePush(Queue*pq,QDataType x);// 入队(队尾插入)voidQueuePop(Queue*pq);// 出队(队头删除)QDataTypeQueueFront(Queue*pq);// 获取队头元素QDataTypeQueueBack(Queue*pq);// 获取队尾元素intQueueSize(Queue*pq);// 获取队列元素个数boolQueueEmpty(Queue*pq);// 判断队列是否为空

2. 源文件实现(queue.c):核心逻辑

#include"queue.h"// 初始化队列voidQueueInit(Queue*pq){assert(pq);// 确保队列指针不为NULLpq->head=NULL;// 初始队头为空pq->tail=NULL;// 初始队尾为空}// 销毁队列(遍历释放所有节点)voidQueueDestroy(Queue*pq){assert(pq);QNode*cur=pq->head;while(cur)// 逐个释放节点{QNode*next=cur->next;// 保存下一个节点free(cur);cur=next;}// 重置指针,防止野指针pq->head=NULL;pq->tail=NULL;}// 入队(队尾插入)voidQueuePush(Queue*pq,QDataType x){assert(pq);// 创建新节点QNode*newnode=(QNode*)malloc(sizeof(QNode));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;// 更新队尾指针}}// 出队(队头删除)voidQueuePop(Queue*pq){assert(pq);assert(!QueueEmpty(pq));// 队空禁止出队// 处理单节点队列(删除后队头队尾都为空)if(pq->head->next==NULL){free(pq->head);pq->head=NULL;pq->tail=NULL;}else// 多节点队列,头删{QNode*next=pq->head->next;free(pq->head);pq->head=next;// 更新队头指针}}// 获取队头元素QDataTypeQueueFront(Queue*pq){assert(pq);assert(!QueueEmpty(pq));// 队空禁止获取returnpq->head->data;}// 获取队尾元素QDataTypeQueueBack(Queue*pq){assert(pq);assert(!QueueEmpty(pq));// 队空禁止获取returnpq->tail->data;}// 获取队列元素个数intQueueSize(Queue*pq){assert(pq);intsize=0;QNode*cur=pq->head;while(cur)// 遍历统计节点数{size++;cur=cur->next;}returnsize;}// 判断队列是否为空boolQueueEmpty(Queue*pq){assert(pq);returnpq->head==NULL;// 队头为空则队列空}

四、核心易错点解析:边界场景处理⚠️

队列实现中最容易出错的是边界场景,我们重点梳理:

  1. 空队列入队:需同时更新head和tail指针,否则会出现tail为NULL的错误
  2. 单节点队列出队:删除后必须将head和tail都置NULL,否则tail会指向已释放的节点(野指针)
  3. 出队前判空:必须用assert(!QueueEmpty(pq))防止空队列出队,避免访问NULL指针

五、写在最后

恭喜你!这一篇中你已经掌握了队列的核心知识:

  • 理解了队列“先进先出”的核心特性,对比了和栈的关键差异
  • 选定了链表作为队列的最优实现方式,吃透了头删尾插的逻辑
  • 完成了队列的工程化代码实现,掌握了边界场景的处理技巧

队列和栈是数据结构的“基础搭档”,二者结合能解决很多复杂问题:

  • 算法层面:二叉树层序遍历、BFS(广度优先搜索)依赖队列
  • 工程层面:消息队列、任务调度、限流削峰都离不开队列

下一篇,我们将进入实战环节,用栈和队列解决经典OJ题,把理论转化为解题能力!😜


点赞+收藏+关注,跟着系列内容一步步吃透栈和队列!你的支持是我创作的最大动力~👍

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询