【数据结构】栈:定义、顺序栈与链式栈

考点频率 :★★★★★(数据结构必考,选择题常考栈的特性与应用场景)

难度 :⭐⭐

建议:重点理解栈的"后进先出(LIFO)"特性,掌握顺序栈与链式栈的区别,以及栈在递归、表达式求值等场景中的应用

1️⃣ 什么是栈?

栈(Stack) 是一种操作受限 的线性表。它的核心特征是:只允许在表的一端进行插入和删除操作 。这一端被称为栈顶(Top) ,另一端被称为栈底(Bottom)

核心特性后进先出(LIFO,Last In First Out)------最后一个进去的,最先出来。

打个比方 :栈就像一叠盘子。你只能从最上面拿走一个盘子(出栈),也只能把干净的盘子放在最上面(入栈)。你不能直接从中间抽走一个盘子,也不能把盘子塞到中间------这就是"操作受限"的含义。

2️⃣ 栈的基本操作

操作 含义 时间复杂度
入栈(Push) 将元素放入栈顶 O(1)O(1)O(1)
出栈(Pop) 移除栈顶元素并返回 O(1)O(1)O(1)
取栈顶(Peek / Top) 查看栈顶元素但不移除 O(1)O(1)O(1)
判空(IsEmpty) 检查栈是否为空 O(1)O(1)O(1)
判满(IsFull) 检查栈是否已满(顺序栈) O(1)O(1)O(1)

核心规律 :所有操作都只发生在栈顶,与栈底无关。

3️⃣ 栈的存储方式

和线性表一样,栈也有两种存储方式:

存储方式 说明
顺序栈(Array-based Stack) 数组 实现的栈,栈顶位置用下标 top 记录
链式栈(Linked Stack) 单链表实现的栈,栈顶是链表的头节点

4️⃣ 顺序栈(Sequential Stack)

4.1 核心结构

顺序栈用数组 作为底层存储,用一个整型变量 top(栈顶指针)记录栈顶元素的当前位置。

c 复制代码
#define MAXSIZE 100
typedef struct {
    int data[MAXSIZE];   // 数组存储元素
    int top;             // 栈顶指针(指向栈顶元素位置)
} SeqStack;

4.2 栈顶指针的含义

情况 top 含义
栈空 -1 没有任何元素
有 1 个元素 0 第 0 个位置有数据
有 n 个元素 n-1 最后一个数据在 n-1 位置

注意 :栈顶指针的初始值在不同教材中有两种约定:top = -1(指向栈顶元素)和 top = 0(指向下一个空闲位置)。软考中通常使用 top = -1 表示栈空,用 ++top 入栈、top-- 出栈。

4.3 基本操作

入栈(Push)

  1. 检查栈是否已满
  2. top++(栈顶指针上移)
  3. data[top] = 新元素

出栈(Pop)

  1. 检查栈是否为空
  2. 取出 data[top]
  3. top--(栈顶指针下移)

4.4 顺序栈的优缺点

优点 缺点
实现简单,代码直观 容量固定,可能浪费内存或溢出
所有操作 O(1)O(1)O(1) 需要预先分配连续空间
无需额外指针存储 扩容成本高(需复制整个数组)

5️⃣ 链式栈(Linked Stack)

5.1 核心结构

链式栈用单链表 作为底层存储,链表的头节点就是栈顶,链表的尾节点就是栈底。

c 复制代码
// 链式栈的节点结构
typedef struct StackNode {
    int data;                  // 数据域
    struct StackNode* next;    // 指针域
} StackNode;

// 链式栈结构(只需记录栈顶指针)
typedef struct {
    StackNode* top;            // 栈顶指针(指向头节点)
    int size;                  // 栈中元素个数(可选)
} LinkStack;

5.2 基本操作

入栈(Push)

  1. 创建新节点,填入数据
  2. 新节点的 next 指向原来的栈顶
  3. 更新栈顶指针指向新节点
c 复制代码
newNode->next = top;
top = newNode;

出栈(Pop)

  1. 检查栈是否为空
  2. 保存栈顶节点的数据
  3. temp = toptop = top->next
  4. 释放 temp 节点

