前缀表达式、中缀表达式、后缀表达式

1.前缀表达式

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

运算优势

编辑
前缀表达式是一种十分有用的表达式,将中缀表达式转换为前缀表达式后,就可以只依靠出栈入栈两种简单操作完全解决中缀表达式的全部运算。
例如,(a+b)*(c+d)转换为*,+,a,b,+,c,d。
后面的前缀表达式的运算方式为:如果当前字符(或字符串)为数字或变量,则压入栈内;如果是运算符,则将栈顶两个元素弹出栈外并作相应运算,再将结果压入栈内。当前缀表达式扫描结束时,栈里的就是中缀表达式运算的最终结果。对比中缀运算的步骤,不难发现前缀运算在计算机上的优势。

求值方法

编辑
前缀表达式求值,要从右至左扫描表达式,首先从右边第一个字符开始判断,若当前字符是数字则一直到数字串的末尾再记录下来,若为运算符,则将右边离得最近的两个“数字串”作相应运算,然后以此作为一个新的“数字串”并记录下来;扫描到表达式最左端时扫描结束,最后运算的值即为表达式的值。
例如:对前缀表达式“- 1 + 2 3”求值,扫描到3时,记录下这个数字串,扫描到2时,记录下这个数字串,当扫描到+时,将+右移做相邻两数字串的运算符,记为2+3,结果为5,记录下5这个新数字串,然后继续向左扫描,扫描到1时,记录下这个数字串,扫描到-时,将-右移做相邻两数字串的运算符,记为1-5,结果为-4,此时关于这个表达式的全部运算已完成,故表达式的值为-4。

表达对照

编辑
中缀表达式转化为前缀表达式的例子:
a+b ---> +,a,b
a+(b-c) ---> +,a,-,b,c
a+(b-c)*d ---> +,a,*,-,b,c,d
a+1+3 ---> +,+,a,1,3

转换算法

编辑
(1) 首先构造一个运算符栈(也可放置括号),运算符(以括号为分界点)在栈内遵循越往栈顶优先级不降低的原则进行排列。
(2)从右至左扫描中缀表达式,从右边第一个字符开始判断:
如果当前字符是数字,则分析到数字串的结尾并将数字串直接输出。
如果是运算符,则比较优先级。如果当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),则将运算符直接入栈;否则将栈顶运算符出栈并输出,直到当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),再将当前运算符入栈。
如果是括号,则根据括号的方向进行处理。如果是向右的括号,则直接入栈;否则,遇向左的括号前将所有的运算符全部出栈并输出,遇右括号后将向左、向右的两括号一起出栈(并不输出)。
(3) 重复上述操作(2)直至扫描结束,将栈内剩余运算符全部出栈并输出,再逆缀输出字符串。中缀表达式也就转换为前缀表达式了。

 

 

实例分析

编辑
中缀表达式“1+((2+3)*4)-5”转换为前缀表达式。
中缀表达式
前缀表达式
(栈尾)运算符栈(栈顶)
说明
5
5
5,是数字串直接输出
-
5
-
-,栈内无运算符,直接入栈
5
-)
),直接入栈
4
5 4
-)
4,是数字串直接输出
*
5 4
-)*
*,栈顶是括号,直接入栈
)
5 4
- ) * )
),直接入栈
3
5 4 3
- ) * )
3,是数字串直接输出
+
5 4 3
- ) * ) +
+,栈顶是括号,直接入栈
2
5 4 3 2
- ) * )+
2,是数字串直接输出
(
5 4 3 2+
- ) *
(,与栈里最后一个)抵消,并释放它们之间的+
(
5 4 3 2+*
-
(,方法与上类同,请参考下一目录
+
5 4 3 2+*
-+
+,优先级大于等于栈顶运算符,直接入栈
1
5 4 3 2+*1
-+
1,是数字串直接输出
5 4 3 2+*1+-
扫描结束,将栈内剩余运算符全部出栈并输出
- + 1 * + 2 3 4 5
逆缀输出字符串

符号处理

编辑
对运算符的具体处理方法如下:
):直接入栈
(:遇)前,将运算符全部出栈并输出;遇)后,将两括号一起删除①
+、-:1
*、/、%:2
^:3

2.后缀表达式

 

一个表达式E的后缀形式可以如下定义:

 

(1)如果E是一个变量或常量,则E的后缀式是E本身。

 

(2)如果E是E1 op E2形式的表达式,这里op是任何二元操作符,则E的后缀式为E1'E2' op,这里E1'和E2'分别为E1和E2的后缀式。

 

(3)如果E是(E1)形式的表达式,则E1的后缀式就是E的后缀式。

 

如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+

 

(a+b)*c-(a+b)/e的后缀表达式为:

 

(a+b)*c-(a+b)/e

 

