利用栈实现简单表达式求值

2023-10-27

简单表达式求值

关键点

  1. 首先明确要使用的数据结构,本文采用栈来实现。
  2. 为了分别操作数字和运算符,采用双栈,一个数值栈和一个运算符栈
  3. 根据栈顶运算符和待入栈运算符的优先级的判断,产生中间结果,而中间结果作为最终结果的一部分需要再次入栈
    • 栈顶运算符优先级大于等于待入栈运算符,则需要计算中间结果
    • 栈顶运算符优先级小于待入栈运算符,则直接入栈

数组模拟栈实现

public class ArrayStack {
    private int maxSize;
    private int[] stack;
    private int top;

    public ArrayStack() {
    }

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        this.stack = new int[this.maxSize];
        this.top = -1;
    }

    public boolean isFull() {
        return this.top == this.maxSize - 1;
    }

    public boolean isEmpty() {
        return this.top == -1;
    }

    public void push(int data) {
        if (this.isFull()) {
            System.out.println("栈满");
            return;
        }
        this.stack[++this.top] = data;
    }

    public int pop() {
        if (this.isEmpty()) {
            throw new RuntimeException("栈空");
        }
        return this.stack[this.top--];
    }
    
	//判断栈顶运算符和待入栈运算符的优先级,需要取得栈顶元素
    public int getTop() {
        return this.stack[this.top];
    }
}

辅助函数

	public static boolean isSymbol(int value) {
        return (value == '+') || (value == '-') || (value == '*') || (value == '/');
    }
	//判断扫描到的字符是否为运算符,注意:字符类型(不是字符串)在和数值类型进行判等时,会参照ASCII码表
	/*验证如下:
		char ch = 'A';
        int num = ch;
        System.out.println(num);//65
        System.out.println(num == ch);//true
	*/
	
	//定义优先级:+、-的优先级为0,*、/的优先级为1
    public static int priority(int value) {
        if ((value == '+') || (value == '-')) {
            return 0;
        } else {
            return 1;
        }
    }
	

	/**
     * 3 + 5,其中3为左操作数,5为右操作数
     * @param left   左操作数
     * @param right  右操作数
     * @param symbol 运算符
     * @return       运算结果
     */
    public static int cal(int left, int right, int symbol) {
        int res = 0;
        switch (symbol) {
            case '*':
                res = left * right;
                break;
            case '/':
                res = left / right;
                break;
            case '+':
                res = left + right;
                break;
            case '-':
                res = left - right;
                break;
            default:
                break;
        }
        return res;
    }

实现一

思路

扫描表达式字符串,判断扫描到的字符串为符号还是数字

  • 如果为数字,则直接入数栈
  • 如果为符号,继续判断符号栈是否为空
    • 如果符号栈为空,即没有可比较的栈顶运算符,则扫描到的符号直接入栈
    • 如果符号栈不为空,继续判断栈顶运算符优先级和所扫描到运算符的优先级
      • 栈顶运算符优先级小于等于所扫描运算符,数栈弹出两数,弹出符号栈栈顶运算符,计算中间结果并压入数栈,所扫描运算符入符号栈
      • 否则,所扫描运算符直接入符号栈
public static void main(String[] args) {
        ArrayStack numStack = new ArrayStack(20);
        ArrayStack symbolStack = new ArrayStack(20);
        String expression = "9+3*3-7";

        int index = 0;//扫描表达式的指针
        int leftNum = 0;
        int rightNum = 0;
        int symbol = 0;//记录出栈运算符
        int result = 0;
        char ch = ' ';//记录每次扫描到的运算符

    	//扫描表达式字符串
        while (index < expression.length()) {
            ch = expression.charAt(index);
            index++;
            if (isSymbol(ch)) {
                if (!symbolStack.isEmpty()) {
                    if (priority(ch) <= priority(symbolStack.getTop())) {
                        rightNum = numStack.pop();//从数栈弹出的第一个数为右操作数
                        leftNum = numStack.pop();//从数栈弹出的第二个数为右操作数
                        symbol = symbolStack.pop();
                        result = cal(leftNum, rightNum, symbol);
                        //中间结果入数栈,扫描到的运算符入符号栈
                        numStack.push(result);
                        symbolStack.push(ch);
                    } else {
                        symbolStack.push(ch);
                    }
                } else {
                    symbolStack.push(ch);
                }
            } else {
                numStack.push(ch - 48); //  '1' -> 1 (字符串形式的数字转化为真正的数值)
            }
        }

    	//表达式扫描完毕,若符号栈还剩下运算符,则继续弹出参与计算,直到为空。
        while (!symbolStack.isEmpty()) {
            rightNum = numStack.pop();
            leftNum = numStack.pop();
            symbol = symbolStack.pop();
            result = cal(leftNum, rightNum, symbol);
            numStack.push(result);
        }
    	//此时数栈剩下的最后一个数字就是最终结果
        System.out.println(expression + " 的结果为:" + numStack.pop());
    }

