str转list_图解中缀表达式转后缀表达式及后缀表达式求值

    在上一篇中详解了用栈实现中缀表达式求值的过程,但是如果表达式中包含括号,中缀表达式就没那么方便了,这时会将中缀表达式转成后缀表达式(又称为逆波兰表达式)来进行求值,在转换的过程中会消除括号。本篇来详解中缀表达式如何转为后缀表达式,以及后缀表达式的求值。     根据运算符相对操作数的位置,表达式分为前缀表达式、中缀表达式和后缀表达式(前缀和后缀表达式没有括号)。 一、前缀表达式     定义:运算符位于其相关的操作数之前       运算:从右向左扫描表达式,遇到数字直接压入栈,遇到运算符将栈顶数值和次顶数值弹出进行运算,再将结果入栈(遇到减法和除法,用栈顶元素作为被减数或被除数)。 如(3+4)*5-6 的前缀表达式为:
- * + 3 4 5 6

    转换方法:运算符根据运算级别从右到左排,操作数根据运算级别从左到右排列,比如上面的表达式:运算级别顺序为+、*、-,从右到左排:- * +;操作数顺序为3、4、5、6,从左到右排:3 4 5 6 。组合就是:- * + 3 4 5 6。

图解前缀表达式求值:

3a3b552b71ba94b3b9e7bf076fa14162.png

二、中缀表达式 

    定义:运算符位于与其相关的操作数之间,我们常见的即为中缀表达式

    运算:需要两个栈,一个为数栈存放数字,另一个为符号栈存放操作符。

1.通过一个index值(索引),来遍历表达式;2.如果发现是一个数字,就直接入数栈;3.如果发现扫描到的是一个符号,分如下情况: 3.1如果发现当前的符号栈为空,直接入栈; 3.2如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或等于栈中的操作符,就需要从数栈中pop出两个数(这里注意如果是减法和除法,次顶元素作为被减数或被除数,与前缀表达式相反),再从符号栈中pop出一个符号,进行运算,将得到的结果入数栈,然后操作符入符号栈;如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈。4.当表达式扫描完毕,就顺序从数栈中和符号栈中pop出相应的数和符号,并运算。5.最后在数栈只有一个数字,就是表达式的结

    图解参考:图解用栈实现中缀表达式求值

三、后缀表达式

    定义:运算符位于与其相关的操作数之后

    运算:

1.扫描表达式,如果是数直接入栈;2.如果是运算符,从栈中弹出两个数进行运算,然后将结果入栈;3.最后栈中剩余的一个值即为表达式的值

如:(3+4)*5-6 的后缀表达式为:

3 4 + 5 * 6 -
四、中缀表达式转后缀表达式 步骤分析
1.初始化两个栈:运算符栈s1和中间结果栈s2;2.从左到右扫描中缀表达式;3.遇到操作数时,将其压入s2;4.遇到运算符时,比较其与s1栈顶运算符的优先级;4.1.如果s1为空,或运算符栈顶为左括号“(”,则直接将此运算符入栈;4.2.否则,若优先级比栈顶运算符的高,也将运算符压入s1(注意必须是高,等于或低于都不行) 4.3.否则,将s1栈顶的运算符弹出并压入s2中,再次转到4.1与s1中新的栈顶运算符进行比较;5.遇到括号时:5.1.如果是左括号“(”,则直接压入s1;5.2.如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;6.重复步骤2-5,直到表达式的最右边;7.将s1中剩余的运算符依次弹出并压入s2;8.依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
图解步骤如下:

①扫描到前7个字符时,依次入栈,如下图:(如果栈顶为左括号,下一个运算符直接入栈)

3ee2df0efc85757e2472f470a8fd5d34.png

②当扫描到右括号“)”时,依次弹出s1栈顶的运算符压入s2,直到遇到左括号,一对括号消除;

2af02353899aa78e1a345831376d83d9.png

③然后扫描到‘*’直接入s1,扫描到‘4’直接入s2

84de919c71b1ac67bb2d5d2ae9d0259e.png

④接着扫描到第二个右括号‘)’,将s1栈顶的‘*’弹出压入s2,然后消除一对括号

a782da3338998ad5d2ee48974267d9dc.png

⑤接着扫描到‘-’,优先级和s1栈顶的‘+’相同,把s1栈顶运算符弹出压入s2,然后将‘-’压入s1,最后扫描到的‘5’入s2栈

dec1e8ddfd3972b35c243d2be1f9718f.png

⑥最后将s1中的运算符依次弹出并压入s2

7c5c021019e99f5935edff6494871d5a.png

