前缀表达式(波兰表达式)& 中缀表达式& 后缀表达式(逆波兰表达式)& 中缀表达式转换后缀表达式

前缀表达式(Polish expression, 波兰表达式)

  • 前缀表达式的运算符位于操作数之前 如中缀表达式(3+4)7-5的前缀表达式是 -+3475
  • 运算过程: 从右往左扫描表达式, 将数字按5,7,4,3的顺序压入栈中, 读到运算符+时弹出两个数(3和4), 运算后, 再将结果7压入栈内, 再读到运算符时弹出两个数(77), 运算后, 再将结果49压入栈内, 再读到运算符-时弹出两个数(49-5), 运算后, 再将结果44压入栈内, 即完成运算
    * 前缀表达式的运算顺序已经规划好了, 因此无需判断运算符的优先级

中缀表达式(Infix expression)

  • 中缀表达式就是最常见的运算表达式 如 (3+4)*7-5. 此表达式需判断运算符的优先级

后缀表达式(Reverse Polish expression, 逆波兰表达式)

  • 与前缀表达式相似, 只是运算符位于操作数后面 如中缀表达式(3+4)7-5的后缀表达式是 34+75-
  • 运算过程: 从左往右扫描表达式, 将数字3,4压入栈中, 读到运算符+时弹出两个数(3和4), 运算后, 再将结果7压入栈内, 再下一个数字7压入栈内, 再读到运算符时弹出两个数(77), 运算后, 再将结果49压入栈内, 再继续将下一个数字5压入栈内, 再读到运算符-时弹出两个数(49-5), 运算后, 再将结果44压入栈内, 即完成运算

中缀表达式(Infix expression): 实现简单计算器

  • 思路分析
  1. 定义两个栈: 一个是数值栈, 另一个运算符栈
  2. 定义两个方法: 判断运算符优先级的方法和计算已入栈数值的方法
  3. 逐个循环扫描输入的中缀表达式, 如果是数字就入数值栈, 如果是运算符, 则需要与运算符栈的栈顶运算符比较优先级. 如果优先级高于栈顶的运算符, 则直接入栈(运算符栈), 否则, 从数值栈取2元素(数值), 在从运算符栈取栈顶元素做运算, 将运算结果再存入数值栈, 之后将当前运算符入栈(运算符栈)
  • 实例代码:

/** 定义数组栈*/
class ArrayStack {
    /** 栈大小*/
    private int maxSize;
    /** 通过该数组存放数据, 模拟栈数据结构*/
    private int[] stack;
    /** 栈顶的 index, 初始值为-1*/
    private int top = -1;

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[maxSize];
    }

    /** 栈满*/
    public boolean isFull() {
        return top == maxSize - 1;
    }

    /** 栈空*/
    public boolean isEmpty() {
        return top == -1;
    }

    /** 入/压栈*/
    public void push(int value) {
        if (isFull()) {
            System.out.println("入栈失败, 栈已满!");
            return;
        }
        top++;
        stack[top] = value;
    }

    /** 出/弹栈*/
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("出栈失败, 没有数据!");
        }
        int value = stack[top];
        top--;
        return value;
    }

    /** 从栈顶开始打印所有内容*/
    public void list() {
        if (isEmpty()) {
            System.out.println("打印失败, 没有数据!");
            return;
        }
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

    /** 查看栈顶元素内容*/
    public int peek() {
        return stack[top];
    }

    /** 运算符的优先级*/
    public int operPriority(int oper) {
        if (oper == '*'|| oper == '/') {
            return 1;
        } else if(oper == '+'|| oper == '-') {
            return 0;
        } else {
            /** 无效的表达式*/
            return -1;
        }
    }

    /** 判断是不是一个运算符*/
    public boolean isOper(char val) {
        return val == '+' || val=='-' || val=='*' || val=='/';
    }

    /** 计算方法*/
    public int cal(int num1, int num2, int oper) {
        int res = 0;
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }
}