实现一的问题

String expression = “9+3*3-7”;

如果表达式数字都在10以内,结果不会出现问题

在这里插入图片描述

String expression = “90+3*3-7”;

但是表达式数字出现多位数时,结果并不正确
在这里插入图片描述

原因分析

由于在扫描表达式字符串的时候,一次只能扫描一个字符。
实现一中,当扫描到一个数字我们便直接入数栈,这显然是不合理的。例如"245",扫描到’2’时,我们仅仅是将数值2入栈。

解决策略

通过原因分析,我们了解到当扫描到一个数字的时候,并不能马上就入栈,而需要继续判断下一个扫描位置是否也为数字。
若下一个扫描位置也为数字,还需要对下下个扫描位置进行判断,进入loop,直到扫描到符号。
那我们就知道,数字字符的宽度已被我们扫描完毕,可以进行入栈操作。

最终实现

改进关键
  • 由于存在多位数,因此需要一个字符串将扫描到的数字字符拼接起来
  • 一直扫描数字字符之后的字符串,直到碰见第一个符号字符
public static void main(String[] args) {
        ArrayStack numStack = new ArrayStack(20);
        ArrayStack symbolStack = new ArrayStack(20);
        String expression = "90+3*3-7";

        int index = 0;
        int leftNum = 0;
        int rightNum = 0;
        int symbol = 0;
        int result = 0;
        char ch = ' ';
        String number = "";//用于拼接数字字符

        while (index < expression.length()) {
            ch = expression.charAt(index);
            index++;
            if (isSymbol(ch)) {
                if (!symbolStack.isEmpty()) {
                    if (priority(ch) <= priority(symbolStack.getTop())) {
                        rightNum = numStack.pop();
                        leftNum = numStack.pop();
                        symbol = symbolStack.pop();
                        result = cal(leftNum, rightNum, symbol);
                        numStack.push(result);
                        symbolStack.push(ch);
                    } else {
                        symbolStack.push(ch);
                    }
                } else {
                    symbolStack.push(ch);
                }
            } else {
                //拼接当前所扫描的数字字符
                number += ch;
                
                //逻辑与顺序不可以交换,因为扫描到最后一个数字字符时,表达式已经到头了
                while (index < expression.length() && !isSymbol(expression.charAt(index))) {
                    //拼接后续所扫描到的数字字符
                    number += expression.charAt(index);
                    index++;
                }
                //将数字字符串转化为数值类型的数字
                numStack.push(Integer.parseInt(number));
                //注意清空每一轮拼接的数字字符串,否则下一轮会包含上一轮拼接的数字字符
                number = "";
            }
        }

        while (!symbolStack.isEmpty()) {
            rightNum = numStack.pop();
            leftNum = numStack.pop();
            symbol = symbolStack.pop();
            result = cal(leftNum, rightNum, symbol);
            numStack.push(result);
        }
        System.out.println(expression + " 的结果为:" + numStack.pop());
    }
验证

