【数据结构与算法】了解前缀中缀后缀表达式,实现逆波兰计算器

一、中缀表达式

1、中缀表达式就是常见的运算表达式,如(3+4)×5-6
2、中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作(前面我们讲的案例就能看的这个问题),因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式.)

中缀表达式的计算机求值
使用栈实现综合计算器的计算 思路
(1)通过一个 index 值(索引),来遍历我们的表达式
(2) 如果我们发现是一个数字, 就直接入数栈
(3)如果发现扫描到是一个符号, 就分如下情况
1) 如果发现当前的符号栈为 空,就直接入栈
2) 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈.
(4) 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
(5)最后在数栈只有一个数字,就是表达式的结果

二、前缀表达式

前缀表达式又称波兰式,其运算符在操作数之前,转化如下:
(3+4)*5-6 转化为:- * + 3 4 5 6

前缀表达式的计算机求值
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 和 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果

三、后缀表达式

1、后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
2、举例说明: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 –
3、转化规则

正常的表达式 逆波兰表达式
a+b a b +
a+(b-c) a b c - +
a+(b-c)*d a b c – d * +
a+d*(b-c) a d b c - * +
a=1+3 a 1 3 + =

后缀表达式的计算机求值
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

四、中缀表达式转成后缀表达式

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

中缀表达式转化为后缀表达式
“1+((2+3)×4)-5” 转换后为: “1 2 3 + 4 × + 5 –”
转化过程

扫描到的元素 s2(栈底->栈顶) s1 (栈底->栈顶) 说明
1 1 数字,直接入栈
+ 1 + s1为空,运算符直接入栈
( 1 + ( 左括号,直接入栈
( 1 + ( ( 同上
2 1 2 + ( ( 数字
+ 1 2 + ( ( + s1栈顶为左括号,运算符直接入栈
3 1 2 3 + ( ( + 数字
) 1 2 3 + + ( 右括号,弹出运算符直至遇到左括号
× 1 2 3 + + ( × s1栈顶为左括号,运算符直接入栈
4 1 2 3 + 4 + ( × 数字
) 1 2 3 + 4 × + 右括号,弹出运算符直至遇到左括号
- 1 2 3 + 4 × + - -与+优先级相同,因此弹出+,再压入-
5 1 2 3 + 4 × + 5 - 数字
到达最右端 1 2 3 + 4 × + 5 - s1中剩余的运算符

五、逆波兰计算器

计算过程
1、将中缀表达式转化为后缀表达式
2、利用后缀表达式进行计算

实现代码

public class PolandNotation {
    public static void main(String[] args) {
        /**
         * 中缀转后缀,并计算
         * 1.  1+((2+3)*4)-5 转成 1 2 3 + 4 * + 5 -
         * 2.  将中缀表达式转成对应的list,方便
         * 3.  将中缀表达式list转成后缀表达式list 即:[1,+,(,(,2,+,3,),*,4,),-,5] =>  [1,2,3,+,4,*,+,5,-]
         */

        String expresstion="1+((2+3)*4)-5";
        List<String> infixExpressionList = toInfixExpressionList(expresstion);
        System.out.println("中缀表达式对应的List="+infixExpressionList);
        List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);
        System.out.println("后缀表达式对应的List="+suffixExpressionList);

        System.out.printf("%s=%d",expresstion,calculate(suffixExpressionList));


    }
    //将后缀表达式的数据和运算符依次放入到ArrayList
    public static List<String> getListString(String suffixExpression){
        String[] split=suffixExpression.split(" ");
        List<String> list=new ArrayList<>();
        for (String ele:split){
            list.add(ele);
        }
        return list;
    }

    //计算
    public static int calculate(List<String> items){
        Stack<String> stack=new Stack<String>();
        for (String item:items){
            if(item.matches("\\d+")){//匹配的是多位数
                stack.push(item);
            }else {
                int num2 = Integer.parseInt(stack.pop()) ;
                int num1 = Integer.parseInt(stack.pop()) ;
                int res=0;
                if(("+").equals(item)){
                    res=num1 +num2;
                }else if(("-").equals(item)){
                    res=num1-num2;
                }else if(("*").equals(item)){
                    res=num1*num2;
                }else if(("/").equals(item)){
                    res=num1/num2;
                }else {
                    throw new RuntimeException("运算符错误");
                }
                stack.push(""+res);
            }
        }
        //最后留在栈中的数据就是运算结果
        return Integer.parseInt(stack.pop());
    }

    //中缀转后缀

    //将中缀表达式转成对应的List   str="1+((2+3)*4)-5"
    public static  List<String> toInfixExpressionList(String s){
        List<String> list=new ArrayList<String>();
        int i=0;
        String str;
        char c;
        do{
            if((c=s.charAt(i))<48||(c=s.charAt(i))>57){//如果是一个非数字,加到ls
                list.add(""+c);
                i++;
            }else {//如果是一个数字
                str="";
                while (i<s.length() && (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
                    str+=c;
                    i++;
                }
                list.add(str);
            }
        }while (i<s.length());
        return list;
    }

    //将中缀表达式转化为后缀表达式
    public static List<String> parseSuffixExpressionList(List<String> list){
        //符号栈
        Stack<String> s1=new Stack<String>();
        //s2只有添加,没有pop,加上如果用栈还得逆序输出,所以这里直接用list
        List<String> s2=new ArrayList<String>();
        for (String item:list){
            if(item.matches("\\d+")){
                s2.add(item);
            }else if(item.equals("(")){
                s1.push(item);
            }else if(item.equals(")")){
                while (!s1.peek().equals("(")){
                    s2.add(s1.pop());
                }
                s1.pop();//去掉左括号
            }else {
                //是运算符 比较优先级
                while (s1.size()!=0 && Operation.getValue(s1.peek())>=Operation.getValue(item)){
                    s2.add(s1.pop());
                }
                s1.push(item);
            }
        }
        //将s1中剩余的运算符依次放到s2中
        while (s1.size()!=0){
            s2.add(s1.pop());
        }

        return s2;
    }
}
class Operation{
    private static int ADD=1;
    private static int SUB=1;
    private static int MUL=2;
    private static int DIV=2;

    //返回优先级
    public static int getValue(String operagion){
        int result=0;
        switch ( operagion){
            case "+":
                result=ADD;
                break;
            case "-":
                result=SUB;
                break;
            case "*":
                result=MUL;
                break;
            case "/":
                result=DIV;
                break;
            default:
                System.out.println("不存在该运算符");
                break;
        }
        return result;
    }
}

该代码实现的逆波兰计算器只支持整数计算哦~

原创文章 255 获赞 116 访问量 19万+

猜你喜欢

转载自blog.csdn.net/cxh6863/article/details/104837983