栈的应用:字符串四则运算表达式求值

栈的应用–四则运算表达式求值

1、后缀(逆波兰)表示法的定义

一种不需要括号的后缀表达法

例:

(1)对于 “9+(3-1)3+10/2” 这种传统的表达式我们称之为中缀表达式*,顾名思义,后缀表达式就是把所有的运算符全部表示在需要计算的数字后面(不包括括号)。

那对于 “9+(3-1)*3+10/2”的后缀表达式为“9 3 1 - 3 * + 10 2 / +”

(2)后缀表达式我们要怎么计算呢?

​ ①我们初始化一个空栈,将字符串 “9+(3-1)3+10/2” 转化为字符数组*[‘9’ , ‘+’ , ‘(’ , ‘3’ , ‘-’ , ‘1’ , ‘)’ , ‘X’ , ‘3’ , ‘+’ , ‘10’ , ‘/’ , ‘2’]**。

​ ②循环遍历数组,若为数字,便输出。

​ ③若不是数字,则判断是否为左括号,若为左括号,则进栈。

​ ④若不是左括号,则判断该运算符与栈顶运算符的优先级。运算符优先级为 ‘(’ == ‘)’ < ‘+’ == ‘-’ < ‘*’ == '/'

​ ⑤若栈顶运算符的优先级小于该运算符,则直接进栈。

​ ⑥否则判断该运算符是否为右括号。

​ ⑦若为右括号,则循环弹出栈顶运算符,直到匹配到左括号停止循环。

​ ⑧若不是右括号,则循环弹出栈顶元素,直到栈顶元素优先级小于该运算符或者栈为空时停止。再将该运算符入栈。

图例

①初始化空栈
在这里插入图片描述

第一个元素为 9 ,直接输出 9。

第二个元素为 + ,此时栈为空,所以直接入栈
在这里插入图片描述

