学习笔记——栈

栈(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 系统栈(内存固定区域)

相关推荐
dialect_8 小时前
KMP算法
开发语言·c++·算法
reikocao8 小时前
卡尔曼滤波的动态机制:P如何改变融合权重
算法
总要学点什么9 小时前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
漂流瓶jz9 小时前
UVA-1612 猜名次 题解答案代码 算法竞赛入门经典第二版
数据结构·算法·题解·排序·aoapc·算法竞赛入门经典
夕除9 小时前
Redis 过期策略
redis·笔记·面试
吞下星星的少年·-·9 小时前
Div.2 CF 1130B题(构造,贪心)
算法·贪心·构造
开开心心_Every10 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
chenbingjie_c11 小时前
C++ 多态完全指南:从虚函数重写到虚函数表原理
开发语言·c++
是main不是漫11 小时前
【双指针——移动零】数组划分类题目的入门基石
c++·算法
Nuanyt11 小时前
Agent 学习笔记(下篇三):上线工程、进阶选择与操作界面(Stage 7 / 7.5 / 8 + 延伸路线)
笔记·python·学习·memory·aiagent·harness