→((a+b)*c)((a+b)/e)-

 

→((a+b)c*)((a+b)e/)-

 

→(ab+c*)(ab+e/)-

 

→ab+c*ab+e/-

 

 

算法实现

编辑
将一个普通的中缀表达式转换为逆波兰表达式的一般算法是:
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
(3)若取出的字符是“(”,则直接送入S1栈顶。
(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
(5)重复上面的1~4步,直至处理完所有的输入字符
(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!

 

 

 

 

 

 

 代码实现

public class PolandNotation {
    public static void main(String[] args) {
        //创建逆波兰表达式
        //(3+4)*5-6   => 3 4 + 5 * 6 -
       /* String suffixExpression= "3 4 + 5 * 6 -";
        *//*1.将3 4 + 5 * 6 -  =》放到List中
        * 2.将list利用方法遍历list配合栈进行计算*//*
        List<String> rpnlist = getList(suffixExpression);
        System.out.println("rpnlist=" +rpnlist);
        int res = calculation(rpnlist);
        System.out.printf("计算结果: %d\n",res);*/


        /*将中缀表达式转换成后缀表达式*/
        //1+((2+3)*4)-5 ====>1 2 3 + 4 * + 5 -
        /*将字符串转换成中缀的list*/
        String expression =  "1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("后缀表达式对应的List=" + infixExpressionList);
        List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        //结果List=[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
        System.out.println("后缀表达式对应的List= " + parseSuffixExpressionList);
        //结果 List= [1, 2, 3, +, 4, *, +, 5, -]
        int calculation = calculation(parseSuffixExpressionList);
        System.out.printf("计算结果为:%d\n",calculation);//=16

    }

    /*将字符串转换成中缀的list*/
    public static List<String> toInfixExpressionList(String s){
        List<String> ls= new ArrayList<>();
        int i=0;//指针:遍历中缀字符串
         String str;//多位数拼接
        char c;//保存字符 放入c;
        do {
            //c是数字:进行字符串拼接
            if ((c=s.charAt(i))<48 || (c=s.charAt(i))>57){
                ls.add(""+c);
                i++;
            }else {//考虑多位数问题
                str="";//
                while (i<s.length()&&(c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
                    str+=c;
                    i++;
                }
                ls.add(str);
            }
        }while (i<s.length());
        return ls;
    }



    /*中缀表达式转成后缀表达式*/
    public  static  List<String> parseSuffixExpressionList(List<String> ls){
        Stack<String> s1 = new Stack<>(); //符号栈
        //第二个栈不需要pop操作,而且需要逆向输出所以利用list代替
        List<String> s2 = new ArrayList<>();
        for (String item : ls) {
            /*如果是数入s1*/
            if (item.matches("\\d+")){
                s2.add(item);
            }else if (item.equals("(")){
                s1.push(item);
            }else if (item.equals(")")){
                /*依次弹出s1栈顶的运算符,并压入s2,知道遇到左括号为止,此时将左括号丢弃*/
                while (!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();//将左括号丢弃消除小括号
            }else {
                /*当item的优先级小于等于s1栈顶的运算符的优先级
                * 1.将s1的运算符弹出并加入到s2中,依次比较新的运算符
                * 方法比较运算符的优先级的方法*/
                while (s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                //将item压入栈中
                s1.push(item);
            }
        }
        /*将s1剩余的运算符压入栈中*/
        while (s1.size()!=0){
            s2.add(s1.pop());
        }
        return s2;//存放到list,因此按顺序输出就是逆波兰表达式
    }












    /*将逆波兰表达式放入到list*/
    public  static List<String> getList(String suffixExpression){
        //字符串分割
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<>();
        for (String ele : split) {
            list.add(ele);
        }
     return list;
    }

    /*完成逆波兰运算*/
    public  static  int calculation(List<String> ls){
        //创建一个栈
        Stack<String> stack = new Stack<String>();
        for (String item : ls) {
            /*使用正则表达式*/
            if (item.matches("\\d+")){
                stack.push(item);//入栈
            }else {
                int num2 =Integer.parseInt(stack.pop());//取出pop2个数据进行运算再入栈
                int num1 =Integer.parseInt(stack.pop());
                int res = 0;
                if (item.equals("+")){
                    res = num1+num2;
                }else if (item.equals("-")){
                    res = num1-num2;
                }else if (item.equals("*")){
                    res= num1*num2;
                }else if (item.equals("/")){
                    res = num1/num2;
                }else {
                    throw  new RuntimeException("运算符号错误");
                }
                //将结果入栈
                stack.push(res+"");
            }
        }
        //留着栈中的就是计算结果
        return Integer.parseInt(stack.pop());
    }


}

 

posted @ 2020-04-21 22:08  苏先生139  阅读(2965)  评论(0编辑  收藏  举报