在数据结构中, 二叉树的遍历有三种(这里不考虑分层遍历等特殊需求): 前序遍历, 中序遍历和后序遍历. 如果将表达式看做一颗二叉树, 那么中缀表达式, 前缀表达式和后缀表达式就是这三种遍历的结果.
算数表达式本身就是中缀表达式, 也就是二叉树中序遍历的结果. 关于表达式二叉树是以运算符为根, 运算值为左右叶子.
例如表达式: 2 * 5 + 6 * (7 - 8) + 6
对应的二叉树为:
那么根据中序遍历(左中右), 前序遍历(中左右), 后序遍历(左右中)的规则可以得到对应的结果:
中序遍历: 2 * 5 + 6 * (7 - 8) + 6 , 即为表达式
前序遍历: ++*25*6-786
后序遍历:25*678-*+6+
从结果来看: 使用后序遍历可以计算表达式结果, 因为值为左右孩子, 操作符为根, 而后序遍历的输出顺序为左孩子, 右孩子, 根, 这样我们就可以读取前两个数通过第三个运算符计算结果.
2.中缀表达式转前后缀表达式
网上有一种简单的方式, 通过加括号来划分他们的作用域(就是标出以运算符为根的二叉树), 其实也是利用了计算式二叉树的特性—根为运算符, 值为左右孩子, 在加括号的过程中都是以运算符为中心, 将其左右孩子画在一起,这样将运算符提到对应的括号前就是前缀表达式, 之后就是后缀表达式.
以 2 * 5 + 6 * (7 - 8) + 6为例, 根据上面的二叉树来分析:
其实我们来看表达式中的(7 - 8) 对应的就是二叉树中的右下角以’-‘为根的子树.
操作步骤:
(1) 加括号标出每个以运算符为根的二叉树, 按照运算符的优先级对所有的运算单位加括号
(((2*5) + (6 * (7 - 8))) + 6)
(2)转换前缀表达式, 把运算符号移动到对应的括号前面
++((25) (6-(78)))6)
去掉括号即为前缀表达式: ++*25*6-786
(3)转换后缀表达式, 把运算符号移动到对应的括号后面
((25)(6(78)-)+6)+
去掉括号即为后缀表达式: 25*678-*+6+
其实我个人认为这种方式虽然简单但是操作起来很容易出错, 而且还是得需要画出对应的二叉树进行验证结果的正确性, 所以某不推荐这种方式, 个人推荐直接根据表达式, 画出对应的二叉树(因为以操作符为根节点, 所以根据中缀表达式即表达式本身就可以画出二叉树), 然后前序中序遍历即可得出对应的表达式.
3 中缀表达式转后缀表达式并计算值
如何使用程序来实现中缀表达式转后缀表达式?
程序实现思路:
设置一个运算符的栈stack,从左只有扫描中缀表达式
(1) 如果遇到数字,直接放到后缀表达式尾;
(2) 如果遇到遇到运算符
a:若此时栈空,则直接入栈;
b:循环:若栈stack不空且当前运算符优先级小于或等于栈顶运算符的优先级,则将栈顶运算符依次出栈,置于后缀表达式尾;
c:若栈stack不空且当前运算符优先级大于栈顶运算符优先级,则将此运算符直接入栈;
d:若当前运算符为’(‘直接入栈.
e:若当前运算符为’)’则将栈顶元素出栈置于后缀表达式尾, 直到遇到运算符’(‘, 注意’(’ 与 ‘)’不用置于后缀表达式尾;
反复执行(1), (2), 直到整个中缀表达式扫描完毕, 若此时栈stack不为空, 则将栈顶运算符依次出栈置于后缀表达式尾.
此时就可以实现将中缀表达式转换成后缀表达式.
如何根据中缀表达式来计算表示式的值?
程序思路:
(1)将后缀表达式的值依次入栈, 如果当前值为操作符, 弹出栈顶的两个元素根据操作符计算结果, 然后再将结果压入栈中.
(2)循环(1)的操作, 最终栈中剩下的值即为计算结果.
参考程序(此程序只能计算四则运算+”( )”):
#include
#include
#include
using namespace std;
/**
*判断当前字符是否为数字
*/
bool isNumber(char s) {
string opers = "+-*/()";
for (int i = 0; i < opers.length(); i++) {
if (s == opers.at(i)) {
return false;
}
}
return true;
}
/**
* 判断当前运算符与栈顶运算符的优先级大小
*/
bool isPriorityLow(char currOper, char topOper) {
if (currOper == '+' || currOper == '-') {
if (topOper == '*' || topOper == '/' || topOper == '+' || topOper == '-') {
return true;
}
}
if (currOper == '*' || currOper == '/') {
if (topOper == '*' || topOper == '/') {
return true;
}
}
return false;
}
/**
* 如果当前运算符优先级小于或等于栈顶运算符, 将栈顶运算符弹出加到后缀表达式尾,
* 并继续进行比较, 直到当前运算符优先级大于栈顶运算符优先级
*/
void loopPushOperator(string& s, char oper, stack<char>& operators) {
if (operators.size() > 0 && isPriorityLow(oper, operators.top())) {
s += operators.top();
operators.pop();
loopPushOperator(s, oper, operators);
}
}
/**
* 判断运算符是压入运算符栈还是加到后缀表达式尾
*/
void handleOperator(string& s, char oper, stack<char>& operators) {
switch (oper) {
case '+':
case '-':
case '*':
case '/':
//如果运算符栈为空, 直接将当前运算符压栈
if (operators.size() <= 0) {
operators.push(oper);
//如果当前运算符优先级小于等于栈顶运算符优先级, 将栈顶运算符加到后缀表达式尾
} else if (isPriorityLow(oper, operators.top())) {
loopPushOperator(s, oper, operators);
operators.push(oper);
///如果当前运算符优先级大于栈顶运算符优先级, 将当前运算符压栈
} else {
operators.push(oper);
}
break;
case '(':
operators.push(oper); //当前运算符为'('直接压栈
break;
case ')':
//将栈中元素弹出加到后缀表达式尾,直到遇到运算符"("
while (operators.top() != '(') {
s += operators.top();
operators.pop();
}
operators.pop();
break;
default:
break;
}
}
/**
* 中缀表达式转后缀表达式
*/
string infixToSuffix(string& s) {
stack<char> operators; //运算符栈
string suffix; //后缀表达式
for (int i = 0; i < s.length(); i++) {
if (isNumber(s.at(i))) { //如果是数字直接加到后缀表达式尾
suffix += s.at(i);
} else {
handleOperator(suffix, s.at(i), operators); //处理运算符
}
}
if (suffix.length() > 0) {
while (operators.size() > 0) { //将运算符栈中留有的运算符全部出栈加到后缀表达式尾
suffix += operators.top();
operators.pop();
}
return suffix;
} else {
return "";
}
}
/**
* 根据运算符,计算栈顶两个数的值,并将计算的值压栈
*/
void CalculateResult( char oper, stack<int>& tmpStack) {
if (tmpStack.size() < 2) {
return;
}
//栈是先进后出,所以先弹出的是第二个值
int secondVal = tmpStack.top();
tmpStack.pop();
int firstVal = tmpStack.top();
tmpStack.pop();
int result = 0;
switch (oper) {
case '+':
result = firstVal + secondVal;
break;
case '-':
result = firstVal - secondVal;
break;
case '*':
result = firstVal * secondVal;
break;
case '/':
result = firstVal / secondVal;
break;
default:
break;
}
tmpStack.push(result);
}
/**
* 通过后缀表达式计算结果
* 将后缀表达式依次入栈, 如果为操作符, 弹出栈中两个元素计算结果再压入栈中
*/
float getResultUseSuffix(string& s) {
if (s.length() <= 0) {
return 0;
}
stack<int> tmpStack;
for (int i = 0; i < s.length(); i++) {
if (isNumber(s.at(i))) {
tmpStack.push(s.at(i) - '0');
} else {
CalculateResult(s.at(i), tmpStack);
}
}
return tmpStack.top();
}
int main() {
string infix;
//输入的字符串不能有空格
cin>>infix;
string suffix = infixToSuffix(infix);
float result = getResultUseSuffix(suffix);
cout<<"计算结果为: "<return 0;
}
至此介绍完毕, 如果有什么问题, 希望留言指正.