中缀表达式一般是先转换为后缀表达式,然后再计算的,其实我们可以边转换边计算。
/*Infix.java
使用两个栈直接求解中缀表达式,一个栈保存运算符S,一个栈保存操作数D。
例子:
a+b*c
D:a,b S:+
D:a,b,c S:+,*
D:a,bc S:+ <-- 每次取一个运算符,弹两个操作数
D:a+bc S:
a*b/c+d
D: a,b S:*
D: a*b S:/ <-- 清空优先级大于等于除号的运算符
D:a*b/c S:+
D:a*b/c+d S:
a^b^c
D:a,b S:^
D:a,b,c S:^,^ <-- 清空栈优先级大于等于幂号扫描优先级的运算符
D:a,b^c S:^
D:a^b^c S:
a*(b+c)
D:a S:*(
D:a,b S:*(+
D:a,b,c S:*(+)
D:a,b+c S:* <-- 清空操作符直到(
D:a*(b+c) S:
这几种情形,通过赋予运算符适当的优先级,可统一为:
清空栈优先级大于等于扫描优先级的运算符 (R0)
这里只是为了说明栈的应用,因此简单假设操作数是一位数字,并且没有空格,
如果扩展成BigDecimal应该不困难。
中缀表达式的计算规则,从左到右扫描一遍,在扫描过程中:
R1 如果遇到数字,则压入操作数栈D;
R2 如果遇到操作符,则根据R0弹出S中的操作符;
R3 每弹出一个操作符,就弹出D中两个操作数,并把结果压入D。
扫描结束时,可能S不为空,按规则R3。最终D中仅有的一个是结果,S 应该为空。
author: ludi 2014.04
*/
import java.util.Stack;
public class Infix
{
public static int eval(int a, int b, char op)
{
int ret = 0;
switch(op){
case '+': ret = a + b; break;
case '-': ret = a - b; break;
case '*': ret = a * b; break;
case '/': ret = a / b; break;
case '%': ret = a % b; break;
case '^': {
ret = 1;
while(b-- > 0){
ret *= a;
}
}break;
default: System.out.println("error op " + op); break;
}
return ret;
}
public static int evaluate(String infix)
{
Stack<Integer> d = new Stack<Integer>();
Stack<Symbol> s = new Stack<Symbol>();
int i, a, b;
char ch;
Symbol op, op2;
for(i = 0; i < infix.length(); ++i){
ch = infix.charAt(i);
if (Character.isDigit(ch)){
d.push(ch - '0');
}else {
op = new Symbol(ch);
while(!s.empty() && (s.peek().compareTo(op) >= 0)){
op2 = s.pop();
b = d.pop();
a = d.pop();
d.push(eval(a, b,op2.op));
}
if(ch == ')'){
s.pop(); /*弹出'('*/
}else s.push(op);
}
}
while(!s.empty()){
op2 = s.pop();
b = d.pop();
a = d.pop();
d.push(eval(a, b, op2.op));
}
System.out.printf("%s = %d d.size %d s.size %d%n", infix, d.peek(), d.size(), s.size());
return d.pop();
}
public static void main(String[] arg)
{
evaluate("1+2*3");
evaluate("2*(3+4)");
evaluate("2*3/2+1");
evaluate("2^3^2");
evaluate("(2^3)^2");
evaluate("(2^3)^2+2^3^2+2*3/2+1+2*(3+4)");
evaluate("2^(2*(3+4))");
}
}
class Symbol implements Comparable<Symbol>
{
char op;
int inputPrec; /*正在被扫描的操作符的优先级*/
int stackPrec; /*栈优先级:值越小停留在栈中时间越长*/
public Symbol (char ch){
op = ch;
switch(op){
case '+':
case '-':inputPrec = stackPrec = 1;
break;
case '*':
case '%':
case '/': inputPrec = 2;
stackPrec = 2;
break;
case '^':
inputPrec = 4;
stackPrec = 3; /*右结合的操作符扫描大于栈优先级*/
break;
case '(':
inputPrec = 5;
stackPrec = -1; /*最高的扫描优先级,最低的栈优先级*/
break;
case ')':
inputPrec = 0;
stackPrec = 0;
break;
}
}
public int compareTo(Symbol item)
{
int result;
if (stackPrec < item.inputPrec)
result = -1;
else if (stackPrec == item.inputPrec)
result = 0;
else
result = 1;
return result;
}
public char getOp()
{ return op; }
}
/*
ludi@msys ~/java
$ javac -encoding UTF-8 Infix.java && java Infix
1+2*3 = 7 d.size 1 s.size 0
2*(3+4) = 14 d.size 1 s.size 0
2*3/2+1 = 4 d.size 1 s.size 0
2^3^2 = 512 d.size 1 s.size 0
(2^3)^2 = 64 d.size 1 s.size 0
(2^3)^2+2^3^2+2*3/2+1+2*(3+4) = 594 d.size 1 s.size 0
2^(2*(3+4)) = 16384 d.size 1 s.size 0
ludi@msys ~/java
*/
这里面为了简明起见,没有做错误处理。
很好奇编译器是怎么计算常量表达式的,不知道是不是这样子呢。
附:
一些与栈有关的有意思的话题:
[1] 神奇的卡塔兰数Catalan http://blog.csdn.net/nupt123456789/article/details/22735113