5.3 链式栈的优缺点

优点 缺点
容量无限(受内存限制) 每个节点需要额外的指针空间
动态分配,按需增长 存储密度低
不需要预先知道最大容量 节点分散,CPU缓存命中率低

6️⃣ 顺序栈 vs 链式栈(对比表)

对比项 顺序栈 链式栈
底层结构 数组 单链表
容量 固定(静态数组)或可扩容(动态数组) 动态增长
存储密度 高(无指针开销) 低(有指针开销)
操作时间复杂度 全部 O(1)O(1)O(1) 全部 O(1)O(1)O(1)
是否需要判满 是(静态数组时)
内存分配 预先分配连续空间 按需分配
适用场景 元素个数可预知 元素个数不可预知

7️⃣ 栈的典型应用(软考常考)

应用场景 说明
函数调用与递归 系统用栈保存函数的返回地址和局部变量
表达式求值 中缀表达式转后缀表达式、后缀表达式求值
括号匹配 检查程序中的 (){}[] 是否配对
撤销操作(Undo) 编辑器中的撤销功能用栈记录操作历史
浏览器前进后退 用两个栈实现页面历史管理

8️⃣ 经典例题

例题1 :一个栈的入栈序列为 1, 2, 3, 4, 5,以下哪个不可能是合法的出栈序列?

A. 1, 2, 3, 4, 5

B. 3, 2, 1, 5, 4

C. 4, 5, 3, 2, 1

D. 4, 3, 5, 1, 2

解析 :D 中 12 之前出栈,但 1 是最先入栈的,如果 1 要出栈,2, 3, 4, 5 必须都已经入栈且 21 之上。D 中 4, 3, 5, 1, 2 顺序不可能出现,因为 2 不可能在 1 之后出栈(21 上方,必须先出栈)。选 D


例题2:以下哪种数据结构最适合实现函数调用中的"返回地址"存储?

A. 队列

B. 栈

C. 数组

D. 链表

解析 :函数调用的返回顺序是后调用的先返回 ,符合栈的 LIFO 特性。选 B

9️⃣ 记忆口诀

栈是受限线性表,后进先出是核心。

入栈出栈都在顶,顺序链式两种存。

顺序数组要判满,链式无限靠指针。

🔟 小测验(评论区对答案)

一个栈的入栈序列为 A, B, C, D,出栈序列不可能是( )。

A. A, B, C, D

B. D, C, B, A

C. B, A, D, C

D. D, A, B, C

🔔 本专栏日更,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容

#软考中级 #软件设计师 #栈 #顺序栈 #链式栈 #数据结构 #软考备考

相关推荐
小+不通文墨17 分钟前
使用vscode里的U8G2 库编写OLED菜单控制
经验分享·笔记·单片机·嵌入式硬件·学习
每周报刊31 分钟前
竞速座舱级视界:爱攻 AGP497UCZD 超宽 QDOLED 显示器解析
数据结构·游戏·计算机外设
葡萄成熟时 !38 分钟前
泛型知识笔记
开发语言·笔记·python
北风toto1 小时前
扩展操作码技术 · 详细笔记
运维·服务器·笔记·软件设计师
Z5998178411 小时前
c#软件开发学习笔记--WPF(Canvas、数据绑定、MVVM模式、值转换器)
笔记·学习·c#
茯苓gao1 小时前
从零开发 EtherCAT 主站(六):SOEM 初始化流程详解,主站是如何发现所有从站的?
笔记·嵌入式硬件·学习·信息与通信
老当益壮梁奶奶2 小时前
Linux软件编程学习笔记(九):消息队列、共享内存与信号灯详解
linux·c语言·笔记·学习
从零开始的嵌入式之旅2 小时前
day33
linux·c语言·经验分享·笔记·嵌入式硬件
小星星闪亮登场3 小时前
图论--最小生成树(内含二分图)
数据结构·算法·图论·迭代加深·图搜索算法
一条破秋裤3 小时前
STM32 学习笔记:STM32 简介与学习路线
笔记·stm32·学习