public class CalculatorApp {
    public static void main(String[] args) {
        System.out.println("请输入要计算的中缀表达式: ");
        Scanner in = new Scanner(System.in);
        String expression = in.nextLine();
        in.close();

        /** 定义数值栈*/
        ArrayStack numStack = new ArrayStack(10);
        /** 定义运算符栈*/
        ArrayStack operStack = new ArrayStack(10);

        /** 表达式每个字符位索引*/
        int index = 0;
        /** 每次循环获取到的字符*/
        char ch;
        /** 计算多位数时, 用于拼接的字符串变量*/
        String keepnum = "";
        /** 当计算时, 从数值栈出栈的第一个数值*/
        int num1 = 0;
        /** 当计算时, 从数值栈出栈的第而二个数值*/
        int num2 = 0;
        /** 运算符 char <-> int*/
        int oper = 0;
        /** 计算结果*/
        int res = 0;
        while (true) {
            /** 每次循环获取单个字符*/
            ch = expression.substring(index, index + 1).charAt(0);
            /** 判断是否为运算符*/
            if (operStack.isOper(ch)) {
                if (operStack.isEmpty()) {
                    /** 如果定义运算符栈为空, 直接入栈*/
                    operStack.push(ch);
                } else {
                    /** 判断当前运算符(如 +)的优先级是否低于栈顶运算符(如 x), 如果低或相等通过*/
                    if (operStack.operPriority(ch) <= operStack.operPriority(operStack.peek())) {
                        /** 之前已入栈的数值与运算符取出, 开始计算*/
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = operStack.cal(num1, num2, oper);
                        /** 数值栈的累计值计算后, 将值重新入栈*/
                        numStack.push(res);
                        /** 当前运算符(如 +), 入栈*/
                        operStack.push(ch);
                    } else {
                        operStack.push(ch);
                    }
                }
            } else {
                /**
                 * 1. 如果计算器只做单数的运算, 可以直接入栈 numStack.push(ch - 48);
                 * 2. 如果支持多位数, 需要往下一位判断是否为数值而不是运算符. 如果下一位是运算符就可以 numStack.push(keepnum)
                 * */

                /** 循环拼接多位数( 如 12, 2, 6, 123)*/
                keepnum += ch;

                /** 如果是最后一位, 不再做下一位字符的判断*/
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepnum));
                } else {
                    if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                        /** 如果下一位是运算符就入栈*/
                        numStack.push(Integer.parseInt(keepnum));
                        /** 清空数*/
                        keepnum = "";
                    }
                }
            }

            index++;
            if (index >= expression.length()) {
                /** 最后位, 停止循环*/
                break;
            }
        }

        while (true) {
            /** 运算符栈为空时, 则计算结束停止循环*/
            if (operStack.isEmpty()) {
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = operStack.cal(num1, num2, oper);
            numStack.push(res);
        }

        System.out.printf("%s的结算结构为 %d\n",expression, numStack.pop());
    }

}

输出:
> 请输入要计算的中缀表达式: 
> 12+2*6-123
> 12+2*6-123的结算结构为 -99

后缀表达式(Reverse Polish expression, 逆波兰表达式): 实现简单计算器

  • 实例代码:

public class CalculatorApp {
    /** 将一个逆波兰表达式, 按各数值与运算符依次放入到 ArrayList中*/
    public static List<String> getListString(String expression) {
        String[] arr = expression.split(" ");
        List<String> list = new ArrayList<>(arr.length);
        for(String e: arr) {
            list.add(e);
        }
        return list;
    }

    /**
     * 逆波兰表达式的运算方法:
     * 1. 从左往右扫描表达式, 将数字3,4压入栈中
     * 2. 读到运算符+时弹出两个数(3和4), 运算后, 再将结果7压入栈内
     * 3. 再下一个数字5压入栈内
     * 4. 再读到运算符*时弹出两个数(7*5), 运算后, 再将结果35压入栈内
     * 5. 再继续将下一个数字6压入栈内
     * 6. 再读到运算符-时弹出两个数(35-6), 运算后, 再将结果29压入栈内, 即完成运算
     * */
    public static int cal(List<String> expressionList) {
        Stack<String> stack = new Stack<>();
        for (String exp : expressionList) {
            /** 是否为数值*/
            if (exp.matches("\\d+")) {
                /** 数值直接入栈*/
                stack.push(exp);
            } else {
                /** 弹出两个数, 并运算*/
                int num1 = Integer.parseInt(stack.pop());
                int num2 = Integer.parseInt(stack.pop());
                int res = 0;
                if (exp.equals("+")) {
                    res = num1 + num2;
                } else if (exp.equals("-")) {
                    res = num2 - num1;
                } else if (exp.equals("*")) {
                    res = num1 * num2;
                } else if (exp.equals("/")) {
                    res = num2 / num1;
                } else {
                    throw new RuntimeException("运算符错误!");
                }
                /** 每次运算结果压入栈*/
                stack.push(String.valueOf(res));
            }
        }
        /** 最后留在栈中的数值为运算结果*/
        return Integer.parseInt(stack.pop());
    }

    public static void main(String[] args) {
        /**
         * 中缀表达式 (3+4)*5-6的后缀表达式为  34+5*6-
         * */
        String expression = "3 4 + 5 * 6 -";
        List<String> expressionList = getListString(expression);
        int res = cal(expressionList);
        System.out.println("34+5*6-的结算结构为 " + res);
    }

}

输出:
> 34+5*6-的结算结构为 29

