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

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

难度 :⭐⭐

建议:重点理解栈的"后进先出(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

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

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

相关推荐
wabs6661 小时前
关于图论【最短路径之Bellman_ford 算法|卡码网94.城市间货物运输的思考】
数据结构·算法·图论·卡码网·bellman_ford·求最短路径
今日无bug1 小时前
JS 数据类型 + 内存分配:从 8 种类型到栈堆模型
javascript·数据结构
wabs6662 小时前
关于哈希表【力扣454.四数相加II的思考】
数据结构·算法·leetcode·散列表
白狐_7982 小时前
408数据结构第7章:查找①——顺序查找、折半查找、分块查找知识总结
数据结构
ShineWinsu2 小时前
对于C++中unordered_map的详细介绍
数据结构·c++·算法·面试·stl·哈希表·unordered_map
IT古董3 小时前
【MES学习笔记系列】MES 术语表
笔记·学习
lingran__3 小时前
C++ 高阶数据结构:AVL 树万字详解|原理、四种旋转、key‑value 实现、调试校验、set/map/红黑树 底层前置知识
数据结构·c++·二叉搜索树·平衡二叉树·avl树·红黑树前置
Anesthesia丶4 小时前
PromoxVE安装笔记
笔记·ubuntu·server·promox
疯狂打码的少年4 小时前
【数据结构】顺序表 vs 链表的对比与选择
数据结构·笔记·链表