目录
前言:
栈的重要应用之一是表达式求值。本文以简单算术表达式为例展现这一应用。
提示:以下是本篇文章正文内容,代码注释较为详细,可供参考
一、计算思路及方法
1.表达式读取及存放
算术表达式可分为两部分:数字和运算符。例如有下列表达式:
a-b/c+d*e;
为了计算上述表达式的值,可以使用两个栈:一个数据栈和一个运算符栈。其中数据栈用于存放操作数(数字),运算符栈存放运算符。
2.存放顺序和运算规则
首先从左到右扫描表达式,遇到操作数时,将其压入操作数栈;遇到运算符时。与当前运算符栈的栈顶运算符比较优先级,若高于栈顶运算符的优先级或运算符栈为空,则将其压入运算符栈;否则将栈顶运算符弹出,并根据所弹出运算符的目数,弹出运算数栈中相应数目的操作数(例如“+”是耳目运算符,弹出两个运算数,而“sin”是一目,弹出一个)。然后做运算并将运算结果压入操作数栈,重复这一过程,直到当前运算符入栈(运算符栈内没有比当前运算符优先级高的其他运算符)。
继续扫描表达式,并执行相应操作,直到表达式结束。最后,如果两个栈非空,则逐个弹出运算符栈顶元素和操作符栈中相应数目的运算数,并执行相应运算,将结果压入操作数栈,重复此过程直到运算符栈为空,最后一次运算的结果便是表达式的值。
上述表达式 a-b/c+d*e的计算过程如下图所示:
从左到右扫描上述表达式。为了方便,运算符栈初始化一个优先级很低的符号 # 。根据上述操作规则: a 入操作数栈,运算符 - 入运算符栈, b 入操作数栈,运算符 / 的优先级高于栈顶运算符 - 的优先级,将其入栈。继续扫描, c 入操作数栈,运算符 + 的优先级低于运算符 / 的优先级,因此将运算符 / 弹出,并将两个操作数 c 和 b 依次弹出,做除法运算:b / c,将结果 t1 压入操作数栈。继续比较运算符 + 与栈顶运算符 - 的优先级,运算符 + 的优先级不高于运算符 - ,因此继续将操作数栈中的两个操作数 t1 和 a 弹出,做减法运算:a - t1,将结果 t2 压栈。
此时运算符已为初始状态,将运算符 + 压入运算符栈。继续扫描, d 入操作数栈,运算符 * 的优先级高于栈顶运算符 + 的优先级,将其压入运算符栈。继续扫描, e 入操作数栈,此时表达式扫描完毕(指针指向运算符 = )。逐个弹出运算符栈中的元素并执行相应操作:将 * 弹出, e 和 d 出栈,做乘法运算: d * e ,将结果 t3 压栈,最后将运算符 + 弹出, t3 和 t2 出栈,做加法运算: t2 + t3,所得结果 t4 便是上述表达式的值。
()括号优先级最高且比较特殊,这里这样实现:遇到“( ”时将其压入运算符栈,当遇到“ )”时,依次弹出运算符栈顶元素并进行相应目数计算,将计算结果压栈,重复上述操作,直到栈顶元素为“( ”停止并将“( ”出栈,至此完成“( ) ”内的运算。
对于sin,cos这样函数的运算符,我们将其识别后可以规定一个字母代替其(比如“ s ”代表“ sin ”,“ c ”代表“ cos ”,将其压入运算符栈然后将扫描指针跳过其剩余长度的扫描。
注意,为了保证算术运算符的左结合性,若当前运算符与栈顶运算符优先级相同,则将栈顶运算符弹出,执行相应运算。另外,对于双目运算符,在执行运算时首先弹出的操作数为运算符的右操作数,其后弹出的数为左操作数,如 b / c的运算过程。
二、代码实现
1.创建结点
关于结点的建立就不赘述了,这里要注意的是后面要用到的类模板Stack要先提前声明,否则在Node类里声明友元时会报错。
#include <cmath>
#include <string>
#include <iostream>
using namespace std;
template<class T> class Stack;
template<class T>
class Node {
private:
T m_data;//数据域
Node* m_next = nullptr;/