0%

简单计算器实现-逆波兰表达式

简单计算器的实现-逆波兰表达式

一般也就是分为两步

  1. 将中缀表达式 解析为 后缀(前缀)表达式
  2. eval 计算后缀表达式解析 出 结果
  1. 中缀表达式

一般的算数表达式, 操作符以中缀形式出现在操作数之间

  1. 前缀表达式

前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面.
为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为波兰式.

  1. 后缀表达式

后缀表达式指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,
严格从左向右进行(不再考虑运算符的优先规则). 也称为 逆波兰表达式

中缀表达式: 1-(2+3)
前缀表达式: 1 2 3 + -
后缀表达式: - 1 + 2 3

中缀表达式 转 前缀表达式

((a+(b*c))+(((d*e)+f)*g)

前缀表达式
++a*bc*+*efg

同样是使用栈的方式解析, 前缀表达式的解析 和 后缀表达式的解析 方式相反, 从右向左

  1. 解析 中缀表达式

从右至左解析中缀表达式

  1. 遇到数字 直接输出
  2. 遇到操作符

2.1 ‘)’直接入栈
2.2 ‘(‘ 将符号栈中元素移除并输出, 直到遇到右括号)只移除,不输出
2.3 运算符: 将符号栈中的元素移除出栈并输出, 直到遇到比当前符号优先级更高的符号或者).
将当前符号入栈
3. 扫描完后, 将栈中剩余符号依次输出

for example
下面以a+b*c+(d*e+f)*g为例子来看看转换过程:
面在描述栈的情况是直接用文字描述了,由左到右为栈底到栈顶.空表示栈空

1. 从后向左遍历表达式: g

输出:  g 
符号栈:

2. 从后向左遍历表达式: *

输出:  g 
符号栈: *

3. 从后向左遍历表达式: )

输出:  g 
符号栈: )*

4. 从后向左遍历表达式: f

输出:  fg 
符号栈: )*

5. 从后向左遍历表达式: +

输出:  fg 
符号栈: +)*

6. 从后向左遍历表达式: e

输出:  efg 
符号栈: +)*

7. 从后向左遍历表达式: *

输出:  efg 
符号栈: *+)*

8. 从后向左遍历表达式: d

输出:  defg 
符号栈: *+)*

