中缀转后缀:
流程:
用一个栈用来存储表达式中的符号;
遍历中缀表达式:
1.若当前字符为字母,则加入输出队列
2.若当前字符为运算符:
- 若当前栈空或栈顶元素为左括号 或 优先级小于当前运算符,则入栈;
- 若当前符号为右括号,则一直出栈直到遇到左括号,并加入输出队列,右括号不入栈;
- 若当前运算符优先级低于或者等于当前栈顶元素,则一直出栈遇到左括号或栈顶元素优先级小于当前运算符,并加入输出队列,最后将当前运算符入栈。
- 最后情况栈内元素,全部加入输出队列
后缀转表达式树
流程
- 遍历后缀表达式,每个字符都新建一个节点rt
- 对于每个字符,如果是字母,则直接将节点入栈;
- 如果是符号,则从栈中依次取出两个节点r,l 作为 rt 的右左子树
- 最后返回栈顶节点即为根节点。
- 表达式树的中序遍历是中缀表达式。
TreeNode* build() {
stack<TreeNode*>s;
for( int i = 0 ; i < str.size() ; i++ ) {
TreeNode* rt = new TreeNode(str[i]) ;
if( !( str[i] >= 'a' && str[i] <= 'z' ) ) { //若是运算符
TreeNode* r = s.top() ;
s.pop() ;
TreeNode* l = s.top() ;
s.pop() ;
rt -> l = l ;
rt -> r = r ;
}
s.push(rt);
}
return s.top() ;
}
利用表达式树得到简化括号的中缀表达式
如果不考虑括号的冗余,后缀转中缀是很容易的,每次的表达式都加上左右括号即可。下面介绍如何得到要求的括号或者最简括号的中缀表达式
- 根据表达式树的建立可知,每个根节点的左孩子+根节点+右孩子就是一个中缀表达式的一部分,对于表达式树的根节点做此操作,就得到完整的中缀表达式。故这是一个递归的过程。
- 给中缀表达式加上适当的括号
- 对于表达式树的每个节点rt,容易递归地获取其左右子树的不加括号的中缀表达式:
- 如果 rt 节点存放的是操作数,则加入输出队列
- 如果 rt 节点存放的是运算符,则对于其左子树的中缀表达式,判断根节点 rt的运算符优先级 是否高于其左儿子存储的运算符优先级,若高则给左子树的中缀表达式加括号,否则不加。而后加入输出队列。
- 加上根节点运算符到输出队列;
- 如果 rt 节点存放的是运算符,则对于其右子树的中缀表达式,判断根节点 rt的运算符优先级 是否不低于 其右儿子存储的运算符优先级。
- 若优先级高于右儿子运算符,则加括号放入输出队列
- 若优先级等于右儿子运算符,直接加括号放入输入队列(这样可以保证运算顺序,但是不加也不影响结果);如((a/(b/c))/d) ,a*(b/c)*d,不加就是a * b / c * d ,此处可以看题目要求,加或不加,很灵活。
- 对于表达式树的每个节点rt,容易递归地获取其左右子树的不加括号的中缀表达式:
下面给出一个综合练习,囊括以上所有知识点
例题 HDU1123
题意:
给定一个中缀表达式(只有小写字母,加减乘除,左右括号),会带有很多冗余的括号,要求去除这些括号时满足以下的要求:
- 完全没有意义的括号去除;
- 加法和乘法的结合律 -> a+(b+c) 等价于 a+b+c ,a*(b*c) 等价于 a * b * c;故这种括号要去除
- 有明显的不影响运算的括号去除,如a+(b * c) -> a+b*c
思路:
先不考虑上述要求, 对于给定的中缀表达式,先转化成后缀表达式
(中缀->后缀);然后利用后缀表达式建立一颗二叉树(后缀二叉树建立表达式树);利用表达式树得到中缀表达式。
做完以上要求后,把要求的括号去除与否的规则考虑进去就可以了
#include <bits/stdc++.h>
using namespace std;
struct TreeNode {
char v ;
TreeNode* l ;
TreeNode* r ;
TreeNode( char v ):v(v),l(NULL),r(NULL) {}
};
string str , res ;
bool higher_priority( char a , char b ) { //判断优先级
return ( ( a == '*' || a == '/' ) && ( b == '+' || b == '-' ) ) ;
}
bool check_sign( TreeNode* rt ) {
return ( rt && ( rt -> v == '+' || rt -> v == '-' || rt -> v == '*' || rt -> v == '/' ) );
}
TreeNode* build() { //根据后缀表达式建立表达式树
stack<TreeNode*>s;
for( int i = 0 ; i < str.size() ; i++ ) {
TreeNode* rt = new TreeNode(str[i]) ;
if( !( str[i] >= 'a' && str[i] <= 'z' ) ) { //若是运算符,则出栈2次,分别作为rt的右,左子树
TreeNode* r = s.top() ;
s.pop() ;
TreeNode* l = s.top() ;
s.pop() ;
rt -> l = l ;
rt -> r = r ;
}
s.push(rt); //无论是操作数还是运算符 新节点均入栈
}
return s.top() ; //栈顶即根节点
}
string post_to_in( TreeNode* rt ) {
if( !rt ) return "" ;
string ans = "" ;
string ls = post_to_in( rt -> l ); //左子树的中缀表达式
string rs = post_to_in( rt -> r ); //右子树的中缀表达式
if( check_sign( rt -> l ) && higher_priority( rt -> v , rt -> l -> v ) ) { //左子树是符号且优先级低于根节点
ans += '(' ;
ans += ls ;
ans += ')' ;
} else ans += ls ; //左子树不是符号
ans += rt -> v ; //根节点
if( check_sign( rt -> r ) && !higher_priority( rt -> r -> v , rt -> v ) ) { //右子树是符号且优先级不高于根节点
if( ( rt -> v == '+' && rt -> r -> v == '-' ) || ( rt -> v == '*' && rt -> r -> v == '/' )
|| ( rt -> v == '+' && rt -> v == rt -> r -> v ) || ( rt -> v == '*' && rt -> v == rt -> r -> v ) ) {
ans += rs ; //题目要求部分(根据相同优先级不加括号,乘法,加法结合律不加括号),可灵活使用
} else { //右子树符号优先级低于根节点,给右子树的表达式加括号
ans += '(' ;
ans += rs ;
ans += ')' ;
}
} else ans += rs ;
return ans ;
}
string in_to_post() {
string res = "" ;
stack<char>op;
for( int i = 0 ; i < str.size() ; i++ ) {
if( str[i] >= 'a' && str[i] <= 'z' ) { //字母直接放到输出队列
res += str[i] ;
} else {
//栈空||栈顶为左括号当前不为右括号||当前元素为左括号||不为右括号且高于栈顶符号优先级 -> 当前符号入栈
if( op.empty() || ( op.top() == '(' && str[i] != ')' ) || str[i] == '(' || ( str[i] != ')' && higher_priority( str[i] , op.top() ) ) ) op.push(str[i]);
else {
char tmp ;
if( str[i] == ')' ) { //当前符号为')',出栈至'(',加入输出队列(左括号不加入)
while( !op.empty() ) {
tmp = op.top() ;
op.pop() ;
if( tmp == '(' ) break ;
res += tmp ;
}
} else {
while( !op.empty() ) { //出栈至栈顶符号优先级低于当前符号或'(',加入输出队列,当前符号入栈
tmp = op.top();
if( tmp == '(' || higher_priority( str[i] , tmp ) ) break ;
op.pop() ;
res += tmp ;
}
op.push(str[i]);
}
}
}
}
while( !op.empty() ) { //清空栈内符号加入输出队列
res += op.top() ;
op.pop() ;
}
return res ;
}
int main() {
ios_base::sync_with_stdio(false) ; cin.tie(0);
int T ;
cin >> T ;
while( T-- ) {
cin >> str ;
str = in_to_post(); // 中缀转后缀
TreeNode* rt = build(); //根据后缀表达式建立表达式树
res = "" ;
string res = post_to_in(rt); //利用表达式树后缀转中缀简化括号
cout << res << endl;
}
return 0 ;
}
转载:https://blog.csdn.net/FrankAx/article/details/104717408
查看评论