1. 简介
例如计算表达式"(51+(31+6)*5) * (6+1)"的值,那么该如何入手呢?首先这是中序表达的形式,类似于数据结构“二叉树的中序遍历顺序”。虽然人认为这个表达式是很简单,能够很清楚的知道该表达式先计算(31+6),然后计算(31+6)*5,但是对于计算机而言,这是困难的,因为中序表达式是一个复杂的结构。计算机能够很清楚的计算出这个表达式的后缀表达式的值,该后缀表达式为" 51 31 6 + 5 * + 6 1 + * ",计算机可以采用逆波兰表达式求值的方式计算计算这个后缀表达式。后缀表达式类似于数据结构“二叉树的后序遍历顺序”。本质上中序表达式与后序表达式表达的含义是一样的,中序的形式适合人看,而后序的形式适合计算机看。那么问题的关键是如何将中序表达式转换成后序表达式?其基本的思路在下面进行描述。
1)对于表达式中的数字而言,中序表达式和后序表达式的顺序是不变的。如中序表达式"(51+(31+6)*5) * (6+1)"和后序表达式" 51 31 6 + 5 * + 6 1 + * "的数字顺序均是51 31 6 5 6 1。
2)中序表达式的优先级。括号的优先级高于加减乘除运算符;嵌套一级括号的优先级小于嵌套二级括号的优先级;同一级括号中乘除的优先级高于加减的优先级。
3)中序表达式中有括号嵌套,而后序表达式中没有括号,后序表达式的生成可以理解成在每个数字的后面插入或者不插入运算符。运算符的数据结构采用栈的结构,栈中存储的元素为【运算符,优先级】。在每个数字后面插入或者不插入运算符是由当前的运算符优先级和栈顶的运算符优先级决定。
2. 代码
//中缀表达式变成后缀表达式
//难点:1)具有括号。2)具有乘除的优先级高于加减优先级
//因此括号改变了加减乘除符号的优先级,但是同一级括号里面的加减乘除符号按照原来的优先级
//该题转换成加减乘除符号可变的优先级
string transToRPN(string s){
string ans;//保存最终生成的逆波兰表达式
//具有优先级的符号栈[符号,优先级];括号的优先级等级是10,乘除优先级等级2,加减优先级等级1,可累加
stack<pair<char,int>> op_stack;
//存放括号
stack<char> brackets;
//表达式需要识别的是符号、括号、数字
int n = s.length();
for(int i=0;i<n;i++){
//对于数字的处理不改变其顺序
if(s[i]>='0'&&s[i]<='9'){
while(s[i]>='0'&&s[i]<='9'){
ans.push_back(s[i]);
i++;
}
ans.push_back(' ');
i--;
}
//对于符号的处理要考虑放置的位置
if(s[i]=='+'||s[i]=='-'){
//计算当前符号优先级
int current_priority = brackets.size()*10+1;
pair<char,int> p({s[i],current_priority});
if(op_stack.empty()){//栈空无前面的符号,表达式为e1 op e2 ==> e1 e2 op
op_stack.push(p);
}else{
//获得上一个符号的优先级
pair<char,int> last_node = op_stack.top();
if(current_priority > last_node.second){
//例如E1 op1 E2 op2 E3,其中op2的优先级高于op1 ==> E1 E2 E3 op2 op1
//此时E2 E3结合在一起,op1继续放在栈中保留
op_stack.push(p);
}else{
//例如E1 op1 E2 op2 E3,其中op2<=op1 ==> E1 E2 op1 E3 op2
//有可能持续的出现当前符号优先级小于等于栈顶符号优先级
while(!op_stack.empty()&¤t_priority<=op_stack.top().second){
ans.push_back(op_stack.top().first);
ans.push_back(' ');
op_stack.pop();
}
//将栈中的符号填完后,当前符号入栈
op_stack.push(p);
}
}
}else if(s[i]=='*'||s[i]=='/'){
//计算当前符号优先级
int current_priority = brackets.size()*10+2;
pair<char,int> p({s[i],current_priority});
if(op_stack.empty()){//栈空无前面的符号,表达式为e1 op e2 ==> e1 e2 op
op_stack.push(p);
}else{
//获得上一个符号的优先级
pair<char,int> last_node = op_stack.top();
if(current_priority > last_node.second){
//例如E1 op1 E2 op2 E3,其中op2的优先级高于op1 ==> E1 E2 E3 op2 op1
//此时E2 E3结合在一起,op1继续放在栈中保留
op_stack.push(p);
}else{
//例如E1 op1 E2 op2 E3,其中op2<=op1 ==> E1 E2 op1 E3 op2
//有可能持续的出现当前符号优先级小于等于栈顶符号优先级
while(!op_stack.empty()&¤t_priority<=op_stack.top().second){
ans.push_back(op_stack.top().first);
ans.push_back(' ');
op_stack.pop();
}
//将栈中的符号填完后,当前符号入栈
op_stack.push(p);
}
}
}else if(s[i]=='('){//左括号入栈的处理
brackets.push(s[i]);
}else if(s[i]==')'){//右括号出栈的处理
brackets.pop();
}
}
while(!op_stack.empty()){
pair<char,int> p = op_stack.top();
ans.push_back(p.first);
ans.push_back(' ');
op_stack.pop();
}
return ans;
}
算法时间复杂度O(n),空间复杂度O(n)