Java数据结构与算法笔记——解析表达式

本文介绍如何使用两个栈将中缀表达式转化为后缀表达式的方法,并提供了一个具体的Java实现案例,同时展示了如何计算后缀表达式的数值结果。

三种解析表达式

前缀表达式:操作符在操作数前面,是一种没有括号的算术表达式。
中缀表达式:操作符在操作数之间。
后缀表达式:操作符在操作数后面,是一种没有括号的算术表达式。

以3+4-5为例:
前缀表达式:+ 3 4 - 5
中缀表达式:3 + 4 - 5
后缀表达式:3 4 + 5 -

以(3+4)*5为例:
前缀表达式:+ 3 4 * 5
中缀表达式:(3 + 4) * 5
后缀表达式:3 4 + 5 *

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

由于后缀表达式的运算符在两个操作数的后面,那么计算机在解析后缀表达式的时候,只需要从左向右扫描,也就是只需要向后扫描。
这样实现的逻辑就简单了,在计算机底层的硬件实现就要简单多了。

使用两个栈实现将中缀表达式转化为后缀表达式

如何将中缀表达式转化为后缀表达式:
1.初始化两个栈:运算符栈s1,中间结果栈s2
2.从左到右扫描表达式
3.遇到操作数压入s2
4.遇到运算符,比较其与s1栈顶运算符的优先级
(1)如果s1栈是空的,或者栈顶元素是“(”,或者运算符优先级比栈顶元素优先级高,就直接压入s1栈
(2)新的运算符优先级比s1栈顶元素低或者相等,就弹出s1栈顶的元素,压入s2栈,并从新判断该运算符(循环步骤4)
5.遇到括号
(1)遇到左括号(,直接压入s1栈
(2)遇到右括号),依次弹出s1栈顶元素,压入s2,直到遇到左括号为止,然后将这对括号丢掉
6.重复2-5,扫描完所有表达式
7.将s1剩余的运算符压入s2栈

package expression;

import java.util.Stack;

public class ExpressionTest1 {
    public static void main(String[] args) {
        String ex = "(34.6+56.2+58)*63-69";
        midToRear(ex);
    }

    //实现整个中缀表达式向后缀表达式转换
    public static void midToRear(String input){
        //初始化两个栈,s1放运算符,s2放中间结果
        Stack<Character> s1 = new Stack<>();
        Stack<Object> s2 = new Stack<>();

        //扫描表达式
        int len = input.length();
        char c;
        char tmpChar;
        String number;//临时存放操作数
        int lastIndex = -1;
        for (int i=0; i<len; i++){
            c = input.charAt(i);

            //遇到操作数
            if(Character.isDigit(c)){//判断c是否是数字
                lastIndex = getLastIndex(input,i);//获取操作数后面的运算符号对应在表达式中的索引值
                number = input.substring(i,lastIndex);//左开右闭取值
                i = lastIndex-1;
                System.out.print(number+"  ");
                s2.push(number);
            }

            //遇到运算符
            else if(isOperator(c)){
                while (!s1.isEmpty() && s1.peek()!='(' && priorityCompare(c,s1.peek())<=0){
                    System.out.print(s1.peek() + "  ");
                    s2.push(s1.pop());
                }
                s1.push(c);
            }

            //遇到括号
            else if (c=='('){
                s1.push(c);
            }
            else if (c == ')'){
                while ((tmpChar=s1.pop())!='('){
                    System.out.print(tmpChar + "  ");
                    s2.push(tmpChar);
                }
            }
            //其他情况忽略
        }

        //将s1剩余的运算符压入s2栈
        while (!s1.isEmpty()){
            System.out.print(s1.peek()+"  ");
            s2.push(s1.pop());
        }
        
    }

    //获取操作数后面的运算符号对应在表达式中的索引值
    public static int getLastIndex(String input, int start){
        int len = input.length();
        char c;
        int rs = -1;//我们规定-1为无效值
        for (int i=start;i<len;i++){
            c = input.charAt(i);
            if(c == '.'){
                continue;
            }else if(!Character.isDigit(c)){
                rs = i;
                break;
            }else if (i == len-1){
                rs = len;
            }
        }
        return rs;
    }

    //判断是否为运算符
    public static boolean isOperator(char c){
        return (c == '+' || c == '-' || c == '*' || c == '/');
    }

    //比较运算符优先级,op1<op2,返回-1;op1=op2,返回0;op1>op2,返回1
    public static int priorityCompare(char op1,char op2){
        if (op1 == '+' || op1 == '-'){
            return (op2=='*' || op2=='/')?-1:0; //如果op2是*或/,返回-1,否则返回0
        }
        if (op2 == '+' || op2 == '-'){
            return (op1=='*' || op1=='/')?1:0; //如果op2是*或/,返回-1,否则返回0
        }
        return 1; //默认返回1
    }
}

升级版:计算出数值结果

package expression;

import java.util.Stack;

public class ExpressionTest1 {
    public static void main(String[] args) {
        String ex = "(1+2)/2-1";
        System.out.println(midToRear(ex));

    }

    //实现整个中缀表达式向后缀表达式转换
    public static Double midToRear(String input){
        //初始化两个栈,s1放运算符,s2放中间结果
        Stack<Character> s1 = new Stack<>();
        Stack<Object> s2 = new Stack<>();

        //扫描表达式
        int len = input.length();
        char c;
        char tmpChar;
        Double number;//临时存放操作数
        int lastIndex = -1;
        for (int i=0; i<len; i++){
            c = input.charAt(i);

            //遇到操作数
            if(Character.isDigit(c)){//判断c是否是数字
                lastIndex = getLastIndex(input,i);//获取操作数后面的运算符号对应在表达式中的索引值
                number = Double.parseDouble(input.substring(i,lastIndex));//左开右闭取值
                i = lastIndex-1;
                System.out.print(number+"  ");
                s2.push(number);
            }

            //遇到运算符
            else if(isOperator(c)){
                while (!s1.isEmpty() && s1.peek()!='(' && priorityCompare(c,s1.peek())<=0){
                    System.out.print(s1.peek() + "  ");
                    //s2.push(s1.pop());
                    //在这个循环里进行运算
                    //s2先pop两个,算完再push进去
                    double num2 = (double) s2.pop();
                    double num1 = (double) s2.pop();
                    s2.push(cal(num1,num2,s1.pop()));//实现计算
                }
                s1.push(c);
            }

            //遇到括号
            else if (c=='('){
                s1.push(c);
            }
            else if (c == ')'){
                while ((tmpChar=s1.pop())!='('){
                    System.out.print(tmpChar + "  ");
                    //s2先pop两个,算完再push进去
                    double num2 = (double) s2.pop();
                    double num1 = (double) s2.pop();
                    s2.push(cal(num1,num2,tmpChar));//实现计算
                }
            }
            //其他情况忽略
        }

        //将s1剩余的运算符压入s2栈
        while (!s1.isEmpty()){
            System.out.print(s1.peek()+"  ");
            //s2先pop两个,算完再push进去
            double num2 = (double) s2.pop();
            double num1 = (double) s2.pop();
            s2.push(cal(num1,num2,s1.pop()));//实现计算
        }

        System.out.println();
        //返回最终结果
        return (Double)s2.pop();

    }

    //获取操作数后面的运算符号对应在表达式中的索引值
    public static int getLastIndex(String input, int start){
        int len = input.length();
        char c;
        int rs = -1;//我们规定-1为无效值
        for (int i=start;i<len;i++){
            c = input.charAt(i);
            if(c == '.'){
                continue;
            }else if(!Character.isDigit(c)){
                rs = i;
                break;
            }else if (i == len-1){
                rs = len;
            }
        }
        return rs;
    }

    //判断是否为运算符
    public static boolean isOperator(char c){
        return (c == '+' || c == '-' || c == '*' || c == '/');
    }

    //比较运算符优先级,op1<op2,返回-1;op1=op2,返回0;op1>op2,返回1
    public static int priorityCompare(char op1,char op2){
        if (op1 == '+' || op1 == '-'){
            return (op2=='*' || op2=='/')?-1:0; //如果op2是*或/,返回-1,否则返回0
        }
        if (op2 == '+' || op2 == '-'){
            return (op1=='*' || op1=='/')?1:0; //如果op2是*或/,返回-1,否则返回0
        }
        return 1; //默认返回1
    }

    //实现计算操作
    public static double cal(double num1, double num2, char op){//注意num1和num2的先后顺序
        switch (op){
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            case '*':
                return num1 * num2;
            case '/':
                if (num2==0) throw new IllegalArgumentException("除数不能为0");
                return num1 / num2;
            default:
                return 0;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值