中缀表达式转换后缀表达式(Infix expression invert to Reverse Polish expression)

  1. 初始化两个栈: 运算符栈 s1和存储中间结果的 List s2
  2. 从左到右扫描中缀表达式
  3. 遇到操作数时, 将其直接加到 s2
  4. 遇到运算符时, 将其与 s1栈顶运算符比较优先级:
    (1) 如果 s1为空或栈顶运算符为左括号"(", 则直接将此运算符入栈
    (2) 否则, 若优先级比栈顶的运算符高, 也将运算符压入 s1
    (3) 否则, 将 s1栈顶的运算符弹出, 并压入到 s2中, 再次转到(4-1)与 s1中新的栈顶运算符相比较
  5. 遇到括号时:
    (1) 如果是左括号"(", 则直接压入 s1
    (2) 如果是右括号")", 则依次弹出 s2栈顶的运算符, 并压入 s2, 直到遇到左括号为止, 再将这一对括号丢弃
  6. 重复步骤2至5, 直到表达式的最右边
  7. 将 s1中剩余的运算符依次弹出并压入 s2
  8. 输出 s2
  • 实例代码:

public class InfixToReversePolishApp {
    /** 运算符的优先级*/
    public static int operPriority(String oper) {
        if (oper.equals("*") || oper.equals("/")) {
            return 2;
        } else if(oper.equals("+") || oper.equals("-")) {
            return 1;
        } else {
            if (!oper.equals("(") && !oper.equals(")")) {
                /** 无效的表达式*/
                System.out.println("不存在该运算符" + oper);
            }
            return 0;
        }
    }

    /** 中缀表达式对应的 String List*/
    public static List<String> toInfixStringList(String infixExpression) {
        List<String> list = new ArrayList<>();
        /** 遍历中缀表达式字符串的索引*/
        int index = 0;
        /** 计算多位数时, 用于拼接的字符串变量*/
        String keepnum;
        /** 每次循环获取到的字符*/
        char ch;
        while (index < infixExpression.length()) {
            /**
             * 不是数字, 就直接加到 List
             * */
            if ((ch = infixExpression.charAt(index)) < 48 ||  (ch = infixExpression.charAt(index)) > 57) {
                list.add(String.valueOf(ch));
                index++;
            } else {
                /** 初始化多位数拼接变量*/
                keepnum = "";
                /**
                 * - 不可以为最后数(由于内部循环拼接, 需要做此判断)
                 * - 每个字符必须为数字 ASCII: 48~57
                 * */
                while (index < infixExpression.length() && (ch = infixExpression.charAt(index)) >= 48
                        && (ch=infixExpression.charAt(index)) <= 57) {
                    /** 循环拼接多位数*/
                    keepnum += ch;
                    index++;
                }
                list.add(keepnum);
            }
        }
        return list;
    }

    /** 中缀表达式 List转换后缀表达式(逆波兰表达式) List*/
    public static String infixToReversePolish(List<String> infixList) {
        /** 运算符栈(含括号)*/
        Stack<String> s1 = new Stack<>();
        /** 存转换后的后缀表达式, 只存无取, 直到方法结束返回. 注: 返回时需逆序输出*/
        List<String> s2 = new ArrayList<>();
        for(String exp: infixList) {
            /** 数字直接存入 s2*/
            if(exp.matches("\\d+")) {
                s2.add(exp);
            } else if (exp.equals("(")) {
                s1.push(exp);
            } else if (exp.equals(")")) {
                /** 如果是右括号")", 则依次弹出 s1栈顶的运算符, 并压入 s2, 直到遇到左括号为止, 再将这一对括号丢弃*/
                while(!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();
            } else {
                /**
                 * 1. s1不为空
                 * 2. s1的栈顶运算符(如果是括号会返回0)大于等于 当前 exp(运算符)的优先级, 便将 s1栈顶弹出栈同时入栈到 s2
                 * */
                while(s1.size() != 0 && operPriority(s1.peek()) >= operPriority(exp) ) {
                    s2.add(s1.pop());
                }
                /** 当前 exp(运算符)入栈到 s1*/
                s1.push(exp);
            }
        }

        /** 将s1中剩余的运算符依次弹出栈, 并加到 s2*/
        while(s1.size() > 0) {
            s2.add(s1.pop());
        }
        return s2.parallelStream()
                .collect(Collectors.joining());
    }

    public static void main(String[] args) {
        /**
         * - 中缀表达式 1+((2+3)*4)-5的后缀表达式为  123+4*+5–
         * */
        String infixExpression = "1+((2+3)*4)-5";
        List<String> infixList = toInfixStringList(infixExpression);
        System.out.println("中缀表达式对应的 List" + infixList);
        String reversePolish = infixToReversePolish(infixList);
        System.out.println("后缀表达式对应的 " + reversePolish);
    }

}

输出:
> 中缀表达式对应的 List[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
> 后缀表达式对应的 123+4*+5-

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

猜你喜欢

转载自blog.csdn.net/qcl108/article/details/109060316