c2java 第5篇 栈和中缀表达式的直接计算

中缀表达式一般是先转换为后缀表达式,然后再计算的,其实我们可以边转换边计算。

/*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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
中缀表达式是我们常见的数学表达式的一种表示方法,例如"3 + 4 * 2 - 5"。但是在计算机中,中缀表达式并不方便进行计算,因此我们需要将其转换为后缀表达式(逆波兰表达式)来求值。 我们可以使用来处理中缀表达式的求值。具体的方法如下: 1. 创建一个空和一个空列表来存储中间和最终结果。 2. 遍历中缀表达式中的每一个字符。 3. 如果遇到操作数(数字),直接将其添加到列表中。 4. 如果遇到运算符,分以下几种情况处理: - 如果为空,或者顶为左括号,则直接将运算符入。 - 如果顶为运算符,并且顶运算符的优先级大于当前运算符,则将顶运算符出,并加入到列表中,然后继续比较当前运算符与新的顶运算符的优先级。 - 最后将当前运算符入。 5. 如果遇到左括号,直接将其入。 6. 如果遇到右括号,则将中的运算符依次出,加入到列表中,直到遇到左括号为止。 7. 遍历完中缀表达式后,将中剩余的运算符依次出,加入到列表中。 8. 最后,遍历列表中的元素进行计算。如果遇到操作数,直接;如果遇到运算符,从中弹出两个操作数,并进行相应的运算,然后将运算结果重新入。 9. 最终,中只剩下一个数,即为中缀表达式的求值结果。 通过上述方法,我们可以实现对中缀表达式的求值,而且时间复杂度为O(n),其中n为表达式的长度。这种方法利用了的特性,在处理运算符时将其按照优先级依次出,确保了运算的正确顺序,并得到了最终的结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值