前缀、中缀、后缀表达式(逆波兰表达式)以及逆波兰表达式的整数计算器实现

中缀表达式

中缀表达式就是通常我们在数学中看到的表达式,是一种人类易读、易理解的表达式,更能符合人类通常习惯性思维的表达式

如:( 3 * 4 ) + 20 - 50 / 2

缺点:对于计算机很难操作

前缀表达式

顾名思义,符号在前,数字在后,成为前缀

如:- * + 3 4 5 6,其对应的中缀表达式为:( 3 + 4 ) * 5 - 6

前缀表达式求值思路:

从右向左扫描表达式,遇到数字,直接将数字压入一个栈中,遇到运算符时,弹出栈顶的两个数,用运算符对他们进行算数运算,并将计算结果再次压入栈中,重复这个过程直到扫描完成整个表达式,最后留在栈中的数字即为最终的运算结果

例如之举例的前缀表达式:- * + 3 4 5 6

  1. 首先右边全是数字,直接按照从右至左的顺序压入栈中
  2. 继续扫描,遇到运算符 + 号,此时弹出栈顶的 3 和 4,进行加法运算,并将结果 7 放入栈中
  3. 接下来是运算符 * 号,此时弹出栈顶的 7 和 5,进行乘法运算,并将结果 35 放入栈中
  4. 最后是运算发 - 号,此时淡出栈顶的 35 和 6,进行减法运算,注意:此时要按照栈先入后出的原则,35 后入栈,比 6 先出,所以,35 - 6 = 29,最后将 29 放入栈中,此时表达式扫描完毕,栈中最后的数字即为结果

后缀表达式(逆波兰表达式)

算数表达式解释:百度百科

后缀表达式求值思路:

遇到数字,将数字压入栈中,遇到符号时,弹出栈顶两个数用改运算符做计算(计算顺序:次顶元素 运算符 栈顶元素),然后将计算结果入栈,重复以上操作直到表达式扫描完成,最后栈中的数字即为表达式结果。

如:( 3 + 4 ) * 5 - 6 的后缀表达式为 3 4 + 5 * 6 -

  1. 将 3 和 4 压入栈中
  2. 扫描到 + 号,弹出 4 和 3(4为栈顶元素,3为次顶元素),计算加法,结果为 7,压入栈中
  3. 扫描到 5,压入栈中
  4. 扫描到 *,弹出 5 和 7,计算乘法,结果 35 压入栈中
  5. 扫描到 6,压入栈中
  6. 扫描到 -,弹出 6,计算 35 - 6,结果 29 压入栈中

逆波兰计算器实现

支持小括号和多位整数简单计算的计算器

实现思路答题分为两部分:

  1. 中缀表达式转后缀表达式
  2. 根据后缀表达式计算出结果

中缀表达式转后缀表达式规则

中缀转后缀表达式规则我们不需要更多的探讨,因为属于发明算法,我们现在只关心怎么用这个算法就好了,规则如下:

  • 1.初始化两个栈:运算符栈 s1 和结果栈 s2
  • 2.从左至右扫描表达式
  • 3.遇到数字时,直接压入 s2
  • 4.遇到符号时,需要比较其与 s1栈顶符号的优先级,满足如下规则