9. 从后向左遍历表达式: (

输出:  +*defg 
符号栈: *

10. 从后向左遍历表达式: +

输出:  *+*defg 
符号栈: +

11. 从后向左遍历表达式: c

输出:  c*+*defg 
符号栈: +

12. 从后向左遍历表达式: *

输出:  c*+*defg 
符号栈: *+


13. 从后向左遍历表达式: b

输出:  bc*+*defg 
符号栈: *+

14. 从后向左遍历表达式: +

输出:  *bc*+*defg 
符号栈: ++

15. 从后向左遍历表达式: a

输出:  a*bc*+*defg 
符号栈: ++

16. 将符号栈中剩余的 符号压入 结果集

输出:  ++a*bc*+*defg 
符号栈: 
  1. 解析 前缀表达式

/**
 * 基本计算器3 </br>
 *
 * @author majunmin
 * @description
 * @datetime 2021-09-25 19:47
 * @since
 */
public class LeetCode_0772 implements Solution {


    @Override
    public int calculate(String s) {
        List<String> pe = parsePE(s);
        int res = evalPE(pe);
        return res;
    }

    private List<String> parsePE(String s) {
        List<String> list = new LinkedList<>();
        Deque<String> opStack = new LinkedList<>();
        final char[] chars = s.toCharArray();
        int num = -1;
        for (int i = chars.length - 1; i >= 0; i--) {
            char c = chars[i];
            if (c == ' ') {
                continue;
            }
            if (c >= '0' && c <= '9') {
                if (num == -1) {
                    num = c - '0';
                } else {
                    num = num + (c - '0') * 10;
                }
            } else {
                if (num != -1) {
                    list.add(0, String.valueOf(num));
                    num = -1;
                }
                if (c == ')') {
                    opStack.push(")");
                } else if (c == '(') {
                    while (!Objects.equals(opStack.peek(), ")")) {
                        list.add(0, opStack.pop());
                    }
                    opStack.pop();
                } else {
                    // 运算符
                    String op = String.valueOf(c);
                    while (!opStack.isEmpty()) {
                        if (Objects.equals(opStack.peek(), ")")) {
                            break;
                        }
                        if (getPriority(op) >= getPriority(opStack.peek())) {
                            break;
                        }
                        list.add(0, opStack.pop());
                    }
                    opStack.push(op);
                }
            }
        }
        if (num != -1) {
            list.add(0, String.valueOf(num));
        }
        while (!opStack.isEmpty()) {
            list.add(0, opStack.pop());
        }

        return list;
    }

    /**
     * 解析 波兰表达式
     * ++a*bc**+*efg
     *
     * @param pe
     * @return
     */
    private int evalPE(List<String> pe) {
        Deque<String> stack = new LinkedList<>();
        // 从右向左遍历
        for (int i = pe.size() - 1; i >= 0; i--) {
            String curStr = pe.get(i);
            if (curStr.charAt(0) >= '0' && curStr.charAt(0) <= '9') {
                stack.push(curStr);
            } else {
                int num = 0;
                int num1 = parseInt(stack.pop());
                int num2 = parseInt(stack.pop());
                switch (curStr) {
                    case "+":
                        num = num1 + num2;
                        break;
                    case "-":
                        num = num1 - num2;
                        break;
                    case "*":
                        num = num1 * num2;
                        break;
                    case "/":
                        num = num1 / num2;
                        break;
                    default:
                        throw new IllegalArgumentException("Unexcept error.");
                }
                stack.push(String.valueOf(num));
            }
        }
        return parseInt(stack.pop());
    }

    /**
     * 将中最表达式转化为  逆波兰表达式
     * 1. 遇到数字直接 放入 list
     * 2. 遇到 符号
     * 2.1  `(` 直接放入
     * 2.2  `)` 将栈中 所有的字符 pop 放入到结果集
     * 2.3  `符号`
     * 2.3.1 符号优先级 <= 栈顶元素优先级,直接放入
     * 2.3.2 符号优先级 > 栈顶元素优先级,将栈中元素pop 并放入结果集, 直到遇到 `(` 或者 栈 为空
     *
     * @param s
     * @return
     */
    private List<String> parseRPE(String s) {
        List<String> rpe = new ArrayList<>();
        Deque<String> stack = new LinkedList<>();
        int num = -1;
        for (char c : s.toCharArray()) {
            if (c == ' ') {
                continue;
            }
            // 是一个数字
            if (c >= '0' && c <= '9') {
                if (num == -1) {
                    num = c - '0';
                } else {
                    num = num * 10 + (c - '0');
                }
            } else {
                //判断数字
                if (num != -1) {
                    rpe.add(String.valueOf(num));
                    num = -1;
                }

                if (c == '(') {
                    stack.push(String.valueOf(c));
                } else if (c == ')') {
                    while (!stack.isEmpty()) {
                        if (!"(".equals(stack.peek())) {
                            rpe.add(stack.pop());
                        } else {
                            stack.pop();
                            break;
                        }
                    }
                } else {
                    // + - * /
                    while (!stack.isEmpty()) {
                        if ("(".equals(stack.peek())) {
                            break;
                        }
                        if (getPriority(String.valueOf(c)) > getPriority(stack.peek())) {
                            break;
                        }
                        rpe.add(stack.pop());
                    }
                    stack.push(String.valueOf(c));
                }
            }
        }
        if (num != -1) {
            rpe.add(String.valueOf(num));
        }
        while (!stack.isEmpty()) {
            rpe.add(stack.pop());
        }
        return rpe;
    }

    private int getPriority(String op) {
        switch (op) {
            case "+":
            case "-":
                return 1;
            case "*":
            case "/":
                return 2;
            default:
                throw new IllegalArgumentException("Unexpected value: " + op);
        }
    }
}

中缀表达式 转 后缀表达式

((a+(b*c))+(((d*e)+f)*g)

后缀表达式
abc*+de*f+g*+

使用栈的方式解析,解析步骤:

从左到右扫描中缀表达式,

  1. 遇到数字直接输出
  2. 遇到运算符

2.1 ‘(‘ 直接入栈
2.2 ‘)’ 将符号栈中的元素移除出栈并输出, 直到 (, ( 只出栈,不输出
2.3 +-*/: 将符号栈中的元素移除出栈并输出, 直到遇到比当前符号优先级更低的符号或者’(‘.
将当前符号入栈
3. 扫描完后, 将栈中剩余符号依次输出

for example:
下面以a+b*c+(d*e+f)*g为例子来讲讲计算机的转换过程下.
面在描述栈的情况是直接用文字描述了,由左到右为栈底到栈顶.空表示栈空


1. 由左向右遍历表达式,首先遇到a,直接将其输出.

此时输出为:a
栈的情况为:空

2. 继续遍历,遇到+,将其放入栈中.

此时输出为:a
栈的情况为:+

3. 继续遍历,遇到b,直接将其输出.

此时输出为:ab
栈的情况为:+

4. 继续遍历,遇到*,因为*的优先级大于栈顶的+,所以将*放入栈内。

此时输出为:ab
栈的情况为:+*

5. 继续遍历,遇到c,直接将其输出.

此时输出为:abc
栈的情况为:+*

6. 继续遍历,遇到+,因为+的优先级低于栈顶的*, 故将*弹出; 然后新的栈顶元素的+与这个+优先级相同, 故也要弹出现在栈顶的+;然后栈空了,将现在这个+放入栈中

此时输出为:abc*+
栈的情况为:+

7. 继续遍历,遇到(,直接将其放入栈中,不遇到)不会将(弹出

此时输出为:abc*+
栈的情况为:+(

8. 继续遍历,遇到d,直接将其输出.

此时输出为:abc*+d
栈的情况为:+(

9. 继续遍历,遇到*,因为栈顶为(,不遇到)不将(弹出,故直接将*放入栈中

此时输出为:abc*+d
栈的情况为:+(*

10. 继续遍历,遇到e,直接将其输出。

此时输出为:abc*+de
栈的情况为:+(*

11. 继续遍历,遇到+,因为+比栈顶*的优先级低,故将*弹出;新的栈顶元素为(,不遇到)不弹出(,故将+放入栈中。

此时输出为:abc*+de*
栈的情况为:+(+

12. 继续遍历,遇到f,直接将其输出。

此时输出为:abc*+de*f

栈的情况为:+(+

13. 继续遍历,遇到),直接将栈中元素依次弹出并输出直到遇到(为止,  注意:(弹出但不输出

此时输出为:abc*+de*f+
栈的情况为:+

14. 继续遍历,遇到*,因为*的优先级大于栈顶元素+的优先级,故直接将*入栈。

此时输出为:abc*+de*f+
栈的情况为:+*

15. 继续遍历,遇到g,直接将其输出。

此时输出为:abc*+de*f+g
栈的情况为:+*

16. 继续遍历,为空,遍历结束。将栈内元素依次弹出。

此时输出为:abc*+de*f+g*+
栈的情况为:空

至此,中缀表达式转后缀已经全部完成,结果为abc*+de*f+g*+

解析 会解析后缀表达就很简单了

leetCode 772:

/**
 * 基本计算器3 </br>
 *
 * @author majunmin
 * @description
 * @datetime 2021-09-25 19:47
 * @since
 */
public class LeetCode_0772 implements Solution {


    @Override
    public int calculate(String s) {
        List<String> rpe = getRPE(s);
        int result = eval(rpe);
        return result;
    }


    /**
     * rpe 表达式仅包含 {@text 数字 +-/*}
     *
     * @param rpe
     * @return
     */
    private int eval(List<String> rpe) {

        Deque<String> stack = new LinkedList<>();

        for (String item : rpe) {
            if (item.charAt(0) >= '0' && item.charAt(0) <= '9') {
                stack.push(item);
            } else {
                int num1 = parseInt(stack.pop());
                int num2 = parseInt(stack.pop());
                int result;
                if (!validOperator(item)) {
                    throw new IllegalArgumentException("");
                }
                switch (item) {
                    case "+":
                        result = num2 + num1;
                        break;
                    case "-":
                        result = num2 - num1;
                        break;
                    case "*":
                        result = num2 * num1;
                        break;
                    default:
                        result = num2 / num1;
                        break;
                }
                stack.push(String.valueOf(result));
            }
        }
        return Integer.parseInt(stack.pop());
    }

    private boolean validOperator(String operator) {
        return "+".equals(operator) || "-".equals(operator)
                || "*".equals(operator) || "/".equals(operator);
    }

    private int parseInt(String item) {
        return Integer.parseInt(item);
    }


    /**
     * 将中最表达式转化为  逆波兰表达式
     * 1. 遇到数字直接 放入 list
     * 2. 遇到 符号
     * 2.1  `(` 直接放入
     * 2.2  `)` 将栈中 所有的字符 pop 放入到结果集
     * 2.3  `符号`
     * 2.3.1 符号优先级 <= 栈顶元素优先级,直接放入
     * 2.3.2 符号优先级 > 栈顶元素优先级,将栈中元素pop 并放入结果集, 直到遇到 `(` 或者 栈 为空
     *
     * @param s
     * @return
     */
    private List<String> getRPE(String s) {
        List<String> rpe = new ArrayList<>();
        Deque<String> stack = new LinkedList<>();
        int num = -1;
        for (char c : s.toCharArray()) {
            if (c == ' ') {
                continue;
            }
            // 是一个数字
            if (c >= '0' && c <= '9') {
                if (num == -1) {
                    num = c - '0';
                } else {
                    num = num * 10 + (c - '0');
                }
            } else {
                //判断数字
                if (num != -1) {
                    rpe.add(String.valueOf(num));
                    num = -1;
                }

                if (c == '(') {
                    stack.push(String.valueOf(c));
                } else if (c == ')') {
                    while (!stack.isEmpty()) {
                        if (!"(".equals(stack.peek())) {
                            rpe.add(stack.pop());
                        } else {
                            stack.pop();
                            break;
                        }
                    }
                } else {
                    // + - * /
                    while (!stack.isEmpty()) {
                        if ("(".equals(stack.peek())) {
                            break;
                        }
                        if (getPriority(String.valueOf(c)) > getPriority(stack.peek())) {
                            break;
                        }
                        rpe.add(stack.pop());
                    }
                    stack.push(String.valueOf(c));
                }
            }
        }
        if (num != -1) {
            rpe.add(String.valueOf(num));
        }
        while (!stack.isEmpty()) {
            rpe.add(stack.pop());
        }
        return rpe;
    }

    private int getPriority(String op) {
        switch (op) {
            case "+":
            case "-":
                return 1;
            case "*":
            case "/":
                return 2;
            default:
                throw new IllegalArgumentException("Unexpected value: " + op);
        }
    }

    public static void main(String[] args) {
        Solution leetCode = new LeetCode_0772();
        System.out.println(leetCode.calculate("6 - 4/2"));
        System.out.println(leetCode.calculate("(2 + 6*3 +5 - (3*14/7+2) *5)+3"));
    }

}

代码实现


数据结构——中缀转后缀表达式
彻底用图解教会你——中缀表达式转后缀和前缀

欢迎关注我的其它发布渠道