转自:前,中,后缀表达式
前缀表达式,中缀表达式,后缀表达式都是四则运算的表达方式,用以四则运算表达式求值,即数学表达式的求值。比如一个简单的数学表达式 (1+2)-(3+4) 这是我们常见的数学表达式类型 即中缀表达式
- 把这个表达式转化位前缀表达式(也称波兰式) -+12+34
- 把这个表达式转化位后缀表达式(也称逆波兰式) 12+34±
为什么要将简单的中缀表达式转化为复杂的波兰式或逆波兰式
原因在于,简单的中缀表达式在用人的思维逻辑来看,确实简单,但在计算机看来中序表达式是非常复杂的结构,而相对而言,(逆)波兰式在计算机看来是非常简单的结构,因为计算机普遍采用的内存结构是栈式结构,而(逆)波兰表达式在其存储和计算上也采用的是栈的特性。
中缀表达式与后缀表达式的转换
1.建立一个栈,一个队列
2.从左至右顺序遍历中缀表达式。
3.遇到操作数时,直接压入队列
4.遇到运算符时:
当遇到左括号或是栈为空,直接压栈
当遇到右括号时,依次弹栈,直至栈顶是左括号,弹栈结束!期间所有的操作符按顺序入队
当遇到操作符时,若栈顶是左括号,则直接入栈
若栈顶是操作符,则比较两操作符优先级,若当前字符优先级较高,则压栈,若当前字
符优先级小于等于栈顶运算符,则将栈顶运算符弹出并入队,继续与当前栈顶元素比较!
直到当前运算符优先级高于栈顶运算符则入栈,或是遇到左括号或是栈为空,亦入栈!
5.遍历完所有字符后,弹出栈中仍剩有的操作符,并入队
6.依次出队即可达到相应的后缀表达式!
vector<string> mid2last(string str)
{
//简易版 中缀表达式转后缀,操作符只涉及+ -
queue<string> q;
stack<string> s;
int n=str.size();
int start=-1;
for(int i=0;i<n;++i)
{
//记录操作数的首位
if(str[i]>='0' && str[i]<='9')
{
if(start==-1)
start=i;
}
else
{
if(start!=-1)
{
//将完整操作数入队
q.push(string(str.begin()+start,str.begin()+i));
//cout<<string(str.begin()+start,str.begin()+i);
start=-1;
}
//遇到空格则继续遍历
if(str[i]==' ')
continue;
//作括号直接入栈
else if(str[i]=='(')
s.push("(");
else if(str[i]==')')
{
//右括号依次弹栈并入队,直至遇到左括号
string tmp=s.top();
s.pop();
while(tmp!="(")
{
q.push(tmp);
tmp=s.top();
s.pop();
}
}
else if(str[i]=='+')
{
//遇到+号操作符,栈为空则直接进栈
if(s.empty())
s.push("+");
else
{
string tmp=s.top();
//栈顶为左括号亦直接进栈
if(tmp=="(")
s.push("+");
//由于操作符只有+ -,且优先级一样,那么遇到栈中不可能共存多个操作符
//因此栈顶为操作符时,直接弹栈并入队,然后将当前字符入栈
else if(tmp=="+" || tmp=="-")
{
s.pop();
s.push("+");
q.push(tmp);
}
}
}
else if(str[i]=='-')
{
//遇到-号,同+号
if(s.empty())
s.push("-");
else
{
string tmp=s.top();
if(tmp=="(")
s.push("-");
else if(tmp=="+" || tmp=="-")
{
s.pop();
s.push("-");
q.push(tmp);
}
}
}
}
}
if(start!=-1)
{
//末尾可能还有操作数,将操作数入队
q.push(string(str.begin()+start,str.end()));
}
while(!s.empty())
{
//弹出栈中剩余操作符入队
q.push(s.top());
s.pop();
}
vector<string> res;
while(!q.empty())
{
//以一定的格式保存后缀表达式
res.push_back(q.front());
q.pop();
}
return res;
}
后缀表达式值的计算
1.建立一个栈
2.从左到右顺序遍历后缀表达式
3.遇到操作数则直接进栈
4.遇到操作符,则依次弹出栈顶上的两个元素,对其进行运算,然后将运算结果进栈
5.重复 2,3,直至遍历完整个表达式,那么栈中将只剩下一个元素,它就是整个表达式的运算结果
int evalRPN(vector<string>& tokens) {
if(tokens.empty())
return 0;
stack<int> helper;
int n=tokens.size();
for(int i=0;i<n;++i)
{
int num;
//运用c++字符串流,将字符串转化为整数
istringstream in(tokens[i]);
in>>num;
if(!in.fail())
{
//若当前字符串是操作数,则转化成功,将操作数入栈
helper.push(num);
}
else
{
//若当前字符串是操作符,则转化失败
//依次弹出两个栈顶元素
int num1=helper.top();
helper.pop();;
int num2=helper.top();
helper.pop();
//判断操作符种类,进行数学运算并将运算结果入栈
if(tokens[i]=="+")
helper.push(num2+num1);
else if(tokens[i]=="-")
helper.push(num2-num1);
else if(tokens[i]=="*")
helper.push(num2*num1);
else if(tokens[i]=="/")
helper.push(num2/num1);
}
}
return helper.top();
}
前缀表达式在操作上与后缀表达式的几个不同点
- 前缀表达式的所有遍历从右到左的!自然遇到括号的处理方式与后缀表达式是相反的
- 在与中缀表达式的转化时,遇到操作符的处理方式是,与栈顶元素比较,若优先级大于等于栈顶运算符,就直接入栈,注意在后缀表达式中是大于。