这是一道面试时遇到的题目,那时没答出来,之后查了相关资料。在这里将自己对于这个问题的理解做下记录,之后便于查阅。
题目:给定一个算数表达式,用程序进行算数表达式求值。比如求解1×2+6×(3-4÷5)。
思路:先用运算符栈将常见的中缀表达式转化为后缀表达式(逆波兰表达式)。再通过操作数栈进行后缀表达式的求值。
后缀表达式:操作数在前,运算符在后,且运算符按优先级从高到低排序。
运算符有+,-,×,÷,(,)。其中‘×’,‘÷’的优先级高于‘+’,‘-’的优先级;‘(’的优先级大于之前所有运算符的优先级,小于之后所有运算符的优先级。‘)’无所谓优先级,碰到’)‘时需要将运算符栈中’(‘之上的运算符弹出来,最后将’('也弹出来。其中,同优先级的运算符,先出现运算符的优先级高。
-
将中缀表达式转化为后缀表达式
下面描述下将常见的中缀表达式转换成后缀表达式的做法:从前到后扫描算数表达式。如果是数字,直接添加到后缀表达式的最后。如果是运算符且运算符栈为空时,将运算符添加到运算符栈中。如果运算符栈不为空,需要进行多情况处理
(1)如果是’+’,’-‘运算符,它们的优先级只比’(‘高,所以需要不断弹出运算符栈中的运算符,直到栈为空或者栈顶为’('运算符;
(2)如果是’×’,‘÷’运算符,它们的优先级比’(‘、’+’、’-‘高,所以需要不断弹出运算符栈中的运算符,直到栈为空或者栈顶为’(’、’+‘,’-'运算符;
(3)如果是’('运算符,直接入栈;
(4)如果是’)‘运算符,不断弹出运算符,直到栈顶为’(’,将’('运算符弹出;
-
求解后缀表达式
得到后缀表达式之后,通过操作数栈就可以计算后缀表达式的值。具体做法为:从头到尾扫描后缀表达式,如果是操作数,入栈;如果是运算符,弹出两个操作数,先弹出的为右操作数,后弹出的为左操作数,计算完之后将结果入栈。扫描完之后,栈中的结果即为表达值的值。
下面是具体的代码(Java实现):
import java.util.Scanner;
import java.util.Stack;
/**
* 数学表达式求值
* 思路:
* 先用栈转换成后缀表达式,然后用栈对后缀表达式求值
* @Author CHT
*/
public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
String expression = null;
while (true){
expression = scanner.next();
System.out.println(String.format("%.2f",new Main().resultOfMathematicalExpression(expression)));
}
}
/**
* 根据后缀表达式计算结果
* @param mathematicalExpression
* @return
*/
private double resultOfMathematicalExpression(String mathematicalExpression){
String postExpression = postMathematicalExpression(mathematicalExpression);
String[] strings = postExpression.split("#");
Stack<Double> operandStack = new Stack<>();
double operandLeft = 0,operandRight = 0;
for (String string:strings) {
switch (string){
case "+":{
operandRight = operandStack.pop();
operandLeft = operandStack.pop();
operandStack.push(operandLeft+operandRight);
break;
}
case "-":{
operandRight = operandStack.pop();
operandLeft = operandStack.pop();
operandStack.push(operandLeft-operandRight);
break;
}
case "*":{
operandRight = operandStack.pop();
operandLeft = operandStack.pop();
operandStack.push(operandLeft*operandRight);
break;
}
case "/":{
operandRight = operandStack.pop();
operandLeft = operandStack.pop();
operandStack.push(operandLeft/operandRight);
break;
}
default:
operandStack.push(Double.parseDouble(string));
}
}
return operandStack.pop();
}
/**
* 将中缀表达式转换成后缀表达式。用'#'分隔
* @param mathematicalExpression
* @return
*/
private String postMathematicalExpression(String mathematicalExpression){
//后缀表达式
StringBuilder postExpression = new StringBuilder();
//操作符栈
Stack<Character> operatorStack = new Stack<>();
//数字直接添加到后缀表达式,运算符按照优先级从高到低排序
for (int i = 0; i < mathematicalExpression.length(); i++) {
//0~9直接添加到后缀表达式
if (mathematicalExpression.charAt(i)>='0' && mathematicalExpression.charAt(i)<='9'){
postExpression.append(mathematicalExpression.charAt(i));
}//'+' '-' 运算符的优先级只比 '(' 高
else if (mathematicalExpression.charAt(i)=='+' || mathematicalExpression.charAt(i)=='-'){
postExpression.append('#');
if (operatorStack.empty() || operatorStack.peek()=='('){
operatorStack.push(mathematicalExpression.charAt(i));
}else {
while (!operatorStack.empty() && operatorStack.peek()!='('){
postExpression.append(operatorStack.pop()).append('#');
}
operatorStack.push(mathematicalExpression.charAt(i));
}
}//'*' '/'的优先级比 '+' '-' '('高
else if (mathematicalExpression.charAt(i)=='*' || mathematicalExpression.charAt(i)=='/'){
postExpression.append('#');
if (operatorStack.empty() || operatorStack.peek()=='(' || operatorStack.peek()=='+' || operatorStack.peek()=='-'){
operatorStack.push(mathematicalExpression.charAt(i));
}else {
while (!operatorStack.empty() && operatorStack.peek()!='(' && operatorStack.peek()!='+' && operatorStack.peek()!='-'){
postExpression.append(operatorStack.pop()).append('#');
}
operatorStack.push(mathematicalExpression.charAt(i));
}
}// '('的优先级比前面的运算符都高,比后面的运算符优先级低
else if (mathematicalExpression.charAt(i)=='('){
operatorStack.push(mathematicalExpression.charAt(i));
}// 碰到')',将'('之前的运算符弹出,最后将'('弹出
else if (mathematicalExpression.charAt(i)==')'){
postExpression.append('#');
while (operatorStack.peek()!='('){
postExpression.append(operatorStack.pop()).append('#');
}
postExpression.deleteCharAt(postExpression.length()-1);
operatorStack.pop();
}
}
while (!operatorStack.empty()){
postExpression.append("#").append(operatorStack.pop());
}
return postExpression.toString();
}
参考: