数据结构与算法 -- 08 栈 | 实际应用



说明:上一篇《 数据结构与算法 – 08 栈》介绍了栈的数据结构定义,特性,常见操作及其复杂度。学以致用才是硬道理,本篇在此基础上收集整理一些栈在软件工程的实际应用,以此加深对栈的理解。

1. 函数调用栈

函数调用栈是一个非常经典且普遍的应用。C,C++,Java等函数调用/方法调用的运行时内存过程的实现就是基于栈做的。线程运行时,操作系统会给其分配一块内存,这块内存被组织成“栈”的结构。在函数被调用时该函数相关的所有内容会被组织成一个“栈帧”。栈帧的结构如下:

  • 函数的返回地址和参数
  • 临时变量: 包括函数的非静态局部变量以及编译器自动生成的其他临时变量
  • 函数调用的上下文
  • 栈是从高地址向低地址延伸,一个函数的栈帧用ebp 和 esp 这两个寄存器来划定范围.ebp 指向当前的栈帧的底部,esp 始终指向栈帧的顶部;
    ebp 寄存器又被称为帧指针(Frame Pointer);
    esp 寄存器又被称为栈指针(Stack Pointer);
    在这里插入图片描述
    重点函数的调用和调用后的返回,其底层实现就是这些栈帧内容的入栈和出栈操作


思考: 为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?

用其它数据结构也可以。只不过,函数调用先进后出的特性,比如函数调用完毕后栈顶恢复到被调用函数,这都正好与栈特性匹配,所以用栈是最顺理成章的事情了。

参考:栈帧

2. 表达式求值

常用场景:编译器利用栈进行表达式求值。以 1 + 2*3 - 4 为例:
使用2个栈,一个用来保存操作数,一个用来保存运算符。详细操作如下:
从左往右遍历表达式,
(1) 遇到数值就压入操作数栈;
(2) 遇到运算符,先将该运算符与运算符栈顶的运算符比较;
(3) 若优先级高于栈顶运算符,则压入运算符栈;若优先级低于或等于栈顶运算符时,则栈顶运算符出栈,操作数栈顶2个操作数出栈,进行运算后的结果压入操作数栈。
(4) 继续 (2) 和 (3) 直到最后。

3. 检查表达式中的括号是否匹配

比如,{[{}]}或 [{()}([])] 等都为合法格式,而{[}()] 或 [({)] 为不合法的格式。此时我们只需要1个栈–用来保存左侧括号。我们从左往右扫描表达式,下面是判断表达式括号是否合法(匹配)的思路:
(1) 遇到左侧括号,入栈;
(2) 遇到右侧括号,则与栈顶左括号比较(先出栈):
若不匹配,说明表达式不合法,扫描结束;
若匹配则继续扫描,(扫描还未完毕)如果遇到栈为空,则不合法,扫描结束;
(3) 扫完后,若栈为空,则合法;若栈不为空,则说明表达式也不合法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值