逆波兰表达式

逆波兰表达式

题干

正常的表达式称为中缀表达式,运算符在中间,主要是给人阅读的,机器求解并不方便。

例如:3 + 5 * (2 + 6) - 1

而且,常常需要用括号来改变运算次序。

相反,如果使用逆波兰表达式(前缀表达式)表示,上面的算式则表示为:

- + 3 * 5 + 2 6 1
不再需要括号,机器可以用递归的方法很方便地求解。

为了简便,我们假设:

只有 + - * 三种运算符
每个运算数都是一个小于10的非负整数
下面的程序对一个逆波兰表示串进行求值。
其返回值为一个结构:其中第一元素表示求值结果,第二个元素表示它已解析的字符数。

public static int[] evaluate(String x){
        if(x.length() == 0) return new int[]{0,0}; 

        char c = x.charAt(0); 
        if (c >= '0' && c <= '9') 
            return new int[]{c - '0', 1};
            

        int[] v1 = evaluate(x.substring(1));
        int[] v2 = __________________________________;//填空位置
      
        
        int v = Integer.MAX_VALUE;
        if (c == '+') v = v1[0] + v2[0];
        if (c == '*') v = v1[0] * v2[0];
        if (c == '-') v = v1[0] - v2[0];


        return new int[]{v, 1 + v1[1] + v2[1]};
        
    }

代码

public class _06逆波兰表达式 {
//	int[]数组中
//	其中第一元素表示求值结果,第二个元素表示它已解析的字符数。
	public static int[] evaluate(String x){
        if(x.length() == 0) return new int[]{0,0}; //

        char c = x.charAt(0); //取到首字符
        if (c >= '0' && c <= '9') //如果是数字
            return new int[]{c - '0', 1};
            
//        如果是下面的情况,说明是字符,虽然此处没有else但是所表达的意思就是上面if之外的情况,因为上面的if没有执行的话才能够运行到下一行代码
        int[] v1 = evaluate(x.substring(1));//这里是除去第一个字符不要的其他部分,第一个字符是运算符
        int[] v2 = evaluate(x.substring(1+v1[1]));//填空位置,猜测是截取上一次没有处理到的部分
//      v1[1]处理v1消耗的字符数量
        //
        int v = Integer.MAX_VALUE;
        if (c == '+') v = v1[0] + v2[0];
        if (c == '*') v = v1[0] * v2[0];
        if (c == '-') v = v1[0] - v2[0];


        return new int[]{v, 1 + v1[1] + v2[1]};
        
    }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(evaluate("-+3*5+261")[0]);

	}

}

总结

1.观察代码,也是从第一个字符开始:如果是数字,那么直接返回;如果不是,说明遇到了一个操作符,那么v1从这个操作符的后一位字符开始递归,直到遇到数字返回;然后v2再进行一些操作;最后把v1和v2对应的值对于回溯时最近的那个操作符进行运算。那么很显然,v2是从v1结束递归后的后一个偏移量开始递归,也就是v1开始递归前的位置往后数n个(结束时v1的偏移量)。

这个题可以用最后的算式倒推,最后的式子是3+5*(2+6)-1。

思考的时候不要去想从哪又回到哪里了,然后又到哪去了×××!!!

只思考v1和v2是一个已经算出来的结果,那么v2[0]一定是等于1的

那么 if(c=='-') v = v1[0] - v2[0];中的

v1[0] 就是3+5*(2+6)计算出的结果,v2[0]就应该是1。此时要利用一下v1的长度x.substring(1),在v1的偏移量上再加上一个v1的1,v1[1]表示在处理v1这个式子结果时消耗的字符的数量

(1)将表达式简化即"3+5"变成逆波兰表达式为"+35",代入程序中思考。
(2)根据如下代码:
    if(c=='+') v = v1[0] + v2[0];
    if(c=='*') v = v1[0] * v2[0];
    if(c=='-') v = v1[0] - v2[0];
可推测v1[0]=3,v2[0]=5;
(3)根据题意evaluate函数返回一个数组,其中第一元素表示求值结果,第二个元素表示它已解析的字符数
则int[] v1 = evaluate(x.substring(1));即执行evaluate("35");v1[0]=3;
(4)那么利用填空位置,我们需要求出v2[0]=5即可,我们马上想到答案:
int[] v2 = evaluate(x.substring(2));即执行evaluate("5");v2[0]=5;
(5)在main函数中用如下代码进行测试:
        int[] arr = evaluate("+35");
        System.out.println(arr[0]);
结果为8,没有问题。
(6)但我们将题目中表达式"-+3*5+261"代入进行测试:
        int[] arr = evaluate("-+3*5+261");
        System.out.println(arr[0]);
结果为40,而3 + 5 * (2 + 6) - 1 = 42,显然结果不对
(7)继续分析,我们可以得到第一个减号左边为"+3*5+26",右边为"1"。
则int[] v1 = evaluate(x.substring(1));   v1[0]为"+3*5+26"的结果,v1[1]为解析的字符数=7。
(8)那么填空位置,我们需要求出v2[0]=1即可,我们可以得到答案:
int[] v2 = evaluate(x.substring(8));即int[] v2 = evaluate(x.substring(v1[1]+1));
所以答案为:
evaluate(x.substring(v1[1]+1))

3.前缀表达式

这是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。

比如本题- + 3 * 5 + 2 6 1 = 3+5*(2+6)-1

4.每个运算数都是一个小于10的非负整数

比如输入字符串为:-+3*5+261,这里的261就可以区分出2 6 1三个数

posted @ 2021-03-10 20:06  记录学习Blog  阅读(222)  评论(0编辑  收藏  举报