栈与队列数据结构详解

栈(Stack)和队列(Queue)这两种基础且非常重要的线性数据结构 。它们的核心区别在于数据元素的添加(插入)和移除(删除)遵循的规则不同。

1. 栈 (Stack)

  • 核心理念: 后进先出 (Last-In-First-Out, LIFO) 。想象一摞盘子:你总是把新盘子放在最上面(添加),也总是从最上面拿走盘子(移除)。最后放上去的盘子,总是最先被拿走。
  • 操作限制: 所有的插入(push)和删除(pop)操作都只能在结构的一端进行,这一端被称为 栈顶 (Top) 。另一端称为 栈底 (Bottom) 。不能从中间或底部直接操作元素。
  • 核心操作:
    • push(item) / 入栈: 将一个新元素添加到栈顶。栈的大小增加。
    • pop() / 出栈: 移除并返回栈顶的元素。栈的大小减小。尝试从一个空栈 pop 通常会导致错误(栈下溢)。
    • peek() / top() / 查看栈顶: 返回栈顶的元素,但不移除它。只是看一眼栈顶是什么。
    • isEmpty() / 判空: 检查栈是否为空。
    • isFull() (仅在固定大小栈中需要): 检查栈是否已满(如果栈的大小是固定的)。
  • 类比:
    • 一摞盘子
    • 弹夹(子弹压入弹夹,射击时弹出最后压入的子弹)
    • 摞起来的书
    • 网页浏览器的"后退"按钮历史记录(最后访问的页面最先返回)
    • 文档编辑器的"撤销"(Undo) 功能(最后执行的操作最先撤销)
  • 实现方式:
    • 数组 (Array): 使用一个数组和一个指向栈顶的索引(通常初始化为 -1 表示空栈)。push 时索引递增并赋值,pop 时返回索引处值并递减索引。实现简单,访问快,但大小通常固定(或动态扩容有开销)。
    • 链表 (Linked List): 使用单链表,通常将链表的头节点 作为栈顶 。push 相当于在链表头部插入新节点,pop 相当于删除链表头节点并返回其值。大小可以灵活增长,但每个节点有额外指针开销。
  • 时间复杂度: 所有核心操作 (push, pop, peek, isEmpty) 在数组和链表实现中通常都是 O(1) 常数时间复杂度。
  • 关键特性:
    • 元素的添加和移除严格限制在栈顶一端。
    • 访问具有局部性:你只能直接访问栈顶元素。要访问栈底元素,必须先移除其上所有元素。
    • 顺序由添加顺序严格反向决定:最后添加的最先移除。
  • 典型应用:
    • 函数调用栈: 程序执行时记录函数调用链、局部变量、返回地址。调用函数时压栈,返回时出栈。
    • 表达式求值: 将中缀表达式转换为后缀/前缀表达式,以及计算后缀表达式(使用操作数栈和运算符栈)。
    • 括号匹配: 检查代码或表达式中括号 (), [], {} 是否成对且嵌套正确。
    • 撤销/重做机制 (Undo/Redo): 将用户操作按顺序压入栈,撤销时弹出并执行逆操作(可能需要两个栈)。
    • 深度优先搜索 (DFS): 在图或树的遍历中,使用栈来记录待访问的节点。
    • 回溯算法: 记录决策路径,当需要回退时从栈顶弹出状态。