4.1 如果s1为空,或者s1栈顶运算符为 ( 直接将此符号入栈s1
4.2 如果该符号比栈顶运算符优先级高 直接将此符号入栈s1
4.3 如果不满足 4.1 和 4.2 规则 将s1栈顶的运算符弹出压入s2中,再次根据 4.1 判断
  • 5.遇到括号时,满足如下规则
如果是 ( 直接压入s1
如果是 ) 依次弹出s1栈顶的运算符,并将这些运算符压入s2,直到遇到 ( 为止,此时将这一对 () 丢弃
  • 6.重复 2 到 5的判断,直到表达式最右边
  • 7.将s1中剩余的运算符依次弹出并压入s2
  • 8.依次弹出s2中的元素,并按照结果的倒叙排列,即为得到的后缀表达式

代码实现

package com.leolee.dataStructure.stack;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @ClassName ReversePolishNotation
 * @Description: 逆波兰表达式
 * @Author LeoLee
 * @Date 2020/9/17
 * @Version V1.0
 **/
public class ReversePolishNotation {

    /*
     * 功能描述: <br> 对逆波兰表达式进行“格式处理”,转为List
     * 〈〉
     * @Param: [reversePolishNotationExpressions]
     * @Return: java.util.List<java.lang.String>
     * @Author: LeoLee
     * @Date: 2020/9/17 18:08
     */
    public static List<String> getListString(String reversePolishNotationExpressions) {

        String[] stringArray = reversePolishNotationExpressions.split(" ");
        List<String> list = new ArrayList<String>();
        for (String s : stringArray) {
            list.add(s);
        }
        return list;
    }

    /*
     * 功能描述: <br> 计算波兰表达式
     * 〈〉
     * @Param: [list] 后最表达式对应的list
     * @Return: int
     * @Author: LeoLee
     * @Date: 2020/9/17 18:33
     */
    public static int calculate (List<String> list) {

        Stack<String> stack = new Stack<String>();

        for (String item : list) {
            //判断字符是数字还是符号
            if (item.matches("\\d+")) {
                //匹配数字直接入栈
                stack.push(item);
            } else {
                //如果是符号,贼需要弹出两个元素进行运算
                int num2 = Integer.valueOf(stack.pop());//栈顶元素
                int num1 = Integer.valueOf(stack.pop());//次顶元素
                int result = 0;
                switch (item.charAt(0)) {
                    //计算顺序:次顶元素 运算符 栈顶元素
                    case '+' :
                        result = num1 + num2;
                        stack.push(String.valueOf(result));
                        break;
                    case '-' :
                        result = num1 - num2;
                        stack.push(String.valueOf(result));
                        break;
                    case '*' :
                        result = num1 * num2;
                        stack.push(String.valueOf(result));
                        break;
                    case '/' :
                        result = num1 / num2;
                        stack.push(String.valueOf(result));
                        break;
                    default:
                        throw new RuntimeException("不支持的运算符");
                }
            }
        }
        return Integer.valueOf(stack.pop());
    }

    /*
     * 功能描述: <br> 中缀表达式转换为 list,主要是为了处理多位数
     * 〈〉
     * @Param: [normalExpression] 中缀表达式
     * @Return: java.util.List<java.lang.String>
     * @Author: LeoLee
     * @Date: 2020/9/17 21:54
     */
    public static List<String> normalExpressionToList (String normalExpression) {

        List<String> list = new ArrayList<String>();
        char c;//用于转存每一个字符
        String str;//用于多位数拼接

        for (int i = 0; i < normalExpression.length(); i++) {
            c = normalExpression.charAt(i);
            //判断是数字还是符号
            if (String.valueOf(c).matches("\\d+")) {//数字处理
                str = "";
                //0~9 对应的 ASC码为 48~57,不在该范围则跳出循环
                while (i < normalExpression.length() && (c = normalExpression.charAt(i)) >= 48 && (c = normalExpression.charAt(i)) <= 57) {
                    str += c;
                    i++;
                }
                i--;
                list.add(str);
            } else {//符号处理
                list.add(String.valueOf(c));
            }
        }
        return list;
    }


    /*
     * 功能描述: <br> 中缀表达式字符list 转 后缀表达式字符list
     * 〈〉
     * @Param: [sourceList] 中缀表达式字符list
     * @Return: java.util.List<java.lang.String> 后缀表达式字符list
     * @Author: LeoLee
     * @Date: 2020/9/17 22:10
     */
    public static List<String> getReversePolishNotationList (List<String> sourceList) {

        Stack<String> s1 = new Stack<String>();//符号栈
        //Stack<String> s2 = new Stack<String>();//结果栈
        //由于s2栈在整个处理过程中不需要pop(),只会压入元素,最后取出结果还要倒叙输出,所以在这里使用List
        List<String> s2 = new ArrayList<String>();

        for (String item : sourceList) {
            if (item.matches("\\d+")) {//如果是一个数字则入s2
                s2.add(item);
            } else if ("(".equals(item)) {//如果是 (,直接压入s1
                s1.push(item);
            } else if (")".equals(item)) {//如果是 )	依次弹出s1栈顶的运算符,并将这些运算符压入s2,直到遇到 ( 为止,此时将这一对 () 丢弃
                while (!s1.peek().equals("(")) {//peek()只查看栈顶元素,并不弹出
                    s2.add(s1.pop());
                }
                s1.pop();//丢弃s1中的 (
            } else {
                //4.1 如果s1为空,或者s1栈顶运算符为 (	直接将此符号入栈s1
                //4.2 如果该符号比栈顶运算符优先级高	直接将此符号入栈s1
                //4.3 如果不满足 4.1 和 4.2 规则	将s1栈顶的运算符弹出压入s2中,再次根据 4.1 判断
                while (s1.size() != 0 && OperatorPriority.getValue(s1.peek()) >= OperatorPriority.getValue(item)) {//即4.3的判断条件,item小于等于栈顶运算符的优先级
                    s2.add(s1.pop());
                }
                s1.push(item);
            }
        }
        //将s1中剩余的运算符依次弹出并压入s2
        while (s1.size() != 0) {
            s2.add(s1.pop());
        }
        return s2;
    }

    //运算符优先级枚举类
    enum OperatorPriority {
        DIVIDE("/", 2),
        MULTIPLY("*", 2),
        ADD("+", 1),
        SUBTRACT("-", 1)
        ;

        private String key;
        private int val;

        private OperatorPriority (String key, int val) {
            this.key = key;
            this.val = val;
        }

        public static int getValue (String key) {

            ReversePolishNotation.OperatorPriority[] operations = ReversePolishNotation.OperatorPriority.values();
            for (int i = 0; i < operations.length; i++) {
                if (operations[i].getKey().equals(key)) {
                    return operations[i].getVal();
                }
            }
            //匹配不到则返回0
            return 0;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public int getVal() {
            return val;
        }

        public void setVal(int val) {
            this.val = val;
        }
    }

    public static void main(String[] args) {

        String normalExpression = "1+(30*3)/2+23-2";

        List<String> list = ReversePolishNotation.normalExpressionToList(normalExpression);
        List<String> reversePolishNotationList = ReversePolishNotation.getReversePolishNotationList(list);
        int result = ReversePolishNotation.calculate(reversePolishNotationList);
        System.out.println("1+(30*3)/2+23-2=" + result);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_25805331/article/details/108652008