栈的应用(中缀表达式转逆波兰表达式)

栈的应用(中缀表达式转逆波兰表达式)

思路

因为中缀表达式对于操作符的优先级很不好计算,就将中缀转成计算机更容易识别的后缀表达式
中缀表达式转后缀表达式的思路步骤分析:

  1. 初始化两个栈:运算符栈s1和存储中间结果的栈s2
  2. 从左至右扫描中缀表达式
  3. 遇到操作数时,将其压入s2
  4. 遇到运算符时:
    1.如果s1空,或栈顶运算符为左括号"(",则直接将此运算符入s1
    2.如果s1栈顶为运算符,并且优先级比运算符高,也将运算符压入s1
    3.否则,将s1栈顶运算符弹出并压入s2,再次转到 4.1 步骤,判断新的栈顶符号,是比较优先级还是直接入栈
  5. 遇到括号时:
    1.如果是左括号 "(" ,则直接压入s1
    2.如果是右括号 ")", 则遍历栈中左括号 “(” 上面的所有运算符,依次弹出压入s2中
    然后将左括号弹出,将这一对括号丢弃
  6. 重复步骤2到5,直至表达式的最右边
  7. 将s1中剩余的运算符依次弹出压入s2
  8. 依次弹出s2中的元素并输出,后缀表达式就是输出结果的逆序

代码实现

处理中缀表达式格式

/**
     * 用于将中缀表达式转成数字,操作符,括号都分开存储的List集合
     * @param s 中缀表达式
     * @return  转换后的List集合
     */
    public static List<String> toInfixExpressionList(String s) {
        //先定义一个List,存放中缀表达式 对应的内容
        List<String> ls = new ArrayList<>();
        int i = 0; //字符指针索引,用于遍历 中缀表达式字符串
        String str = ""; //对多位数的拼接
        char c; //每遍历到一个字符,就放入到c
        while (i != s.length()) {
            //如果c是一个非数字,则加入到ls
            c = s.charAt(i);
            if (c < 48 || c > 57) { // 0 -> ASCILL 48    10 ->  ASCILL 58
                //判断为操作符
                ls.add(c + "");
                i++;
            } else {
                //判断为数字后,紧跟着向后遍历,碰到数字就拼接,不是数字就停止遍历,然后将拼接的多位数加入List
                while(i<s.length()&& (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
                    //拼接多位数
                    str += c;
                    i++;
                }
                ls.add(str);
                str="";
            }
        }
        return ls;
    }

中缀转后缀

	/**
     * 用于将中缀表达式转换成后缀表达式
     *
     * @param inffixList 中缀表达式的List
     * @return           后缀表达式的List
     */
    public static List<String> parseSuffixExpressionList(List<String> inffixList){
        Stack<String> stack = new Stack<>();// 符号栈
        /*
            这里不使用栈来存储表达式转换结果集是因为,使用栈存储,始终没有出栈操作
            并且需要从栈底向栈顶,即逆序输出存入的结果集,使用List就可以直接顺序输出
         */
        List<String> ls = new ArrayList<>();// 结果集
        for (String s : inffixList) {
            if(s.matches("\\d+")){// 判断是数字,直接加入结果集
                ls.add(s);
            }else if(s.equals("(")){// 判断是左括号,直接入栈
                stack.push(s);
            }else if(s.equals(")")){// 判断是右括号,从符号栈中寻找一个左括号,并消除
                // 如果中间有运算符号,直接加入结果集
                while(!stack.peek().equals("(")){
                    //遍历,左右括号之间的运算符
                    ls.add(stack.pop());
                }
                //弹出 ( 左括号,消除
                stack.pop();
            }else{// 判断是操作符
                //当s的优先级小于等于栈顶运算符优先级时,将栈顶运算符弹出,加入ls 结果集,再与新的栈顶运算符比较优先级
                boolean loop = false; //表示是否需要继续比较运算符优先级
                do{
                    //符号栈不为空,才比较优先级
                    if(stack.size() == 0 || stack.peek().equals("(")){
                        stack.push(s);
                        //关闭循环,只有出现遍历的 s运算符优先级小于等于栈顶运算符 时才开启循环,其他情况不需要循环
                        loop = false;
                    }else if(Operation.getPriority(s) > Operation.getPriority(stack.peek())){
                        //s 遍历的运算符优先级比栈顶运算符优先级大,直接入符号栈
                        stack.push(s);
                        loop = false;
                    }else{
                        //s 遍历的运算符优先级小于等于栈顶运算符优先级,弹出栈顶运算符号加入ls
                        //重复循环,判断新的栈顶是否为空或者比较优先级,进行下一步操作
                        loop = true;
                        ls.add(stack.pop());
                    }
                }while (loop);
            }
        }
        //遍历完成后,如果符号栈内不为空,则依次弹出加入到ls中
        while(!stack.isEmpty()){
            ls.add(stack.pop());
        }
        return ls;
    }

运算符类,用于返回运算符优先级

class Operation{
    private final static int ADD_SUB = 1;
    private final static int MUL_DIV = 2;

    public static int getPriority(String oper){
        switch (oper){
            case "+":
            case "-":
                return ADD_SUB;
            case "*":
            case "/":
                return MUL_DIV;
            default:
                throw new RuntimeException("运算符有误!");
        }
    }
}
posted @ 2021-03-08 18:51  编程の小白  阅读(84)  评论(0编辑  收藏  举报