2. 队列 (Queue)

  • 核心理念: 先进先出 (First-In-First-Out, FIFO) 。想象人们在超市收银台排队:新来的人排在队伍末尾(添加),排在队伍最前面的人最先被服务并离开队伍(移除)。最早排队的人,最早得到服务。
  • 操作限制: 元素的添加(enqueue)发生在结构的一端,称为 队尾 (Rear / Tail) 。元素的移除(dequeue)发生在结构的另一端,称为 队头 (Front / Head) 。不能从中间直接操作元素。
  • 核心操作:
    • enqueue(item) / 入队 / offer(item): 将一个新元素添加到队尾。队列的长度增加。
    • dequeue() / 出队 / poll(): 移除并返回队头的元素。队列的长度减小。尝试从一个空队列 dequeue 通常会导致错误(队列下溢)。
    • peek() / front() / 查看队头: 返回队头的元素,但不移除它。
    • isEmpty() / 判空: 检查队列是否为空。
    • isFull() (仅在固定大小队列中需要): 检查队列是否已满。
  • 类比:
    • 超市收银台排队
    • 打印机任务队列(先提交的文档先打印)
    • 客服电话等待队列(先打进来的电话先被接通)
    • 水管(水从一端流入,从另一端流出,先流入的水先流出)
  • 实现方式:
    • 简单数组: dequeue 后所有元素需要前移(时间复杂度 O(n),效率低)。不常用。
    • 循环数组 (Circular Buffer / Ring Buffer): 将数组视为首尾相连的环。front 和 rear 在到达数组末尾时会绕回到开头。高效利用空间,是固定大小队列的常用实现。需要处理队列满/空的条件(通常通过一个计数器或区分 rear+1 == front 表示满和 rear == front 表示空)。
    • 数组 (Array): 使用一个数组和两个索引:front 指向队头元素,rear 指向下一个要插入的位置(队尾)。
    • 链表 (Linked List): 使用单链表或双链表。维护两个指针:head 指向链表的第一个节点(队头),tail 指向链表的最后一个节点(队尾)。enqueue 在 tail 后添加新节点并更新 tail,dequeue 移除 head 节点并更新 head。大小灵活增长,是动态队列的首选实现。
  • 时间复杂度:
    • 链表实现: enqueue, dequeue, peek, isEmpty 都是 O(1) 。
    • 循环数组实现: enqueue, dequeue, peek, isEmpty 也都是 O(1) 。
  • 关键特性:
    • 元素的添加和移除严格限制在两端:添加在队尾,移除在队头。
    • 元素的处理顺序严格遵循其到达的先后顺序:最早添加的最先移除。
  • 典型应用:
    • 任务调度: 操作系统进程调度(如先来先服务 FCFS)、线程池任务队列。
    • 消息队列: 分布式系统中,生产者将消息放入队列,消费者从队列中取出消息处理,实现解耦和异步通信(如 RabbitMQ, Kafka)。
    • 广度优先搜索 (BFS): 在图或树的遍历中,使用队列来记录待访问的节点(按层级遍历)。
    • 数据缓冲: 在数据传输速率不匹配的两个组件之间充当缓冲区(如 IO 缓冲区、网络数据包缓冲区)。生产者写入缓冲队列,消费者从缓冲队列读取。
    • 键盘缓冲区: 记录用户按下的按键顺序,供系统按序处理。
    • 打印任务管理: 多台计算机共享一台打印机时,打印任务按提交顺序排队等待。

栈 vs 队列 对比总结

特性 栈 (Stack) 队列 (Queue)
核心理念 LIFO (后进先出) FIFO (先进先出)
操作端 仅一端 (栈顶) push / pop 两端 enqueue(队尾) / dequeue(队头)
类比 一摞盘子、弹夹 排队、水管
添加操作 push() enqueue() / offer()
移除操作 pop() dequeue() / poll()
查看操作 peek() / top() peek() / front()
典型应用 函数调用栈、表达式求值、括号匹配、撤销操作、DFS 任务调度、消息队列、BFS、缓冲、打印队列
基本实现 数组、链表 循环数组、链表
核心操作时间复杂度 O(1) (push, pop, peek) O(1) (enqueue, dequeue, peek) (链表和循环数组)

关键区别要点

  1. 操作顺序规则: 栈是 LIFO(最后进来最先出去),队列是 FIFO(最先进来最先出去)。这是最本质的区别。
  2. 操作位置: 栈的所有操作(增删查)都集中在栈顶 这一个点。队列的操作分散在队尾(添加) 和队头(移除) 两个点。
  3. 行为类比: 栈是垂直的"叠加",队列是水平的"排队"。

理解栈和队列的核心在于牢牢把握 LIFO 和 FIFO 这两个基本原则,以及它们对操作位置和顺序的限制。它们在算法和系统设计中无处不在,是构建更复杂结构和逻辑的基础模块。

相关推荐
一 乐9 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长9 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn9 小时前
Spring 注解使用详解
java·spring
一条小小yu9 小时前
Spring IoC的理解
java·后端·spring
落魄实习生10 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈10 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白11 小时前
核心功能(Service接口)
java·mybatis
乌暮12 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
大猫和小黄12 小时前
深入理解 Java Optional:从设计哲学到工程实战
java