第3章栈和队列
3.1栈的定义和特点
1.章节定位与学习框架
(1)数据结构板块划分
数据结构模块分为上下两篇,整体学习顺序如下:
- 上篇(线性结构):第 2 章 线性表 → 第 3 章 栈和队列(本章)
- 下篇(非线性结构):第 5 章 树和二叉树 → 第 6 章 图
- 核心逻辑:从 “简单线性结构” 到 “常用线性结构”,再到 “复杂非线性结构”,循序渐进。
(2)本章核心内容
第 3 章包含两大核心线性结构:栈和队列,二者被放在同一章学习的原因:
- 均为 “最常用的线性结构”,实际编程(如 C/C++)中应用广泛;
- 学习方法高度相似(均遵循 “定义 - 描述 - 操作 - 存储” 四要点);
- 逻辑特点互补(栈:先进后出;队列:先进先出),对比学习易理解。
2.数据结构通用学习方法(关键!)
1.四要点学习框架
无论是线性表、栈、队列,还是后续的树、图,均围绕以下 4 个核心要点展开,是贯穿整个数据结构学习的 “通用逻辑”:
| 序号 | 学习要点 | 核心内容说明 |
|---|---|---|
| 1 | 定义(含特点) | 明确结构的本质的限定条件、核心特性 |
| 2 | 描述 | 用语言 / 图示清晰表达结构的逻辑形态 |
| 3 | 操作 | 结构支持的核心功能(如插入、删除) |
| 4 | 存储 | 结构在内存中的实现方式(如顺序存储、链式存储) |
2.本章适用说明
栈和队列的学习完全遵循上述框架,本章先学 “栈” 的前 2 个要点(定义、描述),后续课时会展开 “操作” 和 “存储”;队列的学习逻辑与栈一致,后续同步推进。
3.第一部分:栈的基础认知
(1)栈的定义(本质 + 限定条件)
(a) 核心定义
栈是限定只在线性表的一端进行插入和删除操作的线性表。
- 关键 1:栈属于 “线性表”,具备线性表 “元素有序、一对一逻辑关系” 的本质;
- 关键 2:与普通线性表的核心区别 ——操作位置限定:仅允许在 “一端” 进行插入和删除,普通线性表可在任意位置操作。
(b) 易错辨析
- 正确表述:栈是一种线性表(满足线性表本质);
- 错误表述:线性表就是栈(普通线性表无操作位置限定,范围更广)。
(2)栈的核心术语(必记)
| 术语 | 英文 | 功能 / 特点 |
|---|---|---|
| 栈顶 | Top | 允许进行插入、删除操作的一端 |
| 栈底 | Bottom | 不允许任何插入、删除操作的一端 |
| 入栈(压栈) | Push | 在栈顶插入元素的操作 |
| 出栈(弹栈) | Pop | 在栈顶删除元素的操作 |
(3)栈的核心特点:后进先出(LIFO)
(a)特点本质
由 “仅栈顶操作” 的限定条件决定:后进入栈的元素会 “压在栈顶”,先进入的元素会 “被压在栈底”,删除时只能先删栈顶元素,导致 “后入的先出,先入的后出”。
(b)实例理解(视频图示逻辑)
假设元素按 “A₀ → A₁ → A₂ → ... → Aₙ₋₁” 的顺序入栈(依次从栈顶压入):
- 入栈顺序:A₀(最先入,在栈底)→ A₁ → ... → Aₙ₋₁(最后入,在栈顶);
- 出栈顺序:Aₙ₋₁(最先出,栈顶元素)→ Aₙ₋₂ → ... → A₀(最后出,栈底元素);
- 结论:入栈与出栈顺序完全相反,即 “后进先出(LIFO,Last In First Out)”,也可表述为 “先进后出”。
(4)栈的优缺点与实际应用
(a)核心优缺点
| 优点 | 缺点 |
|---|---|
| 程序可读性极强(逻辑清晰,符合人类理解习惯) | 效率较低(频繁进行入栈、出栈操作,内存开销较大) |
(b)实际应用场景
视频明确提到:在 C/C++ 前导课程中 “大量使用”,核心作用是简化程序逻辑,让代码更易理解(如函数调用栈、表达式求值等场景)。
4.本章后续学习预告
- 栈的深入学习:栈的核心操作(入栈、出栈、判空、取栈顶元素等)、栈的存储实现(顺序存储、链式存储);
- 队列的学习:按 “定义 - 描述 - 操作 - 存储” 四要点,对比栈的 “后进先出” 学习队列的 “先进先出”;
- 习题巩固:针对栈和队列的核心知识点,通过习题强化理解(对应本章课时 14)。
3.2栈的表示和操作的实现
1.栈的存储结构
栈的存储理论上包含 4 种(顺序、链式、索引、散列),但本章重点学习顺序存储(顺序栈) 和链式存储(链式栈),其余两种暂不深入。
(1)顺序栈
(a)定义
- 限定在表尾进行插入(进栈)和删除(退栈)的栈,仅表尾可操作,其他端不可用。
- 核心指针:
- 栈底指针(bottom):始终指向栈底,位置固定。
- 栈顶指针(top):指向栈顶元素的上方存储单元(空单元),随元素进出移动。
(b)核心操作(结合示意图逻辑)
| 操作 | 步骤 | 示例(依次插入 A、B、C、D、E、K,再依次删除) |
|---|---|---|
| 进栈(插入) | 1. 将元素存入当前/* by 01022.hk - online tools website : 01022.hk/zh/txtcount.html */
top指向的存储单元;2. /* by 01022.hk - online tools website : 01022.hk/zh/txtcount.html */
top向上移动(top++)。 | 插入 A:A 存入top位置→top上移;插入 K 后:K 存入对应单元→ top移至 K 上方。 |
| 退栈(删除) | 1. top向下移动(top--)(指向栈顶元素);2. 弹出当前 top指向的元素。 | 删 K:top--(指向 K)→弹出 K;删 A 后: top回到空栈初始位置→无元素可删。 |
(c)溢出现象(操作禁忌)
-
上溢(overflow):栈满时继续进站。
例:栈容量固定,插入 K 后再插 L,无空单元可用→无法进站。
-
下溢(underflow):空栈时继续退站。
例:弹出所有元素(A~K)后,再执行退站→无元素可弹。
✅ 考试提示:若记不清 “上溢 / 下溢”,可统称 “溢出”;但编程中需明确区分,避免逻辑错误。
(2)链式栈
(a)定义
- 用链表存储栈元素,节点包含数据域(data,存元素) 和指针域(next,存后继节点地址)。
- 核心特点:
- 通常不带头节点,插入 / 删除仅在表头(栈顶) 进行;
- 栈顶指针(
top)= 链表头指针,始终指向栈顶节点。
(a)关键特性
| 特性 | 说明 |
|---|---|
| 空栈状态 | top指向null(无节点)。 |
| 满栈状态 | 通常不讨论(链表可动态申请空间,仅当内存无法申请时视为 “满”)。 |
| 与顺序栈对比 | 优势不突出: 1. 栈仅在栈顶操作,无需频繁移动元素(链表解决 “移动效率低” 的优势无用); 2. 指针域额外占用空间。✅ 结论:无特殊说明时,栈优先用顺序存储。 |
2.栈操作的关键问题(高频考点)
(1)top指针的移动顺序(核心逻辑)
| 操作 | 顺序(初始化时top指向顺序表第一个位置) | 原因 |
|---|---|---|
| 进栈 | 先存元素 → 再移top(top++) | 确保top始终指向栈顶元素的上方空单元。 |
| 退栈 | 先移top(top--) → 再弹元素 | 初始top指向空单元,需先下移指向栈顶元素,才能弹出。 |
(2)栈的生活类比(理解 “后进先出”)
-
示例:军训打枪的子弹装填与发射
最后压入枪膛的子弹 → 最先发射(后进先出);
最先压入枪膛的子弹 → 最后发射(先进后出)。
✅ 栈的核心特性:
后进先出(LIFO),仅栈顶可操作。
3.栈的基本操作(4 个核心操作)
| 操作名称 | 定义 | 关键细节 |
|---|---|---|
| 1. 栈的初始化 | 将栈置为空栈。 | top指向初始位置(顺序表首地址),bottom指向栈底,栈内无元素。 |
| 2. 读取栈顶元素 | 仅获取栈顶元素的值,不修改栈内数据。 | 不移动top指针,不改变栈结构(内存 “读操作” 特性:仅读取,不改写数据)。 |
| 3. 进栈(插入) | 将元素插入栈顶。 | 步骤:存元素→top++(顺序栈);新节点接在表头→top指向新节点(链式栈)。 |
| 4. 退栈(删除) | 将栈顶元素弹出并从栈中移除。 | 步骤:top--→弹元素(顺序栈);top指向后继节点→释放原栈顶节点(链式栈)。 |
| ✅ 后续任务:巩固提升篇需通过编程实现这 4 个操作。 |
4.栈的典型应用(理论 + 实例)
(1)数值转换(核心方法:除 D 取余,余数倒排)
(a)原理
十进制数N转换为基数为D(如 2→二进制、8→八进制、16→十六进制)的数时,需通过 “除 D 取余” 得到余数,再将余数 “倒排” 得到结果 ——栈的 “后进先出” 特性完美适配 “倒排” 需求。
(b)实例
| 转换需求 | 步骤(除 D 取余,余数入栈,最后出栈倒排) | 结果 |
|---|---|---|
| 十进制 75→二进制(D=2) | 75÷2=37 余 1(入栈)→37÷2=18 余 1(入栈)→18÷2=9 余 0(入栈)→9÷2=4 余 1(入栈)→4÷2=2 余 0(入栈)→2÷2=1 余 0(入栈)→1÷2=0 余 1(入栈)→出栈顺序:1、0、0、1、0、1、1 | 1001011 |
| 十进制 1348→八进制(D=8) | 1348÷8=168 余 4(入栈)→168÷8=21 余 0(入栈)→21÷8=2 余 5(入栈)→2÷8=0 余 2(入栈)→出栈顺序:2、5、0、4 | 2504 |
(2)表达式求值(中缀表达式用双栈,后缀表达式用单栈)
①核心概念
- 中缀表达式:运算符在操作数中间(如
3*(7-2))→需用双栈(数据栈 + 运算符栈)求值; - 后缀表达式(逆波兰式):运算符在操作数后面(如
3 7 2 - *)→需用单栈求值(需先将中缀转后缀)。
②中缀表达式求值(重点:双栈配合 + 优先级规则)
(a)运算符优先级规则(必记)
- 表达式前后补 “#”,标记开始和结束,“#” 优先级最低;
- 乘除(×、÷)优先级 > 加减(+、-);
- 左括号 “(”:优先级> 括号外运算符,< 括号内运算符;
- 右括号 “)”:优先级 < 括号内运算符,遇 “(” 时,两者同时出栈(数据栈不操作);
- 两 “#” 相遇→表达式计算结束,所有栈元素弹出。
(b)实例:计算3*(7-2)(补全为#3*(7-2)#)
| 扫描字符 | 数据栈(存操作数) | 运算符栈(存运算符) | 操作逻辑 |
|---|---|---|---|
| # | [] | [#] | 运算符栈空,压入 “#”。 |
| 3 | [3] | [#] | 数据直接压入数据栈。 |
| * | [3] | [#, *] | “*” 优先级 > 栈顶 “#”,压入运算符栈。 |
| ( | [3] | [#, *, (] | “(” 优先级> 栈顶 “*”,压入运算符栈。 |
| 7 | [3, 7] | [#, *, (] | 数据直接压入数据栈。 |
| - | [3, 7] | [#, *, (, -] | “-” 优先级 > 栈顶 “(”,压入运算符栈。 |
| 2 | [3, 7, 2] | [#, *, (, -] | 数据直接压入数据栈。 |
| ) | [3, 5] | [#, *] | 1. “)” 优先级 < 栈顶 “-”,弹出 “-”;2. 数据栈弹 2(第二个操作数)、弹 7(第一个操作数),算 7-2=5,压回数据栈;3. 运算符栈顶为 “(”,与 “)” 配对,同时弹出。 |
| # | [15] | [] | 1. “#” 优先级 < 栈顶 “”,弹出 “”;2. 数据栈弹 5(第二个操作数)、弹 3(第一个操作数),算 3×5=15,压回数据栈;3. 两 “#” 相遇,计算结束,弹出所有栈元素。 |
✅ 最终结果:数据栈剩余 15,即3*(7-2)=15。 |
1.初始化双栈:数据栈为空,运算符栈先压入左侧 “#”;

2.从左到右扫描表达式:
遇到 “3”(数据):直接压入数据栈;

3.遇到 “*”(运算符):优先级高于栈顶 “#”,压入运算符栈;

4.遇到 “(”(左括号):优先级高于栈顶 “*”,压入运算符栈;

5.遇到 “7”(数据):压入数据栈;

6.遇到 “-”(运算符):优先级高于栈顶 “(”,压入运算符栈;

7.遇到 “2”(数据):压入数据栈;

8.遇到 “)”(右括号):优先级低于栈顶 “-”,需先弹出 “-”;此时从数据栈弹出两个元素(先弹 2,后弹 7),计算 7-2=5,将 5 压回数据栈;接着运算符栈顶为 “(”,与 “)” 配对,两者同时弹出(数据栈不操作);



9.遇到 “#”(右侧):优先级低于栈顶 “”,弹出 “”;从数据栈弹出两个元素(先弹 5,后弹 3),计算 3*5=15,将 15 压回数据栈;最后两个 “#” 相遇,表达式计算结束,弹出所有栈元素,数据栈中剩余的 15 就是结果。


5.队列引入
本章为 “栈和队列”,当前已完成栈的核心内容(存储、操作、应用),下一板块将学习第三章第二个重点 ——队列(定义、存储结构、操作及应用)。
6.小结
- 栈的核心:仅栈顶可操作,后进先出(LIFO);
- 存储优先:无特殊说明时,栈用顺序存储(链式栈优势不突出);
- 高频考点:
top指针移动顺序、溢出现象、中缀表达式双栈求值、数值转换的 “除 D 取余倒排”; - 应用关键:栈是解决 “需倒序处理数据” 问题的核心数据结构。
3.3队列的定义和特点
1.队列的定义(★核心概念)
-
官方定义:限制在表的一端进行删除操作、表的另一端进行插入操作的线性表。
-
与栈的定义对比(视频重点强调):
| 数据结构 | 操作限制 | 核心差异点 |
|---|---|---|
| 栈 | 仅允许在同一端进行插入和删除 | 单端 “一站式” 操作(插 + 删) |
| 队列 | 两端分工:一端只删、一端只插 | 双端 “分工式” 操作(插删分离) |
- 本质:通过 “操作位置限制” 形成有序的线性表,保证数据处理的规律性。
2.队列的核心特点(★高频考点)
-
核心规律:先进先出(FIFO,First In First Out)
-
视频生活化举例:
- 排队买票:先到者站在队前,先买票离开(符合 “先入先出”);
- 上公交:先排队的人先上车,后排队的人依次在后(若用栈的 “后进先出”,会出现 “后到先上车”,与现实逻辑相悖);
- 关键提醒:“排队” 本质就是队列的现实体现,视频调侃 “排队不叫排站”,目的是强化 “队列≠栈” 的认知。
3.队列的关键术语(★基础必备)
为明确两端的操作分工,视频定义两个核心术语,需结合操作记忆:
| 术语 | 英文 | 对应操作 | 现实场景类比 |
|---|---|---|---|
| 队头 | front | 仅负责 “出队”(删除) | 排队时 “先买票离开的一端” |
| 队尾 | rear | 仅负责 “入队”(插入) | 排队时 “新加入者排队的一端” |
- 操作规则强化:入队只能在队尾,出队只能在队头(不可混淆,否则破坏 FIFO 规律),可简单记为 “尾进头出”。
4.队列的 7 个基本操作(★考研基础操作)
视频明确队列需掌握 7 个核心操作,每个操作需明确 “目的 + 注意事项”,避免错误使用:
| 函数名 | 操作名称 | 操作目的 | 注意事项 / 补充说明 |
|---|---|---|---|
| Status InitQueue(SqQueue &Q) | 初始化 | 创建一个空队列,初始化队头、队尾指针等结构 | 后续所有操作的前提,需确保初始状态合法 |
| int QueueLength(SqQueue Q) | 求队列长度 | 统计队列中当前元素的个数 | 需遍历或通过指针计算(后续存储实现会讲具体方法) |
| Status EnQueue(SqQueue &Q, QElemType e) | 入队(插入) | 在队尾添加新元素 | 需先判断队列是否 “满”,避免 “满队列入队”(溢出) |
| Status DeQueue(SqQueue &Q, QElemType &e) | 出队(删除) | 在队头移除并返回元素 | 需先判断队列是否 “空”,避免 “空队列出队”(错误) |
| Status QueueEmpty(SqQueue Q) | 判断队列空 | 检查队列是否无元素(队头 = 队尾等标识) | 出队、取队头元素前的必要判断 |
| Status QueueFull(SqQueue Q) | 判断队列满 | 检查队列是否达到存储上限 | 入队前的必要判断,防止元素溢出 |
| Status GetHead(SqQueue Q, QElemType &e) | 取队头元素 | 读取队头元素的值(不删除元素) | 与 “出队” 的核心区别:仅读不删,需先判断队列非空 |
5.视频重点强调的易错点 & 记忆技巧
- 易错点:栈与队列的操作端混淆
-
栈:“一端管插又管删”(如杯子,只能从开口端放东西、拿东西);
-
队列:“一端只插、一端只删”(如水管,一端进水、一端出水,方向固定)。
- 记忆技巧
-
用 “生活场景锚定”:想到 “排队” 就对应队列,想到 “叠盘子”(后放的先拿)对应栈;
-
术语口诀:“前(队头)出后(队尾)进,先进先出”。
3.4队列的表示和操作的实现
1.队列的存储结构
(1)存储分类(4 种,核心记 2 种)
-
顺序存储、链式存储、索引存储、散列存储
-
实际应用核心:仅需掌握 “顺序存储” 和 “链式存储”,以顺序存储为主
(2)顺序存储(重点)
-
存储方式:用地址连续的存储单元,按 “队头→队尾” 依次存放
-
关键指针(视频反复强调):
-
Q.front:指向队头(元素存放位置)
-
Q.rear:指向队尾的下一个位置(非队尾元素本身,易错点)
-
-
初始状态:队空时 front == rear == 0(指针重合)
-
操作规则(与栈的区别):
-
进队:仅动rear,rear++(先存元素,再移指针)
-
出队:仅动front,front++(先取元素,再移指针)
-
栈动top(进 + 1 / 出 - 1),队列分动两个指针(进动 rear / 出动 front)
-
(3)链式存储(了解即可,视频不推荐用)
-
结构:同样用front(队头)、rear(队尾)指针,仅在两端插入 / 删除
-
缺点(视频明确否定):
-
需额外空间存节点地址,浪费资源
-
无法体现链表 “任意位置插入删除” 的优势(队列仅两端操作)
-
-
结论:实际优先用 “顺序存储”,链式存储无需深入掌握
2.顺序队列的致命问题:假满
(1)假满现象(视频举例说明)
-
场景:当rear指向存储空间末尾(如申请 5 个空间,rear=5),即使front前有空闲(如 front=2,前 2 个位置空),也无法进队
-
本质:“看似满,实则有空闲”,空间浪费
(2)问题原因
-
front和rear仅单向移动,无法回头利用已出队元素的空闲空间
-
例:5 个空间存 10、20、30、40、50(rear=5),出队 10、20(front=2),此时想进 60 却因 rear=5 无法进,形成假满
3.循环队列(核心考点)
(1)定义与核心价值(视频强调 “考试默认队列 = 循环队列”)
-
逻辑设计:将顺序队列的存储空间 “逻辑上连成环”,让front和rear可循环移动
-
解决问题:彻底消除 “假满”,节省空间
-
实现关键:取余运算((指针+1) % max),让指针到末尾后跳回起始位置(如 max=10,rear=9 时,(9+1)%10=0,跳回 0 位置)
(2)核心前提:牺牲 1 个空间(区分空 / 满的关键)
-
问题:循环队列中,“空” 和 “满” 若都用front==rear,计算机无法区分
-
解决方案(严蔚敏教材方案,视频指定考点):牺牲 1 个存储空间,用 “满时留 1 个空位置” 区分
-
例:申请 10 个空间(max=10),最多存 9 个元素(max-1)
(3)循环队列六大关键操作(视频逐句推导,考试必背)
| 操作类型 | 操作逻辑 / 公式 | 视频实例辅助(max=10) |
|---|---|---|
| 1. 最大存储数 | 最多存 max - 1 个元素(因牺牲 1 个空间) | max=10→最多存 9 个,max=15→最多存 14 个 |
| 2. 队空判断 | front == rear(指针重合,无元素) | front=4、rear=4→队空 |
| 3. 队满判断 | (rear + 1) % max == front(rear 加 1 后追上 front,留 1 个空) | front=2、rear=1→(1+1)%10=2==front→满 |
| 4. 进队操作 | 1. 先判断是否满; 2. 不满则将元素存入 rear 位置; 3. 更新rear = (rear+1) % max | rear=7→存元素→rear=(7+1)%10=8 |
| 5. 出队操作 | 1. 先判断是否空; 2. 非空则取 front 位置元素; 3. 更新front = (front+1) % max | front=4→取元素→front=(4+1)%10=5 |
| 6. 实际元素个数 | (rear - front + max) % max(覆盖两种场景,视频推导验证) | 场景 1:front=4、rear=7→(7-4+10)%10=3;场景 2:front=6、rear=2→(2-6+10)%10=6 |
4.视频核心考点 & 易错提醒(老师反复强调)
- 术语必记:front(队头指针)、rear(队尾下一个位置指针)、取余运算(循环核心),勿混淆
- 考试默认:无论题目是否说明,“队列” 均指 “循环队列”,直接用六大操作解题
- 公式易错点:实际元素个数公式必须加max再取余(避免 rear<front 时出现负数,如 front=6、rear=2,(2-6) 为负,加 10 后再取余得 6)
- 操作顺序:进队先存元素再移 rear,出队先取元素再移 front(顺序反了会出错)
参考资料:教材《数据结构 C 语言 第 3 版》 数据结构考研指导(基础篇) 、数据结构考研指导(基础篇) 视频课程|赵海英
2557

被折叠的 条评论
为什么被折叠?



