-
栈的特点是后进先出,所以常用来处理具有递归结构的数据
- 深度优先搜索
- 表达式求值
- 子程序 / 函数调用的管理
- 消除递归
-
表达式的递归定义
- 基本符号集:${0,1,…,9,+,-,*,/,(,)}$
- 语法成分集:{<表达式> , <项> , <因子> , <常数>, <数字> }
- 中缀表达式:$23\ +\ (34\ *\ 45)\ /\ (5\ +\ 6\ +\ 7)$
- 后缀表达式:$23\ 34\ 45\ *\ 5\ 6\ +\ 7\ +\ /\ +$
-
中缀表达式
- 运算符在中间
- 需要括号改变优先级
例如:$4\ *\ x\ *\ (2\ *\ x\ +\ a)\ –\ c$
-
后缀表达式
- 运算符在后面
- 完全不需要括号
例如:$4\ x\ *\ 2\ x\ *\ a\ +\ *\ c\ –$
-
后缀表达式求值思路
循环:依次顺序读入表达式的符号序列(假设以
=
作为输入序列的结束),并根据读入的元素符号逐一分析- 当遇到的是一个操作数,则压入栈顶
- 当遇到的是一个运算符, 就从栈中两次取出栈顶(注意两个操作数在符号两侧的顺序,第一个是右操作数,第二个是左操作数),按照运算符对这两个操作数进行计算。然后将计算结果压入栈顶
如此继续,直到遇到符号
=
,这时栈顶的值就是输入表达式的值 -
部分代码:
// 计算器类定义 template <class T> class Calculator { private: stack<T> s; // 这个栈用于压入保存操作数 // 从栈顶弹出两个操作数 opd1 和 opd2 bool GetTwoOperands(T& opd1,T& opd2); // 取两个操作数,并按 op 对两个操作数进行计算 void Compute(char op); public: Calculator(void){} ; // 创建计算器实例,开辟一个空栈 void Run(void); // 读入后缀表达式,遇 “=” 符号结束 void Clear(void); // 清除计算器,为下一次计算做准备 }; template <class T> bool Calculator<T>::GetTwoOperands(T& opnd1, T& opnd2) { if (s.empty()) { cerr << "Missing operand!" <<endl; return false; } opnd1 = s.top(); // 右操作数 s.pop(); if (s.empty()) { cerr << "Missing operand!" <<endl; return false; } opnd2 = s.top(); // 左操作数 s.pop(); return true; } template <class T> void Calculator<T>::Compute(char op) { T operand1, operand2; bool result = GetTwoOperands(operand1, operand2); if (result == true) { switch(op) { case '+' : s.push(operand2 + operand1); break; case '-' : s.push(operand2 - operand1); break; case '*' : s.push(operand2 * operand1); break; case '/' : if (operand1 == 0.0) { cerr << "Divide by 0!" << endl; stack<T>().swap(s); } else { s.push(operand2 / operand1); break; } } }else{ stack<T>().swap(s); } } template <class T> void Calculator<T>::Run(void) { char c; T newoperand; while (cin >> c, c != '=') { switch(c) { case '+': case '-': case '*': case '/': Compute(c); break; default: cin.putback(c); cin >> newoperand; s.push(newoperand); break; } } if (!s.empty()) cout << s.top() << endl; // 印出求值的最后结果 }
-
将中缀表达式转换为后缀表达式
思路:利用栈来记录表达式中的运算符
-
当输入是操作数,直接输出到后缀表达式序列
-
当输入的是左括号时,也把它压栈
-
当输入的是运算符时:
while (以下循环)
if(栈非空 and 栈顶不是左括号 and 输入运算符的优先级≤
栈顶运算符的优先级)
将当前栈顶元素弹栈,放到后缀表达式序列中(此步反复循环,直到上述 if 条件不 成立,将输入的运算符压入栈中);
else
把输入的运算符压栈(>
当前栈顶运算符才压栈!) -
当输入的是右括号时,先判断栈是否为空
- 若为空(括号不匹配),异常处理,清栈退出;
- 若非空,则把栈中的元素依次弹出
- 遇到第一个左括号为止,将弹出的元素输出到后缀表达式的序列中(弹出的 开括号不放到序列中)
- 若没有遇到开括号,说明括号也不匹配,做异常处理,清栈退出
-
最后,当中缀表达式的符号序列全部读入时,若栈内仍有元素,把它们全部依次弹 出,都放到后缀表达式序列尾部。
-
若弹出的元素遇到开括号时,则说明括号不匹配,做错误异常处理,清栈退出
-