String expression = “315+20*3-10”;
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用栈实现简单表达式求值 的相关文章

  • Spring Batch 多线程 - 如何使每个线程读取唯一的记录?

    这个问题在很多论坛上都被问过很多次了 但我没有看到适合我的答案 我正在尝试在我的 Spring Batch 实现中实现多线程步骤 有一个包含 100k 条记录的临时表 想要在 10 个线程中处理它 每个线程的提交间隔为 300 因此在任何时
  • 如何默认将 Maven 插件附加到阶段?

    我有一个 Maven 插件应该在编译阶段运行 所以在项目中consumes我的插件 我必须做这样的事情
  • 在画布上绘图

    我正在编写一个 Android 应用程序 它可以在视图的 onDraw 事件上直接绘制到画布上 我正在绘制一些涉及单独绘制每个像素的东西 为此我使用类似的东西 for int x 0 x lt xMax x for int y 0 y lt
  • 如何找到给定字符串的最长重复子串

    我是java新手 我被分配寻找字符串的最长子字符串 我在网上研究 似乎解决这个问题的好方法是实现后缀树 请告诉我如何做到这一点或者您是否有任何其他解决方案 请记住 这应该是在 Java 知识水平较低的情况下完成的 提前致谢 附 测试仪字符串
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • 路径中 File.separator 和斜杠之间的区别

    使用有什么区别File separator和一个正常的 在 Java 路径字符串中 与双反斜杠相反 平台独立性似乎不是原因 因为两个版本都可以在 Windows 和 Unix 下运行 public class SlashTest Test
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • 加密 JBoss 配置中的敏感信息

    JBoss 中的标准数据源配置要求数据库用户的用户名和密码位于 xxx ds xml 文件中 如果我将数据源定义为 c3p0 mbean 我会遇到同样的问题 是否有标准方法来加密用户和密码 保存密钥的好地方是什么 这当然也与 tomcat
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • Eclipse Java 远程调试器通过 VPN 速度极慢

    我有时被迫离开办公室工作 这意味着我需要通过 VPN 进入我的实验室 我注意到在这种情况下使用 Eclipse 进行远程调试速度非常慢 速度慢到调试器需要 5 7 分钟才能连接到远程 jvm 连接后 每次单步执行断点 行可能需要 20 30
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 玩!框架:运行“h2-browser”可以运行,但网页不可用

    当我运行命令时activator h2 browser它会使用以下 url 打开浏览器 192 168 1 17 8082 但我得到 使用 Chrome 此网页无法使用 奇怪的是它以前确实有效 从那时起我唯一改变的是JAVA OPTS以启用
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List

