想当年学数据结构的时候,一直觉得这个是我一辈子都搞不懂的一个东西。现在看看。。。还挺简单的。。。
重点在于如何解析由括号、运算符和数字组成的字符串,并按照正确的顺序完成各种初级算术操作。利用了两个栈(一个用于保存运算符,一个用于保存操作数)可以十分简单的完成这个任务。
表达式由括号、运算符和操作数(数字)组成。我们根据以下4中情况从左到右逐个将这些实体送入栈处理:
- 将操作数压入操作数栈
- 将运算符压入运算符栈
- 忽略左括号
- 在遇到右括号时,弹出一个运算符,弹出所需数量的操作数,并将运算符和操作数的运算结果压入操作数栈。
在处理完最后一个右括号时,操作数栈上只会有一个值,它就是表达式的值。每当算法遇到一个被括号包围并由一个运算符和两个操作数组成的子表达式时,它都将运算符和操作数的计算结果压入操作数栈。这样的结果就好像在输入中用这个值代替了该子表达式,因此用这个值代替子表达式得到的结果和原表达式相同。我们可以反复应用这个规律并得到一个最终值。
例如计算一个表达式,逐步计算子表达式如下:
- (1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
- (1 + ( 5 * (4 * 5 ) ) )
- (1 + ( 5 * 20 ) ) )
- (1 + 100 )
- 101
Dijkstra 的双栈算术表达式求值算法:
import java.util.Scanner;
import java.util.Stack;
public class Evaluate {
public static void main(String[] args) {
Stack<String> ops = new Stack<>(); // 运算符栈
Stack<Double> vals = new Stack<>(); // 运算数栈
Scanner sc = new Scanner(System.in);
String str = sc.next();
for(int i = 0;i < str.length();i++){
String subStr = str.substring(i,i+1);
// 读取字符,如果是运算符则压入操作符栈
if(subStr.equals("("));
else if(subStr.equals("+")) ops.push(subStr);
else if(subStr.equals("-")) ops.push(subStr);
else if(subStr.equals("*")) ops.push(subStr);
else if(subStr.equals("/")) ops.push(subStr);
else if(subStr.equals(")")){
// 如果遇到右括号,则弹出运算符和计算结果
String op = ops.pop();
Double val = vals.pop();
if(op.equals("+")) val = vals.pop() + val;
else if (op.equals("-")) val = vals.pop() - val;
else if (op.equals("*")) val = vals.pop() * val;
else if (op.equals("/")) val = vals.pop() / val;
vals.push(val);
}
else { // 遇到运算数则压入运算数栈
vals.push(Double.parseDouble(subStr));
}
}
System.out.println(vals.pop());
}
}
这段 Stack 的用例使用了两个栈来计算表达式的值。
运行效果:
>>> (1+((2+3)*(4*5)))
101.0