后缀表达式与中缀表达式转化+括号最优化(后缀建立表达式树) + 综合练习 HDU1123 Complicated Expressions


中缀转后缀:

流程:

用一个用来存储表达式中的符号
遍历中缀表达式:
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 ,此处可以看题目要求,加或不加,很灵活。

下面给出一个综合练习,囊括以上所有知识点


例题 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 ;
}

猜你喜欢

转载自blog.csdn.net/FrankAx/article/details/104717408
今日推荐