第三个元素为 ( ,直接入栈
在这里插入图片描述

第四个元素为 3 ,输出 3 ,此时输出为 9 3。

第五个元素为 - ,- 的优先级 大于 ( , 入栈
在这里插入图片描述
第六个元素为 1 ,输出 1,此时输出为 9 3 1。

第七个元素为 ) ,循环出栈栈顶元素,直到匹配到 ( 为止,此时输出结果为 9 3 1 - ,输出之后栈为
在这里插入图片描述
第八个元素为 * ,* 的优先级大于栈顶元素 + ,直接入栈
在这里插入图片描述
第九个元素为 3 ,直接输出 3 ,此时输出为 9 3 1 - 3。

第十个元素为 + ,+ 的优先级小于 * ,则循环弹出栈顶元素,直到栈顶元素的优先级小于 + 或者栈为空时停止,此时输出为 9 3 1 - 3 * + ,栈为空循环停止,第十个元素 + 入栈
在这里插入图片描述
第十一个元素为 10,直接输出 10,此时输出为 9 3 1 - 3 * + 10。

第十二个元素为 / ,/ 的优先级大于 + ,直接入栈,不输出
在这里插入图片描述
第十三个元素为 2 ,直接输出 2,此时输出为 9 3 1 - 3 * + 10 2。

至此数组遍历完毕,弹出栈中剩余元素即可,输出为 9 3 1 - 3 * + 10 2 / +

2、计算后缀表达式

相比于求后缀表达式,后缀表达式的计算要容易许多。

首先把我们得到的后缀表达式“9 3 1 - 3 * + 10 2 / +”分解为数组[ ‘9’ , ‘3’ , ‘1’ , ‘-’ , ‘3’ , ‘*’ , ‘+’ , ‘10’ , ‘2’ , ‘/’ , ‘+’]。

创建一个空栈。

循环遍历数组,若为数字则入栈。

若为运算符,则取两次栈顶元素,进行计算(注意:当运算符为 ‘-’ 或 ‘/’ 时,第一个弹出的栈顶元素为减数(除数),第二个为被减数(被除数)),并将计算结果入栈。

继续以上述表达式为例进行分析:

图例

初始化一个空栈
在这里插入图片描述

数组第一个元素为 9 ,入栈
在这里插入图片描述

数组第二个元素为 3,入栈
在这里插入图片描述

第三个元素为 1 ,入栈
在这里插入图片描述

第四个元素为 ’ - ‘,弹出两个栈顶元素计算 3-1 ==2 ,将计算结果入栈
在这里插入图片描述

第五个元素为 3 ,入栈
在这里插入图片描述

第六个元素为 *,弹出两个元素计算 3 * 2==6,将计算结果入栈
在这里插入图片描述

第七个元素为 +,弹出两个元素计算 9 + 6 == 15,将计算结果入栈
在这里插入图片描述

第八个元素为 10,入栈
在这里插入图片描述

第九个元素为 2,入栈
在这里插入图片描述

第十个元素为 / ,弹出两个栈顶元素,计算 10 / 2 == 5,并将计算结果入栈
在这里插入图片描述
第十一个元素为 + ,弹出两个栈顶元素,计算 15+5==20,并将结果入栈
在这里插入图片描述

此时数组为空,弹出栈顶元素即为结果 20。

3、代码实现

import com.sun.javaws.IconUtil;

import java.util.Scanner;
import java.util.Stack;

public class Test {
    public static void main(String[] args) {
        String s = null;
        Scanner in = new Scanner(System.in);
        System.out.println("请输入您要计算的表达式:");
        s = in.nextLine();
        try{
            s = getPostfixExpression(s);
            System.out.println("您得到的后缀表达式为:"+s);
            System.out.println("经计算,值为"+countPostfixExpression(s));
        }catch (Exception e){
            System.out.println("请检查您输入的表达式是否正确");
        }
    }

    //计算后缀表达式
    static int countPostfixExpression(String exp) throws Exception {
        // 利用转化时添加的空格进行分割,得到数组
        String []exps = exp.split(" ");
        // 创建数字栈
        Stack numStack = new Stack();
        // 遍历数组
        for ( String e : exps ) {
            // 判断元素是否为运算符
            // 当元素为运算符时,去出数字栈内前两个元素进行相应计算
            // 若输入的表达式没问题,则在遇见运算符时,数字栈中必有两个或两个以上元素,否则即抛出异常
            // 计算完成后需将计算结果入栈,参与后续计算
            if(e.equals("+")){
                if(numStack.size()<2){
                    throw new Exception();
                }
                numStack.push((int)numStack.pop()+(int)numStack.pop());
            }else if(e.equals("-")){
                if(numStack.size()<2){
                    throw new Exception();
                }
                numStack.push(-((int) numStack.pop()-(int)numStack.pop()));
            }else if(e.equals("*")){
                if(numStack.size()<2){
                    throw new Exception();
                }
                numStack.push((int)numStack.pop()*(int)numStack.pop());
            }else if(e.equals("/")){
                if(numStack.size()<2){
                    throw new Exception();
                }
                int x = (int)numStack.pop();
                int y = (int)numStack.pop();
                if(x == 0){
                    throw new Exception();
                }
                numStack.push(y/x);
            }else{
                // 将数字入栈
                numStack.push(Integer.parseInt(e));
            }
        }
        return (int)numStack.pop();
    }

    // 获取后缀表达式
    static String getPostfixExpression(String exp) throws Exception{
        // 将字符串解析为数字
        char[] exps = exp.toCharArray();
        // 判断表达式是否以数字开头,不是则判定为不合法,且抛出异常
        if(!('0'<=exps[0]&&exps[0]<='9')){
            throw new Exception();
        }
        // 创建两个栈,用来存储运算符与数字
        Stack expsStack = new Stack();
        Stack numStack = new Stack();

        String result = "";

        // 循环遍历数组元素
        for (char c : exps) {
            // 消除表达式中的空格
            if(c==' '){
                continue;
            }
            // 判断元素是否为数字
            if('0'<=c && c <= '9'){
                // 当元素为数字且当前数字栈为空时,直接入栈。
                if(numStack.empty()){
                    numStack.push(((int)c)%48);

                }else{
                    // 当数字栈不为空时,将数字栈中元素取出并乘以10
                    // 因为将字符串分割为字符数组时,类似于13这样的数字会被拆解为‘1’,‘3’两个元素
                    // 当两个数字字符连续时,我们就认为它是一个多位数被拆分,数字栈里永远只有一个元素
                    // 当检测到运算符时,弹出数字栈中的元素即为正确数字
                    int x =((int)numStack.pop())%48;
                    x = x*10 + ((int)c)%48;
                    numStack.push(x);
                }
            }else{
                // System.out.println(numStack);
                // 检测到运算符,判断数字栈是否为空,不为空则弹出数字
                if(!numStack.empty()){
                    // 数字与运算符之间添加空格,方便后续计算
                    result = result + numStack.pop() + " ";
                }
                // 检测运算符栈是否为空
                if(expsStack.empty()){
                    // 判断输入的运算符是否合法,此程序中,只允许输入 +、-、*、/、(、)等,输入不正确的运算符就抛出异常
                    if(getPriority(c)==-1){
                        throw new Exception();
                    }
                    // 运算符合法,且运算符栈为空,直接入栈
                    expsStack.push(c);
                }else{
                    // 运算符栈不为空时
                    // 检测运算符合法性,不合法抛出异常
                    if(getPriority(c)==-1){
                        throw new Exception();
                    }else if(getPriority((char)expsStack.peek()) < getPriority(c)){
                        // 运算符合法时,比较栈顶运算符与输入运算符的优先级,若输入运算符优先级大于栈顶运算符优先级
                        // 直接入栈
                        expsStack.push(c);
                    }else{
                        // 否则判断是否为左括号
                        // 运算符优先级顺序为 ( = ) < + = - < * = /
                        if(c == '('){
                            // 左括号直接入栈(匹配到右括号时出栈)
                            expsStack.push(c);
                        }else if(c == ')'){
                            // 当元素安抚为右括号时
                            // 循环出栈运算符,直到匹配到第一个左括号
                            while((char)expsStack.peek() != '('){
                                // System.out.println(expsStack.pop());
                                result = result + (char)expsStack.pop() + " ";
                            }
                            // 弹出匹配完成的左括号
                            expsStack.pop();
                        }else{
                            // 若运算符不是括号,则循环出栈运算符,直到栈顶运算符优先级 < 输入运算符
                            while(getPriority((char)expsStack.peek()) >= getPriority(c)){
                                // System.out.println((char)expsStack.pop());
                                result = result + (char)expsStack.pop() + " ";
                                // 当栈为空时,跳出循环
                                if(expsStack.empty()){
                                    break;
                                }
                            }
                            // 将输入运算符入栈
                            expsStack.push(c);
                        }
                    }
                }
            }
        }
        // 输出数字栈所有元素
        while(!numStack.empty()){
            result = result + numStack.pop() + " ";
        }
        // 输出运算符栈所有元素
        while(!expsStack.empty()){
            // System.out.println(expsStack.pop());
            result = result + (char)expsStack.pop() + " ";
        }

        // 返回转换的后缀表达式
        return result;
    }

    // 获取运算符优先级
    static int getPriority(char exp){
        int pri = -1;

        if(exp == '+' || exp == '-'){
            pri = 1;
        }else if(exp == '*' || exp == '/'){
            pri = 2;
        }else if(exp == '(' || exp == ')'){
            pri = 0;
        }

        return pri;
    }

}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐先生没洗头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值