随机推荐

  • 生成性对抗网络(GAN) 和styleGan

    生成性对抗网络 GAN 是机器学习中一个相对较新的概念 于2014年首次引入 他们的目标是合成与真实图像无法区分的人工样本 如图像 GAN应用程序的一个常见示例是通过从名人面孔数据集学习来生成人造人脸图像 随着时间的推移 GAN图像变得更加
  • C#面试题

    1 维护数据库的完整性 一致性 你喜欢用触发器还是自写业务逻辑 为什么 答 尽可能用约束 包括CHECK 主键 唯一键 外键 非空字段 实现 这种方式的效率最好 其次用触发器 这种方式可以保证无论何种业务系统访问数据库都能维持数据库的完整性
  • telnet传输文件:telnet登录Linux后通过busybox ftpget获取远程文件

    telnet传输文件 telnet登录Linux后通过busybox ftpget获取远程文件 文章目录 telnet传输文件 telnet登录Linux后通过busybox ftpget获取远程文件 1 场景 2 telnet登录 3 b
  • 【TIP】已经上架的app在AppStore上搜不到的解决办法

    appstore上架后搜不到APP 修改定价 将你的app定价修改成0 99刀 修改你的发行范围 全取消后只选中国 save 这时候你的app status将会变成pending contract 将之前的修改都改回来 修改定价free 全
  • Mac系统下Android studio配置环境变量(ADB、JDK、GRADLE、Flutter)

    mac os 启动台 gt 终端 进入当前用户的home目录 默认 cd 若 bash profile文件不存在则创建 touch bashrc 名字可以自己定义 bash profile 打开 bash profile 文件不存在则创建则
  • 第六章 系统总线

    http eduunix ccut edu cn index2 edu C7 E5 BB AA B4 F3 D1 A7 BC C6 CB E3 BB FA BF CE B3 CC CE A2 D0 CD BC C6 CB E3 BB FA
  • 阿里P8精心整理的微服务系统架构设计手册,一睹微服务架构世界

    近几年 微服务架构在大量技术社区迅速蹿红 被认为是 IT 软件架构的未来方向 一线互联网公司由于具有大量的业务体量和业务场景 比如阿里 百度 网易 很早就开始入坑微服务架构 随着云端办公以来 发现微服务越来越重要了 Docker 容器技术和
  • MatConvNet:3.代码(一)cnn_mnist.m注释

    原文链接 https blog csdn net qq 20259459 article details 54411178 博主博客地址 http blog csdn net qq 20259459 作者邮箱 jinweizhi93 gma
  • 用函数完成两个数相加(用两个方法实现)

    用函数完成两个数相加 1 方法一 int f1 int x int y 声明函数 定义函数 int z z x y return z include
  • JAVA 敏感词过滤

    package me mymilkbottles import org apache commons lang CharUtils import java io File import java util HashMap import ja
  • 基于vue+leaflet+echart的足迹分享评论平台

    其实题目是随便取的 目的只是用来证明Vue leaflet springboot技术栈的可行性 效果 小专栏不支持上传视频 想看的话可以去我的知乎看最新的文章 那个应该可以 在这里 主要功能描述 vue leaflet结合 足迹管理 新建
  • python编程-2.编写程序,输出所有由1,2,3,4这四个数字组成的素数,并且每个数字在素数中只出现一次。

    data用于存储在一定范围内的素数 data set for n in range 1234 4321 1 if n 2 0 continue for i in range 3 int n 0 5 1 2 if n i 0 break el
  • 组合逻辑电路——编码器

    组合逻辑电路 编码器 概念 编码的概念 在数字系统中 常需要将有特定意义的信息编成二进制代码 这一过程称为编码 编码器 实现编码的数字电路被称为编码器 二进制编码器 这里我们采用与非门来设计二进制编码器 二进制编码器输出端数量不定 可以根据
  • ACM MM 2022

    有预训练 460多m 来源丨https zhuanlan zhihu com p 547671620 Bidirectional Self Training with Multiple Anisotropic Prototypes for
  • Glide使用及原理分析

    文章目录 前言 一 Glide的基本使用 二 Glide的网络请求 1 HttpURLConnection实现一个原生图片加载框架 2 Glide为什么能监听网络变化 三 Glide的生命周期 1 Fragment的生命周期 动态加载Fra
  • 解决线程安全问题的三种方法

    解决线程安全问题的三种方法 一 使用同步代码块 如 卖票案例 出现了线程安全 重复的票不能出现 步骤 成员位置建立锁对象 synchronized 锁对象 出现安全问题代码 1 锁对象 任意对象 2 必须保证多个线程使用的是同一个锁对象 3
  • pip配置问题解决-如何使用修改windows系统环境变量

    问题发现 在使用pip安装环境的时候 出现了如下的问题 解决办法 我是在windows系统环境变量上添加上python的Scripts文件夹路径 将其放到环境变量的path中去 操作如下 右键我的电脑 点开属性 在最下面的 关于 上 找到
  • 力扣:删除链表中的节点

    237 删除链表中的节点 请编写一个函数 用于 删除单链表中某个特定节点 在设计函数时需要注意 你无法访问链表的头节点 head 只能直接访问 要被删除的节点 题目数据保证需要删除的节点 不是末尾节点 示例 1 输入 head 4 5 1
  • 区块链节点和区块区别_区块链的常识之,区块链节点,是什么?

    专业科普 区块链节点 通常指的是区块链网络中的计算机 也就是说任何连接到区块链网络的计算机 包括手机 矿机等 都称为节点 比如说比特币网络 是一个公有链 用户在自己的联网电脑上运行比特币程序时 这个电脑就成为比特币区块链网络中的一个节点 是
  • 利用栈实现简单表达式求值

    简单表达式求值 关键点 首先明确要使用的数据结构 本文采用栈来实现 为了分别操作数字和运算符 采用双栈 一个数值栈和一个运算符栈 根据栈顶运算符和待入栈运算符的优先级的判断 产生中间结果 而中间结果作为最终结果的一部分需要再次入栈 栈顶运算