美文网首页我的收藏数据结构
栈的应用之中缀表达式和后缀表达式

栈的应用之中缀表达式和后缀表达式

作者: tianma | 来源:发表于2016-04-10 18:26 被阅读519次

版权声明:本文源自简书tianma,转载请务必注明出处: http://www.jianshu.com/p/a0d4764eba18

中缀表达式: 是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法,但是不易被计算机所解析。
后缀表达式:是一个通用的算术或逻辑公式表示方法, 操作符是后缀形式处于操作数的后面(例:3 4 +),后缀表达式虽然不是人们所习惯的运算表示方法,但是易被计算机解析。

例如:对于中缀表达式 9+(3-1)*2+10/2 , 其后缀表达式是 9 3 1 - 3 * + 10 2 / + , 那么为了方便计算机解析计算,我们需要将中缀表达式转换成后缀表达式,然后再对后缀表达式进行解析。

1. 中缀表达式转后缀表达式:

  1. 当读到一个操作数时,立即将它放到输出中。读到的是操作符则需要接着判断是否该入栈。读到的是左圆括号则入栈。
  2. 在读到操作符时,如果栈为空或者栈顶操作符为(,则入栈。如果栈顶操作符不为(,且此操作符优先级小于或等于此时栈顶操作符,则将栈中元素弹出直至 ①遇到左括号 或者 ②栈顶元素为更低优先级 或者 ③栈为空为止,并将当前操作符入栈;否则当前操作符继续入栈。操作符中,+-优先级低,*/优先级高。
  3. 如果遇到一个右括号,那么就将栈中元素弹出并输出直至遇到左括号为止。但是这个左括号只被弹出,并不输出。
  4. 如果读到输入的末尾,若栈不为空则将栈元素弹出直到该栈变成空栈,并将弹出的符号写到输出中。

"9+(3-1)2+10/2" 转换过程:*

操作过程 栈中元素 输出
读入 9,输出 9
读入 +,栈为空,规则2,入栈 + 9
读入 ( ,左括号,规则1,入栈 + ( 9
读入 3,输出 + ( 9 3
读入 -,栈顶为(,规则2,入栈 + ( - 9 3
读入 1,输出 + ( - 9 3 1
读入 ) ,右括号,规则3,出栈并输出 + 9 3 1 -
读入 *,*优先级高于栈顶+,规则,2,入栈 + * 9 3 1 -
读入 3,输出 + * 9 3 1 - 3
读入 +,+优先级低于栈顶*,规则2,栈中元素出栈,当前操作符入栈 + 9 3 1 - 3 * +
读入 10, 输出 + 9 3 1 - 3 * + 10
读入 / , /优先级高于+,入栈 + / 9 3 1 - 3 * + 10
读入 2, 输出 + / 9 3 1 - 3 * + 10
读至末尾,规则4,栈不为空,栈中元素出栈并输出 9 3 1 - 3 * + 10 / +

2. 后缀表达式计算最终结果:

  1. 从左到右遍历表达式的每个数字和符号,遇到是数字则进栈,遇到是运算符则将栈顶两个元素出栈,进行运算并将运算结果进栈;
  2. 遍历完后缀表达式,此时栈中剩余的数字就是运算结果。

"9 3 1 - 3 * + 10 2 / +" 计算过程:

操作过程 栈中元素
读入 9,入栈 9
读入 3,入栈 9 3
读入 1,入栈 9 3 1
读入 -,运算并将结果入栈 9 2
读入 3,入栈 9 2 3
读入 *,运算并将结果入栈 9 6
读入 +,运算并将结果入栈 15
读入 10,入栈 15 10
读入 2,入栈 15 10 2
读入 /,运算并将结果入栈 15 5
读入 +,运算并将结果入栈 20
读入完毕,栈中元素即为结果 20

简单中缀表达式计算的java实现:

public class SimpleCalcutor {

    private class Item {

        private String value;
        private Integer number;

        public Item(String value) {
            this.value = value;
            try {
                number = Integer.parseInt(value);
            } catch (Exception ignore) {
            }
        }

        public boolean isNumber() {
            return number != null;
        }

        public int getNumber() {
            if (isNumber())
                return number;
            throw new NumberFormatException();
        }

        public boolean isAdd() {
            return "+".equals(value);
        }

        public boolean isSub() {
            return "-".equals(value);
        }

        public boolean isMul() {
            return "*".equals(value);
        }

        public boolean isDiv() {
            return "/".equals(value);
        }

        public boolean isLeftBracket() {
            return "(".equals(value);
        }

        public boolean isRightBracket() {
            return ")".equals(value);
        }

        public int getPriority() {
            if (isAdd() || isSub())
                return 0;
            if (isMul() || isDiv())
                return 1;
            throw new RuntimeException("This is not +, -, *, /");
        }

        @Override
        public String toString() {
            return value != null ? value.toString() : null;
        }
    }

    /**
     * 计算结果
     * 
     * @param calStr
     * @return
     */
    public int calculate(String calStr) {
        List<Item> infixes = parse(calStr);
        List<Item> postfixes = infix2postfix(infixes);
        return calculateByPostfix(postfixes);
    }

    /**
     * 利用正则表达式将待计算的字符串转化为List<Item>形式 ,如 10/2 -> [10, /, 2]
     * 
     * @param calStr
     * @return
     */
    private List<Item> parse(String calStr) {
        Pattern pattern = Pattern.compile("\\D|\\d+");
        Matcher m = pattern.matcher(calStr);
        List<Item> items = new ArrayList<Item>();
        while (m.find()) {
            items.add(new Item(m.group(0)));
        }
        return items;
    }

    /**
     * 中缀表达式转换为后缀表达式
     * <p>
     * 1.当读到一个操作数时,立即将它放到输出中。读到的是操作符则需要接着判断是否该入栈。读到的是左圆括号则入栈。<br>
     * 2.如果遇到一个右括号,那么就将栈中元素弹出并输出直至遇到左括号为止。但是这个左括号只被弹出,并不输出。<br>
     * 3.在读到操作符时,如果此操作符优先级小于或等于此时栈顶操作符,则将栈中元素弹出直至(1)遇到左括号或者(2)栈顶元素为更低优先级或者(3)
     * 栈为空为止。操作符中,'+''-'优先级最低,'('')'优先级最高。 <br>
     * 4.如果读到输入的末尾,将栈元素弹出直到该栈变成空栈,将符号写到输出中。
     * 
     * @return
     */
    private List<Item> infix2postfix(List<Item> infixes) {
        List<Item> postfixes = new ArrayList<Item>();
        Stack<Item> stack = new Stack<Item>();
        for (Item item : infixes) {
            if (item.isNumber()) {
                postfixes.add(item);
            } else if (item.isRightBracket()) {
                // ) 右括号,将栈中元素弹出直至左括号,且左括号和右括号不加入到后缀表达式中
                while (true) {
                    Item tmp = stack.pop();
                    if (tmp.isLeftBracket())
                        break;
                    postfixes.add(tmp);
                }
            } else if (item.isLeftBracket()) {
                // ( 左括号,将左括号入栈
                stack.push(item);
            } else {
                // 当前操作符为 +, -, *, /,
                if (stack.isEmpty()) {
                    // 操作符栈为空,则将当前操作符压入栈
                    stack.push(item);
                    continue;
                }
                Item top = stack.peek();
                if (top.isLeftBracket()) {
                    // 操作符栈顶为左括号(,则将当前操作符压入栈
                    stack.push(item);
                    continue;
                }
                if (item.getPriority() <= top.getPriority()) {
                    // 如果此操作符(+,-,*,/)优先级小于或等于此时栈顶操作符
                    // 则将栈中元素弹出直至(1)遇到左括号或者(2)栈顶元素为更低优先级或者(3)栈为空为止
                    // 并将弹出的元素加入后缀表达式中,将当前操作符压入栈中
                    while (true) {
                        Item tmp = stack.peek();
                        if (tmp.isLeftBracket() || tmp.getPriority() < item.getPriority()) {
                            break;
                        }
                        postfixes.add(tmp);
                        stack.pop();
                        if (stack.isEmpty())
                            break;
                    }
                    stack.push(item);
                } else {
                    // 如果当前操作符(+,-,*,/)优先级大于此时栈顶操作符,则将当前操作符压入栈
                    stack.push(item);
                }
            }
        }
        // 如果栈中元素不为空,则将栈中元素全部弹出,加入后缀表达式中
        while (!stack.isEmpty()) {
            postfixes.add(stack.pop());
        }
        return postfixes;
    }

    /**
     * 通过后缀表达式计算数值
     * <p>
     * 1. 从左到右遍历表达式的每个数字和符号,遇到是数字则进栈,遇到是运算符则将栈顶两个元素出栈,进行运算并将运算结果进栈<br>
     * 2. 遍历完后缀表达式,此时栈中剩余的数字就是运算结果
     * 
     * @param postfixes
     * @return
     */
    private int calculateByPostfix(List<Item> postfixes) {
        Stack<Integer> stack = new Stack<Integer>();
        for (Item item : postfixes) {
            if (item.isNumber()) {
                stack.push(item.getNumber());
            } else {
                // 运算符
                int num1 = stack.pop();
                int num2 = stack.pop();
                int result;
                if (item.isAdd()) {
                    result = num2 + num1;
                } else if (item.isSub()) {
                    result = num2 - num1;
                } else if (item.isMul()) {
                    result = num2 * num1;
                } else if (item.isDiv()) {
                    result = num2 / num1;
                } else {
                    throw new IllegalArgumentException("Operator invalid : " + item.value);
                }
                stack.push(result);
            }
        }
        return stack.pop();
    }

    public static void main(String[] args) {
        SimpleCalcutor calcutor = new SimpleCalcutor();
        String calStr = "9+(3-1)*3+10/2";
        int result = calcutor.calculate(calStr);
        System.out.println(result);
    }

}

源码github地址:
SimpleCalculator

参考链接:
利用栈将中缀表达式转换成后缀表达式

相关文章

  • 数据结构与算法--后缀表达式

    中缀表达式转后缀表达式 中缀表达式转后缀表达式的思路步骤分析。 初始化一个栈和一个队列,运算符栈 S1 和存储中间...

  • Python 简单计算器-逆波兰后缀表达式实现

    中缀表达式 后缀表达式 简易计算器,可以通过栈来实现。然而如果直接使用中缀表达式,需要处理括号,而使用后缀表达式则...

  • 四则运算表达式求值

    利用栈求解四则运算:求解思路为先将中缀表达式转换为后缀表达式,再利用后缀表达式求解中缀表达式:运算符出现在两个数字...

  • 计算器

    使用Java写的一个可以计算+,-,*,/ 的计算器。首先用栈把中缀表达式转化成后缀表达式,再利用栈对后缀表达式求...

  • day04-栈

    栈 解决实际问题: 表达式的求职和转换(中缀表达式转后缀表达式) 二叉树的遍历 深度优先搜索 概念: 栈(stac...

  • 【数据结构】【C#】011-栈的应用:📟表达式求值

    C#数据结构:栈的应用:表达式求值 后缀表达式 在我们日常生活中所见表达式都是中缀表达式,如 “5*(3+7)-4...

  • 利用栈将中缀表达式转为后缀表达式

    利用栈将中缀表达式转为后缀表达式 算法思想 顺序扫描中缀表达式(可以存储在字符数组中) 判断当前元素类型:如果是数...

  • 送外卖小公司OA

    中缀表达式转后缀表达式的方法: 遇到操作数:直接输出(添加到后缀表达式中) 栈为空时,遇到运算符,直接入栈 遇到左...

  • 表达式树

    表达式树中缀表达式转换为后缀表达式后缀表达式总结

  • 中缀表达式转后缀表达式并求值

    1.什么是中缀表达式?中缀表达式示例 2.什么是后缀表达式?后缀表达式示例 3.代码

网友评论

    本文标题:栈的应用之中缀表达式和后缀表达式

    本文链接:https://www.haomeiwen.com/subject/ynjvlttx.html