学习笔记——栈

栈(Stack)

一、栈的定义与特性

定义

栈是限定仅在表尾进行插入和删除操作的线性表。

特性

  • 先进后出(FILO - First In Last Out)

  • 后进先出(LIFO - Last In First First)

核心概念

  • 栈顶(Top):允许操作的一端

  • 栈底(Bottom):不允许操作的一端

  • 入栈(Push):向栈顶添加元素

  • 出栈(Pop):从栈顶移除元素

二、两种"栈"的区别与联系

数据结构中的栈

// 链式栈示例

typedef struct stacknode {

DATATYPE data;

struct stacknode *next;

} LinkStackNode;

  • 内存位置:堆空间(动态分配)

  • 适用场景:更广泛,可自定义大小

  • 操作方式:通过指针操作

系统栈(调用栈)

  • 内存位置:0~3G内存中的一段(通常约8M)

  • 存储内容:

  1. 函数的调用关系

  2. 局部变量

  3. 参数

  4. 返回地址

  • 工作原理:同样遵循先进后出原则

共同点

  • 都遵循先进后出原则

  • 都有入栈(push)和出栈(pop)操作

三、栈的存储方式

1. 顺序存储(数组实现)

  • 使用连续内存空间

  • 需要预分配固定大小

  • 操作简单,效率高

2. 链式存储(链表实现)

// 链栈节点结构

typedef struct stacknode {

DATATYPE data; // 数据域

struct stacknode *next; // 指针域

} LinkStackNode;

// 链栈结构

typedef struct {

LinkStackNode *top; // 栈顶指针

int clen; // 栈长度

} LinkStack;

四、栈的分类(按增长方向)

根据栈指针移动方向,栈可分为4种类型:

|-----|---------|----------|------------|
| 类型 | 名称 | Top指针指向 | 新增元素后Top变化 |
| 空增栈 | 空栈+地址增加 | 新元素待插入位置 | 地址增大 |
| 空减栈 | 空栈+地址减少 | 新元素待插入位置 | 地址减小 |
| 满增栈 | 满栈+地址增加 | 最后入栈的元素 | 地址增大 |
| 满减栈 | 满栈+地址减少 | 最后入栈的元素 | 地址减小 |

关键区别:

  • 增栈:新增元素后,Top指针指向的内存地址慢慢变大

  • 减栈:新增元素后,Top指针指向的内存地址慢慢变小

  • 空栈:Top指针指向新元素待插入的位置

  • 满栈:Top指针指向最后入栈的元素的位置

五、栈的基本操作接口

链栈操作函数

// 创建栈

LinkStack* CreateLinkStack();

// 入栈操作

int PushLinkStack(LinkStack* ls, DATATYPE* newdata);

// 出栈操作

int PopLinkStack(LinkStack* ls);

// 获取栈顶元素

DATATYPE* GetTopLinkStack(LinkStack* ls);

// 获取栈大小

int GetSizeLinkStack(LinkStack* ls);

// 判断栈是否为空

int IsEmptyLinkStack(LinkStack* ls);

// 销毁栈

int DestroyLinkStack(LinkStack* ls);

六、栈的应用场景

1. 递归问题

  • 函数调用栈本身就是栈的应用

  • 保存函数调用现场,确保正确返回

2. 回溯问题

  • 深度优先搜索(DFS)

  • 路径记录和回退

3. 表达式求值

  • 中缀转后缀表达式

  • 运算符优先级处理

4. 括号匹配检查

  • 检查括号是否成对出现

  • 检查嵌套是否正确

5. 浏览器历史记录

  • 前进/后退功能

  • 页面访问历史管理

七、相关数据结构对比

双向链表 vs 栈

// 双向链表节点(对比)

typedef struct dounode {

DATATYPE data;

struct dounode *prev; // 前驱指针

struct dounode *next; // 后继指针

} DouLinkNode;

// 栈节点(对比)

typedef struct stacknode {

DATATYPE data;

struct stacknode *next; // 只有后继指针

} LinkStackNode;

主要区别:
访问方式:
  • 栈:只能从栈顶访问(受限)

  • 双向链表:可从任意位置访问(灵活)

操作限制:
  • 栈:只能在表尾操作

  • 链表:可在任意位置插入删除

实现复杂度:
  • 栈:实现简单

  • 链表:实现相对复杂

八、栈的适用原则

何时使用栈?

  • 需要"撤销"操作时

  • 需要记录历史状态时

  • 问题具有递归特性时

  • 需要反转数据顺序时

  • 检查嵌套结构时

栈的优势

  • 操作简单:只有push和pop

  • 效率高:时间复杂度O(1)

  • 内存可控:链栈动态分配,不浪费空间

  • 逻辑清晰:先进后出,易于理解

九、实际编程示例

表达式求值栈示例

// 使用两个栈:数字栈和运算符栈

LinkStack* num_stack = CreateLinkStack(); // 数字栈

LinkStack* op_stack = CreateLinkStack(); // 运算符栈

// 扫描表达式

while (表达式未结束) {

if (是数字) {

PushLinkStack(num_stack, &数字);

} else if (是运算符) {

// 处理优先级

while (栈顶运算符优先级 >= 当前运算符) {

// 弹出运算

弹出两个数字和一个运算符;

计算结果;

结果入数字栈;

}

PushLinkStack(op_stack, &运算符);

}

}

十、 总结要点

  • 栈的本质:受限的线性表,只能在表尾操作

  • 核心特性:先进后出(FILO/LIFO)

  • 两种实现:顺序存储(数组)和链式存储(链表)

  • 四种分类:空增栈、空减栈、满增栈、满减栈

  • 应用广泛:递归、回溯、表达式求值、括号匹配等

  • 系统对比:数据结构栈(堆空间) vs 系统栈(内存固定区域)

相关推荐
自然语1 小时前
人工智能之数字生命-情绪
人工智能·算法
编程修仙1 小时前
第七篇 java的注解以及使用反射实现自定义注解功能
xml·java·开发语言·spring
Ayanami_Reii1 小时前
进阶数据结构应用-维护序列
数据结构·算法·线段树
_w_z_j_1 小时前
mari和shiny() (多状态dp数组)
算法
GesLuck1 小时前
Beaglebone BB Black C版 AM3358(一)
c语言·开发语言·物联网·硬件架构
lusasky1 小时前
Java内存堆栈AI分析工具全览
java·开发语言
CoderYanger1 小时前
C.滑动窗口-越长越合法/求最短/最小——2904. 最短且字典序最小的美丽子字符串
java·开发语言·数据结构·算法·leetcode·1024程序员节
Tim_101 小时前
【算法专题训练】33、堆
算法
光头程序员1 小时前
学习笔记——vite 打包构建优化之tree shaking
笔记·学习