概述
输入的算术表达式是中缀表达式,由于操作符的优先级不同,不能直接进行求解,而后缀表达式的计算不需要判别操作符的优先级,所以我实现算术表达式的思路是:1)应用堆栈将中缀表达式转化为后缀表达式,2)用堆栈实现后缀表达式求值。
用堆栈实现后缀表达式求值的过程:从左到右读入后缀表达式的各项,并根据读入的对象判断执行何种操作,有以下3中情况:
1) 当读入的是一个运算数时,把它压入栈中;
2) 当读入的是一个运算符时,就从栈中弹出适当数量的运算数并进行计算,计算结果再压回到栈中;
3) 处理完整个后缀表达式之后,栈顶上的元素就是表达式的结果。
应用堆栈将中缀表达式转换为后缀表达式的基本过程为:从头到尾读取中缀表达式的每个对象,对不同对象按不同情况处理:
1) 如果遇到空格则认为是分隔符,无需处理;
2) 若遇到运算数,则直接输出;
3) 若是左括号,则将其压入至栈中;
4) 若遇到的是右括号,表明括号内的中缀表达式已经扫描完毕,将栈顶的运算符弹出并输出,直到遇到左括号(左括号也出栈,但不输出);
5) 若遇到的是运算符,若该运算符的优先级大于栈顶运算符的优先级,则把它压入栈中;若该运算符的优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出,在比较新的栈顶运算符,按这样的处理方法,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
6) 若中缀表达式中的各对象处理完毕,则把堆栈中保留的运算符一并输出。
在代码实现的过程中,首先我们要定义一个栈,因为在后缀表达式求值的过程中,栈是用来存运算数的;在中缀转换为后缀表达式的过程中,栈是用来存放操作符的。所以在实现栈的时候我用了模板。栈的实现如下:
// Stack.h
#include<vector>
template <class T>
class Stack{
public:
void push(const T&);
T pop();
T peek();//该函数只是返回最顶端的元素,并不删除
bool empty();
private:
std::vector<T> _stack;
};
template<class T>
void Stack<T>::push(const T& a){
_stack.push_back(a);
}
template<class T>
T Stack<T>::pop(){
//在要用到出栈的时候一定要保证栈不为空
auto it = _stack.end();
auto c = *(it - 1);
_stack.pop_back();
return c;
}
template<class T>
bool Stack<T>::empty(){
return _stack.begin() == _stack.end();
}
template<class T>
T Stack<T>::peek(){
auto it = _stack.end();
return *(it - 1);
}
定义相应的运算符的优先级,我用整数的大小来定义优先级的大小,对应的函数实现如下:
int op_pri(const char& a){
switch (a){
case ')':
return 0;
case '+':case '-':
return 1;
case '*':case'/':
return 2;
case '(':
return 3;
}
}
在实现优先级的时候,当输入为左括号时,需要无条件进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以右括号的优先级我定义为最低。但是要注意当当前的运算符为右括号时,证明括号内的中缀表达式已经扫描完毕,要将栈顶运算符弹出,直到左括号(这时左括号出栈,但是不用加入到运算中);还要注意当运算符是左括号时,因为我把左括号定义为了最高优先级了,如果按正常的情况,当栈顶是左括号时,应当会马上输出但是这不符合我们的要求,希望的是紧接的运算符能无条件的入栈,所以在代码的实现过程中这是中特殊的情况用个if语句强调。
接下来是对中缀转换为后缀表达式实现的最主要的功能函数,就是运算符的出入栈的情况:
int oper_cp(const char&a, const char& b){
int c = op_pri(a);
int d = op_pri(b);
if (c == 0 && d == 3)
return -1;//当左括号碰到右括号时,定义为一个特殊状态,此状态是直接把栈中的左括号操作符弹出
//右括号也不进栈
else if (d == 3)//定义优先级时,以整数大小来对应优先级的大小。但是当输入为左括号时,需要无条件
//进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以
//右括号的优先级要最低。
//但是在右括号进栈后,若紧接的操作符是+、-、*、/的话,需要进栈,这个条件语句就是在这种情况下
//对应的操作符直接进栈
return 1;
else if (c > d){
return 1;
}
else
return 0;
}
有了上述两个函数的支持,接下来就是直接对中缀表达式求值的函数,这些问题只是逻辑问题,我的代码实现如下:
void exp(Stack<double> &val, Stack<char> &op){
char c{};
string s{};
do
{
c = getchar();
if ((c >= '0'&&c <= '9') || c == '.')
{
s.push_back(c);
continue;
}
else
{
if (s != "