提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、中缀表达式与后缀表达式
1.中缀表达式
中缀表达式就是我们日常见到的表达式写法,即运算符在两个运算数的中间。
例:(5+20+1∗3)/14
中缀表达式中会出现括号,这种表达式如果是人直接来进行计算的话,会比较方便,但如果编写程序,对表达式进行求值时会遇到麻烦。
2.后缀表达式
后缀表达式,又叫逆波兰式,其特点是操作符在操作数的后面。如上述中缀表达式可以转化为后缀表达式:
520+13∗+14/
当中缀表达式转换为后缀表达式后,式中的括号会取消掉(因为此时可以通过运算符的位置来决定运算的优先级)。这种表达方式虽然对人不友好,但对于计算机来说可以直接从左向右遍历表达式进行表达式求值。
二、将中缀表达式转化为后缀表达式
当我们拿到一个中缀表达式的字符串时,首先我们要将此字符串进行分割,在这里我们创建一个unit类储存算式中的操作数和操作符,其中的isNum方法用于判断此对象存储的是操作数还是操作符。如果是操作数,则value存储操作数的值;如果是操作符,则value存储操作符的权值(加减为0,乘除模为1,其他为2),op_ch存储操作符的具体值。
类实现如下:
class unit{ //此类存储运算数和运算符,通过isNum区分
public:
bool isNum;
char op_ch='#';
double value=0; //如果isNum为真,value储存数字的值,否则储存运算符的权重
unit(string s)
{
if(s[0]>='0'&&s[0]<='9')
{
isNum=true;
value=To_num(s);
}
else
{
isNum=false;
op_ch=s[0];
value=Level(op_ch);
}
}
double To_num(string s)
{
double num;
stringstream ss(s);
ss>>num;
return num;
}
int Level(char op)
{
if(op=='('||op==')') return 0;
else if(op=='+'||op=='-') return 1;
else return 2;
}
};
分割时是一个双指针算法,每次分割出一个unit,就依次存入vector中:
代码实现如下:
vector<unit> Split_str(string str) //分割表达式,并返回按顺序存储在vector中的中缀表达式
{
vector<unit> v;
int i=0,j=0;
while(j<str.size())
{
if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]=='('||str[i]==')'||str[i]=='%')
{
unit temp(str.substr(i,1));
v.push_back(temp);
i++,j++;
}
else
{
while((str[j]>='0'&&str[j]<='9')||str[j]=='.') j++;
string s=str.substr(i,j-i);
unit temp(s);
v.push_back(temp);
i=j;
}
}
return v;
}
若我们想把中缀表达式转化为后缀表达式,需要用到栈这种数据结构。首先,我们遍历中缀表达式字符串,遇到数字时,直接写入结果vector中,遇到操作符时,若操作符栈为空,则直接入栈,否则判定此时栈顶元素的权重,当操作符的权重大于栈顶元素时,直接将操作符入栈,否则不断pop出栈顶元素存入结果vector,直到操作符的权重大于栈顶元素,这时将操作符入栈。
对于括号的处理可以这样理解:左括号在入栈前的权值为无穷大,在入栈后的权值为无穷小。当遇到右括号时,不断pop出栈顶元素存入结果vector,直到pop出一个左括号与其进行匹配(注意括号永远不会进入结果vector)。
当表达式遍历结束后,将栈中的元素依次出栈,存入结果vector即可。
代码实现如下:
vector<unit> Exchange(vector<unit> v) //传入储存在vector中的中缀表达式,返回后缀表达式
{
vector<unit> res;
stack<unit> op;
for(int i=0;i<v.size();i++)
{
if(v[i].isNum) res.push_back(v[i]);
else if(v[i].op_ch=='(') op.push(v[i]);
else if(v[i].op_ch==')')
{
while(op.top().op_ch!='(')
{
res.push_back(op.top());
op.pop();
}
op.pop();
}
else
{
if(op.empty()) op.push(v[i]);
else
{
while(op.top().value>=v[i].value)
{
res.push_back(op.top());
op.pop();
if(op.empty()) break;
}
op.push(v[i]);
}
}
}
while(!op.empty())
{
res.push_back(op.top());
op.pop();
}
return res;
}
三、后缀表达式的计算
当我们成功转化为后缀表达式后,计算结果就想当简单了。首先我们需要一个栈来存储所有的操作数。遍历后缀表达式,操作数直接入栈,每当遇到操作符时,pop出栈顶的两个操作数,我们在这里将其分别称为栈顶和次栈顶元素吧。然后进行计算:
temp=次栈顶元素 操作符 栈顶元素(注意这里的前后顺序)
最后将计算出的结果直接入栈。这样,当后缀表达式遍历结束后,栈中唯一的元素即为表达式的运算结果。
代码实现如下:
double Calculate(vector<unit> v) //计算并返回后缀表达式的值
{
double res;
stack<double> num;
for(int i=0;i<v.size();i++)
{
if(v[i].isNum) num.push(v[i].value);
else
{
double a=num.top();
num.pop();
double b=num.top();
num.pop();
if(v[i].op_ch=='+') num.push(b+a);
if(v[i].op_ch=='-') num.push(b-a);
if(v[i].op_ch=='*') num.push(b*a);
if(v[i].op_ch=='/')
{
if(a==0)
{
cout<<"除0错误!"<<endl;
exit(1);
}
num.push(b/a);
}
if(v[i].op_ch=='%')
{
if(a==0)
{
cout<<"模0错误!"<<endl;
exit(1);
}
num.push(Mod(b,a));
}
}
}
return num.top();
}
最后完善一下错误检查机制,完整代码如下:
//本程序可实现表达式运算,并可以检查表达式括号匹配情况,表达式输入是否合法,是否出现除0或模0错误
#include<iostream>
#include<sstream>
#include<vector>
#include<stack>
#include<cmath>
using namespace std;
class unit{ //此类存储运算数和运算符,通过isNum区分
public:
bool isNum;
char op_ch='#';
double value=0; //如果isNum为真,value储存数字的值,否则储存运算符的权重
unit(string s)
{
if(s[0]>='0'&&s[0]<='9')
{
isNum=true;
value=To_num(s);
}
else
{
isNum=false;
op_ch=s[0];
value=Level(op_ch);
}
}
double To_num(string s)
{
double num;
stringstream ss(s);
ss>>num;
return num;
}
int Level(char op)
{
if(op=='('||op==')') return 0;
else if(op=='+'||op=='-') return 1;
else return 2;
}
};
double Mod(double a,double b) //取模
{
double aa=abs(a);
double bb=abs(b);
while(aa>bb) aa-=bb;
if(a*b>0) return aa;
else return bb;
}
bool Check(vector<unit> v) //检查表达式括号是否匹配
{
stack<char> s;
for(int i=0;i<v.size();i++)
{
if(v[i].op_ch=='(') s.push(v[i].op_ch);
if(v[i].op_ch==')')
{
if(s.empty()) return false;
s.pop();
}
}
if(!s.empty()) return false;
else return true;
}
bool Check_num(string s) //检查数字是否合法
{
if(!(s[0]>='0'&&s[0]<='9')) return false;
int point=0;
for(int i=0;i<s.size();i++)
{
if(s[i]=='.') point++;
if(!((s[i]>='0'&&s[i]<='9')||s[i]=='.')) return false;
}
if(point>1) return false;
else return true;
}
vector<unit> Split_str(string str) //分割表达式,并返回按顺序存储在vector中的中缀表达式
{
vector<unit> v;
int i=0,j=0;
while(j<str.size())
{
if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]=='('||str[i]==')'||str[i]=='%')
{
unit temp(str.substr(i,1));
v.push_back(temp);
i++,j++;
}
else
{
while((str[j]>='0'&&str[j]<='9')||str[j]=='.') j++;
string s=str.substr(i,j-i);
if(!Check_num(s))
{
cout<<endl<<"存在非法数字或符号!"<<endl;
exit(1);
}
unit temp(s);
v.push_back(temp);
i=j;
}
}
return v;
}
vector<unit> Exchange(vector<unit> v) //传入储存在vector中的中缀表达式,返回后缀表达式
{
if(!Check(v))
{
cout<<endl<<"括号不匹配,表达式错误!"<<endl;
exit(1);
}
vector<unit> res;
stack<unit> op;
for(int i=0;i<v.size();i++)
{
if(v[i].isNum) res.push_back(v[i]);
else if(v[i].op_ch=='(') op.push(v[i]);
else if(v[i].op_ch==')')
{
while(op.top().op_ch!='(')
{
res.push_back(op.top());
op.pop();
}
op.pop();
}
else
{
if(op.empty()) op.push(v[i]);
else
{
while(op.top().value>=v[i].value)
{
res.push_back(op.top());
op.pop();
if(op.empty()) break;
}
op.push(v[i]);
}
}
}
while(!op.empty())
{
res.push_back(op.top());
op.pop();
}
return res;
}
double Calculate(vector<unit> v) //计算并返回后缀表达式的值
{
double res;
stack<double> num;
for(int i=0;i<v.size();i++)
{
if(v[i].isNum) num.push(v[i].value);
else
{
double a=num.top();
num.pop();
double b=num.top();
num.pop();
if(v[i].op_ch=='+') num.push(b+a);
if(v[i].op_ch=='-') num.push(b-a);
if(v[i].op_ch=='*') num.push(b*a);
if(v[i].op_ch=='/')
{
if(a==0)
{
cout<<"除0错误!"<<endl;
exit(1);
}
num.push(b/a);
}
if(v[i].op_ch=='%')
{
if(a==0)
{
cout<<"模0错误!"<<endl;
exit(1);
}
num.push(Mod(b,a));
}
}
}
return num.top();
}
int main()
{
string str;
cin>>str;
vector<unit> exp;
exp=Split_str(str);
cout<<"分割结果:";
for(int i=0;i<exp.size();i++)
{
if(exp[i].isNum) cout<<exp[i].value<<'|';
else cout<<exp[i].op_ch<<'|';
}
exp=Exchange(exp);
cout<<endl<<"后缀表达式:";
for(int i=0;i<exp.size();i++)
{
if(exp[i].isNum) cout<<exp[i].value<<' ';
else cout<<exp[i].op_ch<<' ';
}
cout<<endl<<"运算结果:";
cout<<Calculate(exp)<<endl;
return 0;
}