当前位置: 首页 > news >正文

【表达式求值算法】拆解复杂问题:实现计算器

        表达式求值算法是个 Hard 级别的问题,本次就来解决它。最终实现一个包含如下功能的计算器:

        1.输入一个字符串,可以包含 + - * / 、数字、括号以及空格,算法返回运行结果。

        2.要符合运算规则,括号的优先级最高,先乘除后加减。

        3.除号是整数除法,无论正负都向0取整(5/2=2,-5/2=-2)。

        4.可以假定输入的算式一定合法,且计算过程不会出现整型溢出,不会出现除数为0的意外情况。

        比如输入如下字符串,算法会返回9:

        “3 * (2-6 / (3 - 7))”

        其关键在于层层拆解问题,化整为零,逐个击破,相信这种思维方式能帮助大家解决各种复杂问题。

        下面就来拆解,先从最简单的一个问题开始。

处理加减法

        如果输入的算式只包含加减法,而且不存在空格,以字符串算式“1-12+3”为例,来说一个很简单的思路:

        1.先给第一个数字加一个默认符号“+”,变成“+1-12+3”。

        2.把一个运算符和数字组成一对,也就是三对 +1,-12,+3,把它们转化成数字,然后放到一个栈中。

        3.将栈中所有的数字求和,就是原算式的结果。

        直接看代码:

import java.util.Stack;public class Calculate {public int calculate(String s) {Stack<Integer> stack = new Stack();// 记录 num 前的符号,初始化为+char sign = '+';// 记录算式中的数字int num = 0;for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);// 如果是数字,连续读取出来if (c >= '0' && c <= '9') {num = 10 * num + (c - '0');}// 如果不是数字,就是遇到了下一个符号,之前的数字和符号就要入栈if ((c < '0' || c > '9') || i == s.length() - 1) {switch (sign) {case '+':stack.push(num);break;case '-':stack.push(-num);break;}// 更新符号为当前符号,数字清零sign = c;num = 0;}}// 将栈中所有结果求和就是答案int res = 0;while (!stack.empty()) {res += stack.pop();}return res;}public static void main(String[] args) {Calculate calculate = new Calculate();int res = calculate.calculate("1-12+3");System.out.println(res);}
}

 处理乘除法

        思路和仅处理加减法没什么区别,拿字符串“2-3*4+5”举例,核心思路依然是把字符串分解成符号和数字的组合。

        比如上述例子就可以分解为 +2,-3,*4,+5几对,其他部分都不用变,在 switch 部分加上对应的 case 就行了。乘除法优先于加减法体现在:乘除法可以和栈顶的数结合然后把结果加入栈;而加减法只能把自己放入栈。空格不是运算符,加个条件直接忽略就行:

import java.util.Stack;public class Calculate {public int calculate(String s) {Stack<Integer> stack = new Stack();// 记录 num 前的符号,初始化为+char sign = '+';// 记录算式中的数字int num = 0;// 记录栈顶元素int pre = 0;for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);// 如果是数字,连续读取出来if (c >= '0' && c <= '9') {num = 10 * num + (c - '0');}// 如果不是数字,就是遇到了下一个符号,之前的数字和符号就要入栈if ((c < '0' || c > '9') && c != ' ' || i == s.length() - 1) {switch (sign) {case '+':stack.push(num);break;case '-':stack.push(-num);break;case '*':pre = stack.pop();stack.push(pre * num);break;case '/':pre = stack.pop();stack.push(pre / num);break;}// 更新符号为当前符号,数字清零sign = c;num = 0;}}// 将栈中所有结果求和就是答案int res = 0;while (!stack.empty()) {res += stack.pop();}return res;}public static void main(String[] args) {Calculate calculate = new Calculate();int res = calculate.calculate("2-3*4-5");System.out.println(res);}
}

处理括号

        这里就到难点了,但其实一点都不难,因为括号具有递归性质,无论括号嵌套多少层,都可以用一个递归搞定。括号包含的算式,直接视为一个数字就行了。(leetcode例题:224.基本计算器)

        话不多说,直接上代码:

import java.util.Stack;public class Calculate {public int calculate(String s) {Stack<Character> stack = new Stack();for (int i = s.length() - 1; i >= 0; i--) {stack.push(s.charAt(i));}return helper(stack);}public int helper(Stack<Character> stack) {Stack<Integer> nums = new Stack<>();// 记录 num 前的符号,初始化为+char sign = '+';// 记录算式中的数字int num = 0;// 记录栈顶元素int pre = 0;while (!stack.empty()) {char c = stack.pop();if (c >= '0' && c <= '9') {num = 10 * num + (c - '0');}// 遇到左括号开始递归计算if (c == '(') {num = helper(stack);}// 如果不是数字,就是遇到了下一个符号,之前的数字和符号就要入栈if ((c < '0' || c > '9') && c != ' ' || stack.empty()) {switch (sign) {case '+':nums.push(num);break;case '-':nums.push(-num);break;case '*':pre = stack.pop();nums.push(pre * num);break;case '/':pre = stack.pop();nums.push(pre / num);break;}// 更新符号为当前符号,数字清零sign = c;num = 0;}if (c == ')') {break;}}// 将栈中所有结果求和就是答案int res = 0;while (!nums.empty()) {res += nums.pop();}return res;}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Rust调用tree-sitter解析C语言
  • 11 - TCPClient实验
  • 中断合并参数coalesce_params解释
  • StringReader 使用 JAXB自动将 XML 数据映射到 Java 对象
  • 3. 轴指令(omron 机器自动化控制器)——>MC_MoveVelocity
  • WebGL阴影与后期处理
  • 前端vue-作用域插槽的传值,子传父,父用obj对象接收
  • 【SpringBoot详细教程】-03-整合Junit【持续更新】
  • Go基础学习04-变量重声明;类型转换;类型断言;Unicode代码点;类型别名;潜在类型
  • 毕业设计选题:基于ssm+vue+uniapp的校园失物招领小程序
  • 《MATLAB项目实战》,专栏目录和介绍
  • 华为驱动未卸载导致内存完整性无法开启,导致lol卡顿,后台十几个重复进程
  • Pytorch实现Transformer
  • React Native 在 build 的时候如果出现 `babel.config.js` 配置文件的错误
  • 量子计算如何引发第四次工业革命——解读加来道雄的量子物理观
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • go append函数以及写入
  • HTML5新特性总结
  • Java多态
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • js 实现textarea输入字数提示
  • JS学习笔记——闭包
  • Octave 入门
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • 动态规划入门(以爬楼梯为例)
  • 构建二叉树进行数值数组的去重及优化
  • 漂亮刷新控件-iOS
  • 什么软件可以剪辑音乐?
  • 算法-插入排序
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 听说你叫Java(二)–Servlet请求
  • 微信小程序设置上一页数据
  • 一些关于Rust在2019年的思考
  • 由插件封装引出的一丢丢思考
  • 原生js练习题---第五课
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • Hibernate主键生成策略及选择
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • 从如何停掉 Promise 链说起
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (算法)硬币问题
  • (一一四)第九章编程练习
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转载)深入super,看Python如何解决钻石继承难题
  • *算法训练(leetcode)第三十九天 | 115. 不同的子序列、583. 两个字符串的删除操作、72. 编辑距离
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .Net Core 微服务之Consul(二)-集群搭建