⑦依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

s2弹出顺序:- 5 + * 4 + 3 2 1 逆序,即后缀表达式:1 2 3 + 4 * + 5 -

代码实现

思路: 

  1. 先将中缀表达式转换成对应的ArrayList集合,便于遍历,如上述表达式:"1+((2+3)*4)-5" 转换后应该为:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]

  2. 将步骤①得到的List转成后缀表达式对应的List,如[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] 转换为[1, 2, 3, +, 4, *, +, 5, -] 

注意:这里由于s2栈在转换过程中并没有pop操作,所以用ArrayList代替,这样直接遍历即可,不必逆序输出,简化了操作。 ①先将中缀表达式转换成对应的ArrayList集合
public static List infixExpression2List(String infixExpression) {    ArrayList ls = new ArrayList();    int i = 0;    String str;//对多位数进行拼接    char ch;//每遍历到一个字符,就放入ch    do {        //如果ch是一个非数字,加入ls        if ((ch = infixExpression.charAt(i)) < 48 || (ch = infixExpression.charAt(i)) > 57) {            ls.add("" + ch);            i++;        } else {            str = "";//先将str置空            //这里判断i的范围是因为内层循环结束后才会判断外层循环条件,如果不加会在内层循环时出现下标越界            while (i < infixExpression.length() && (ch = infixExpression.charAt(i)) >= 48 && (ch = infixExpression.charAt(i)) <= 57) {                str += ch;//拼接多位数                i++;            }            ls.add(str);        }    } while (i < infixExpression.length());    return ls;}

②比较运算符优先级的方法

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 operation) {        int result = 0;        switch (operation) {            case "+":                result = ADD;                break;            case "-":                result = SUB;                break;            case "*":                result = MUL;                break;            case "/":                result = DIV;                break;            default:                System.out.println("不存在的运算符");                break;        }        return result;    }}

③将步骤①得到的List转成后缀表达式对应的List

public static Listinfix2SuffixExpressionList(List infixList) {    Stack s1 = new Stack();//操作符栈    //由于在整个转换过程中s2栈都没有pop操作,而且后面需要逆序输出,为了简化,可以不用栈结构,改用List s2代替    List s2 = new ArrayList();    //遍历infixList    for (String item : infixList) {        //如果是一个数,则入s2        if (item.matches("\\d+")) {            s2.add(item);        } else if (item.equals("(")) {//如果是左括号,直接入s1栈            s1.push(item);        } else if (item.equals(")")) {//如果是右括号,则依次弹出s1栈顶的运算符,并加入s2,直到遇到左括号为止,此时将这一对括号丢弃;            while (!s1.peek().equals("(")) {                s2.add(s1.pop());            }            s1.pop();//去除s1中对应的左括号,很重要!!!        } else {            //如果是运算符,比较其与s1栈顶运算符的优先级:            //1.如果s1为空,或运算符栈顶为左括号“(”,则直接将此运算符入栈;            //2.否则,若优先级比栈顶运算符的高,也将运算符压入s1(注意必须是高,等于或低于都不行)            //3.否则,将s1栈顶的运算符弹出并压入s2中,再次转到4.1与s1中新的栈顶运算符进行比较;            //当item的优先级小于等于s1栈顶的优先级时,将s1栈顶的运算符弹出并加入s2中,再次转到1与s1中新的栈顶运算符进行比较            while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {                s2.add(s1.pop());            }            //将item入栈s1            s1.push(item);        }    }    //将s1中的剩余运算符弹出并加入s2    while (s1.size() != 0) {        s2.add(s1.pop());    }    return s2;//由于加入的是一个List,本身有序,所以正常输出即为后缀表达式而不必逆序}

④通过步骤③得到的list计算后缀表达式的值

public static int calculator(List ls){   Stackstack = new Stack();   //遍历ls   for (String item : ls) {       if (item.matches("\\d+")){//如果是数直接入栈           stack.push(item);       }else{           int num2=Integer.parseInt(stack.pop());           int num1=Integer.parseInt(stack.pop());           int res=0;           if (item.equals("+")){               res=num1+num2;           }else if (item.equals("-")){//后缀表达式用后取出的数-先取出的数               res=num1-num2;           }else if (item.equals("*")){               res=num1*num2;           }else if(item.equals("/")){               res=num1/num2;           }else{               throw new RuntimeException("运算符有误");           }           stack.push(res+"");       }   }   //栈最后剩下的数即为运算结果   return Integer.parseInt(stack.pop());}

图解后缀表达式求值

3fd8dcdc6be18df7990230bdd7ff484f.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值