题目
转了一圈发现都是用栈的,没人用表达式树递归做吗…个人感觉这种做法更好理解
不知道表达式树的,可以先看这篇博文
例如图片中这棵表达式树对应的就是
4+1∗(5−2)−6/3
4
+
1
∗
(
5
−
2
)
−
6
/
3
(来源见水印)
大致思路就是,对于表达式的一段子串[L,r],找出这段区间中最晚被计算的运算符c[mid],然后以mid为根,递归处理[L,mid-1]和[mid+1,r]。不过代码实现中并不需要真正把这棵树建出来。因为后缀表达式本质上就是表达式树的后序遍历,所以只要先将左子树处理成后缀表达式,再处理右子树,最后把根c[mid]放在后缀表达式的尾部就行了。
由于我的代码是从另一道计算表达式修改而来,所以有些地方可能比较冗余。
代码中有详细注释。
#include<bits/stdc++.h>
using namespace std;
const int N=1001;
char c[N];
int s[N],b[N],len; //s中存放的是后缀表达式;len为长度;b[i]表示s[i]是数还是符号,数就是0,符号就是1
int isc(int x){ return c[x]=='+'||c[x]=='-'||c[x]=='*'||c[x]=='/'||c[x]=='^'; } //判断c[x]是不是符号
int js(int a,int b,char x){ //计算
if (x=='+') return a+b;
if (x=='-') return a-b;
if (x=='*') return a*b;
if (x=='/') return a/b;
if (x=='^') return pow(a,b);
}
int prio(char x){ //优先级
if (x=='+'||x=='-') return 1;
if (x=='*'||x=='/') return 2;
if (x=='^') return 3;
return 4;
}
int cnum(int L){ //返回位置L的数
return c[L]-'0';
}
void cal(int L,int r){ //递归到[L,r]区间
int mid=L,cnt=0,flag=0; //mid找最后运算的字符位置,cnt是统计括号的,flag标记这一段里有没有运算符
if (c[L]=='('){ //下面这一段代码判断当前这一段是不是被一对括号包着
int i,cnt=1;
for(i=L+1;i<=r;i++){
if (cnt==0) break;
if (c[i]=='(') cnt++;
if (c[i]==')') cnt--;
}
if (i>r) { cal(L+1,r-1);return; } //如果是的话直接去掉括号
}
for(int i=L;i<=r;i++){
if (c[i]=='(') cnt++;
if (c[i]==')') cnt--;
if (isc(i)){
flag=1;
if (cnt==0&&prio(c[i])<=prio(c[mid])) mid=i; //在括号外且优先级较低
}
}
if (!flag){ //如果当前这一段只有数字
s[++len]=cnum(L); //直接加入后缀表达式
return;
}
cal(L,mid-1);cal(mid+1,r); //递归左右子树
s[++len]=c[mid];b[len]=1; //加入后缀表达式
}
void print(){ //这一段就是计算后缀表达式
while(len>1){
for(int i=1;i<=len;i++)
if (b[i]) printf("%c ",s[i]);
else printf("%d ",s[i]);
int mid;
for(mid=1;mid<=len;mid++)
if (b[mid]) break;
s[mid-2]=js(s[mid-2],s[mid-1],s[mid]);
for(int i=mid-1;i<=len-2;i++) s[i]=s[i+2],b[i]=b[i+2];
len-=2;
printf("\n");
}
printf("%d",s[1]);
}
int main(){
scanf("%s",c);
int n=strlen(c);
cal(0,n-1);
print();
return 0;
}