)
继上一篇整理完栈的学习笔记后紧接着就学了同属线性表的队列。趁热把这段的理解、踩坑和思考记录下来顺便也补上了上一篇里自己没吃透的模糊点算是对线性表基础部分的一次闭环梳理。一、初识队列从 “排队” 具象理解先进先出最开始接触队列时我是靠生活里 “排队” 的例子理解它的核心特性 —— 先进先出FIFO的。先排队的人先办理后排队的人后离开和 “队列” 这个名字本身完全对应反而比栈更好理解。我自己还会把它具象成一条两端开口的隧道一端只负责进一端只负责出数据顺着方向单向流动。对比栈是 “只有一个口的水杯”队列是 “两端通透的管道”两个模型放在一起对比从一开始就没把两者的特性搞混。本质上来说队列和栈一样都是操作受限的线性表。它不是做不到中间插入、任意位置删除而是主动约束了操作方式 —— 只能在队尾插入、队头删除用限制换来了逻辑的确定性和场景的适配性。二、实现选型我为什么优先用链表实现队列真正动手实现的时候我优先选择了链表版本理由很直接只要同时维护头指针和尾指针入队和出队都能做到 O (1) 的时间复杂度。如果用数组实现从队头删除元素需要把后面所有数据整体前移时间复杂度直接变成 O (n)效率差很多。直觉上链表天然更适配队列 “两端操作” 的特性。学到这里也顺便纠正了我之前学栈时的一个模糊认知数组和链表实现的核心差异从来不是 “谁浪费空间更多”而是性能特征和适用场景。数组实现内存连续、缓存命中率高、访问速度快但有扩容开销适合数据量相对稳定、追求读取效率的场景链表实现不需要连续内存、无扩容烦恼但指针有额外空间开销、内存分散访问慢适合数据量不确定、频繁头尾操作的场景。也正因为选了链表实现我天然避开了数组队列最经典的 “假溢出” 问题。但顺着知识点往下挖还是把数组队列的问题和解法理清楚了普通数组队列的头尾指针只会单向后移出队空出来的前部空间没法复用最终会出现 “前面空着、后面堵死明明有空间却入不了队” 的假溢出。标准解法就是循环队列通过取模运算让数组在逻辑上首尾相连指针走到末尾后自动绕回开头把前部空闲空间重新利用起来。这里有个很关键的细节循环队列通常会额外多申请一个空位用来区分 “队空” 和 “队满” 两种状态。如果不预留这个空位队空和队满时都会出现 front rear 的情况无法区分多留一格后就可以通过 (rear1)%容量 front 判断队满front rear 判断队空。这格额外空间只做状态判断不存储有效数据是用极小的空间代价换来了逻辑的清晰。三、边界处理靠习惯养成而非临时细心和栈的实现一样队列的边界问题说难不难但很容易漏判空队列时执行出队会出现下溢错误固定容量的队列满时入队会出现溢出循环队列下标计算错误导致越界。这些问题说到底不是逻辑难题写代码做多了自然会形成条件反射 —— 现在只要涉及删除、取出操作我第一反应都是先判断结构是否为空。这种边界意识靠的是日积月累的练习比临时 “细心一点” 靠谱得多。至于结构体的设计把 front、rear、有效元素个数、总容量一起封装进去属于线性表实现的常规操作整体思路和栈是相通的。四、经典题复盘用两个栈实现队列让我对栈和队列理解再上一个台阶的还是「用两个栈实现队列」这道经典题。核心思路其实很巧妙用一个栈专门做 “入队栈”所有入队操作直接压进这个栈另一个栈专门做 “出队栈”出队时先看出队栈是否为空如果为空就把入队栈里的元素逐个弹出、逐个压进出队栈。等全部倒完之后出队栈的栈顶正好就是最早入队的元素完美实现了先进先出。做完这道题最大的感受是栈和队列不是两个孤立的数据结构它们的特性是可以互相转换的。栈的 “后进先出” 倒一次就变成了队列的 “先进先出”。原本抽象的特性通过这道题一下子就落地了。五、深度思考队列的应用以及为什么函数调用不用队列说到队列的实际应用我印象最深的场景是聊天消息先发送的消息先展示出来后发的排在后面完全贴合先进先出的特性。而更重要的收获是终于补上了上一篇栈里没彻底想通的问题为什么说栈是程序最底层、最核心的应用而队列不是答案就藏在函数调用的逻辑里。函数嵌套调用时比如 A 调用 BB 调用 C执行顺序一定是先跑完最内层的 C把结果返回给 BB 执行完再把结果返回给 A。也就是后被调用的函数先执行完成并返回这完全匹配栈 “后进先出” 的特性。如果换成队列的先进先出就变成先调用的 A 先返回 —— 可 A 还在等 B 的执行结果逻辑上根本说不通。也正是因为这个底层逻辑栈支撑起了所有程序的函数调用、递归执行是程序运行的基础结构而队列更多用在业务层面的顺序调度、消息排队定位上有本质区别。想通这一点才算真的理解了栈和队列各自的核心价值。六、给刚学队列的新手一点小建议最后和栈对应给新手提两个建议第一先建立具象认知。把队列想象成排队、隧道先把 “先进先出、两端操作” 的直觉刻进脑子里再动手写代码会顺畅很多。第二入门实现优先从链表版本入手。链表队列逻辑更直白不用一开始就纠结假溢出、循环队列、判空判满这些细节先把核心特性跑通再去优化数组版本学习曲线会平缓很多。学完栈和队列对 “操作受限的线性表” 这句话的理解又深了一层。不是功能越全越好合适的约束反而能让结构更精准地匹配问题。基础部分先整理到这里后面学到更进阶的用法再回来补充更新。