中缀表达式求解

中缀表达式就是我们常见的算术表达式。例如1+2-3这样的式子,在计算机中,我们想要表示这种式子可以用字符串来承载,表示是不费力气的,但是如何求解呢?其实中缀表达式求解问题的思想也很简单。首先我们可以将式子的组成元素拆分为算符数据两部分。算符就是+-*/()之类的运算符,数据就是结合在算符左右的数字。我们可以看到,算符有一元运算符,也有二元运算符。所谓一元二元,就是算符要作用于几个数据,例如+运算符,它就要结合两个数据进行运算,所以+是一个二元算符。

除此之外并不所有的算符都要结合与数据,有些算符可以用来调整优先级使用,例如()。没错算符之间是有优先级次序的,这也是烦恼我们对中缀表达式求解的关节点。如何正确的根据算符顺序来计算中缀表达式呢?

我们的运算法则规定,先乘除后加减。也就是乘和除的优先级要高于加和减。例如1 + 2 * 3 + 4。如果我们逐一的遍历字符串进行运算,显然会得到的一个错误的结果。我们应该先算2*3然后再作加法运算。实际上,进过一番分析你就会发现,根据我们上面所说的将算符和数据分离的思想,一个算符它可以运算的时机便是,它能够在局部保障一种优先级最高的状态

就像是例子中说的那样,为什么我们要先算乘法,因为乘运算符在局部保持了一种局部优先级最高的状态,一旦一个算符在局部处于一种优先级较高的状态,那么就说明,这个算符可以进行运算了。

算符的运算就要根据算符的性质,如果是二元算符,那么他就要结合两个数据,然后进行算符相应的运算。

从我们的分析中也可以得到,确定算符是否是局部最高优先级状态的这一操作,具有滞后性。换言之,我们当我们面临一个算符时,我们无法立刻知道他是否处于一个局部优先级最高的状态,我们需要根据它之前的算符,和它之后的算法来共同确定,这个算符在局部中的一个优先级状况。

谈到滞后性,第一让我想到的便是利用回溯的方式来解决,那么栈是解决这类问题的一个不错的方案。

算法实现

  • 准备工作

    首先我们需要准备两个栈结构,一个用于存储数据,一个用于存储算符。其次我们还需要实现对于数字型字符串转数字的函数,用于对中缀表达式字串进行解析。

  • 算法的基本思想便是,如果遇到数字型字符串,将其转换为数字,放入数据栈中,如果是算符型字符串,那么我们先判断算符的优先级,如果算符在局部的优先级能够达到可运算的水准,那么我们就将数据栈中的数据弹出,然后进行相应的运算,运算完毕之后重新放入数据栈。

  1. 初始状态时,数据栈与算符栈皆为空栈。我们看到了,当前是一个字符型的数字,于是将其放入栈中。
  2. 很快我们遇到了第一个算符,我们发现算符栈中还没有算符,也就是说,当前算符在局部的优先级还不能确定,既然不能确定,那么就先将其入栈。
  3. 紧接着,我们遇到了第二个算符,我们比较发现,此时的算符优先级,要高于栈中的算符优先级,也就是说,栈中的算符此时已然不具备局部优先级最高的条件了,那么我们可以先不考虑它了,先考虑这时字符指针指向的这个算符,我们发现,*号这个算符优先级显然高于+,但是我们人不能确定,*算符是否已经达到一个局部优先级最高的状态,因为我们还不知道下一个遇到的算符是什么,所以我们可以先将*运算符入栈。
  4. 接下来,我们发现此时字符指针指向+运算符,这时我们发现,栈中的*算符已经达到了我们之前说的在局部优先级最高的一个状态,那么此时的状态就说明,我们的*算符可以进行运算了。
  5. 接下来,栈中我们发现,算符栈只剩下一个算符,而目前字符指针指向的算符与栈中字符优先级相等,那么此时栈中算符的状态也处于一种局部优先级最高的状态。
  6. 算符栈空了,那么当前指针指向的算符可以入栈了。

    最后字符串遍历完了,我们继续将算符栈清空,重复之前的操作。最终数据栈会只剩下一个元素,这个元素便是最终的结果11

一点小改进

为了使我们的算法实现起来比较优雅,我们可以在遍历表达式之前,现在算符栈中推入一个(算符,然后再将我们的表达式末尾加上一个)算符,这个操作不会影响最终结果,毕竟1 + 2 * 3 + 4 = (1 + 2 * 3 + 4)然后我们可以自定义一个算符优先级比对表,进而进行算符优先级的比对。

Java语言实现

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;public class Test2 {static enum Action {PUSH, /* 将算符入栈,移动字符指针 */POP_AND_CALC, /* 算符出栈并计算,字符指针不移动 */ERR, /* 不合理的情况 */POP_AND_NEXT /* 算符出栈,移动字符指针(主要是为了`)`算符) */}static class ResultAndIndex {double result = 0.0;int index = 0;}private static Action[][] actionTable = new Action[][]{{Action.POP_AND_CALC, Action.POP_AND_CALC, Action.POP_AND_CALC, Action.POP_AND_CALC,Action.PUSH,Action.ERR},{Action.POP_AND_CALC, Action.POP_AND_CALC, Action.POP_AND_CALC, Action.POP_AND_CALC,Action.PUSH,Action.ERR},{Action.PUSH, Action.PUSH, Action.POP_AND_CALC, Action.POP_AND_CALC, Action.PUSH, Action.ERR},{Action.PUSH, Action.PUSH, Action.POP_AND_CALC, Action.POP_AND_CALC, Action.PUSH, Action.ERR},{Action.PUSH, Action.PUSH, Action.PUSH, Action.PUSH,Action.PUSH,Action.ERR},{Action.POP_AND_CALC,Action.POP_AND_CALC,Action.POP_AND_CALC,Action.POP_AND_CALC,Action.POP_AND_NEXT,Action.ERR}};private static HashMap<Character, Integer> operatorIndexMap = new HashMap<>();private static Deque<Short> iStack = new ArrayDeque<>();private static Deque<Short> fStack = new ArrayDeque<>();private static Deque<Character> operatorStack = new ArrayDeque<>();private static Deque<Double> dataStack = new ArrayDeque<>();/* 编码运算符 */static{operatorIndexMap.put('+', 0);operatorIndexMap.put('-', 1);operatorIndexMap.put('*', 2);operatorIndexMap.put('/', 3);operatorIndexMap.put('(', 4);operatorIndexMap.put(')', 5);}/* 从字符串中解析出一个实数 */private static ResultAndIndex nextRealNumber(String str, int startIndex) {ResultAndIndex res = new ResultAndIndex();boolean isDouble = false;char ch;int i, strLen = str.length(),index = startIndex;while (index < strLen) {ch = str.charAt(index);if (ch >= '0' && ch <= '9') {if (isDouble) {fStack.addFirst((short)(ch - '0'));} else {iStack.addFirst((short)(ch - '0'));}}else if (ch == '.') {isDouble = true;} else {break;}index++;}i = 0;while (!iStack.isEmpty()) {res.result += iStack.pollFirst() * Math.pow(10, i++);}i = 0;while (!fStack.isEmpty()) {res.result += fStack.pollFirst() * Math.pow(10, --i);}res.index = index;return res;}/* 字符是否是数字 */private static boolean isDigit(char ch) {return ch >= '0' && ch <= '9';}/* 字符是否是我们支持的运算符 */private static boolean isSupportOperator(char ch) {return  ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')';}/* 根据指定的算符,进行相应的运算 */private static double calc(char opt) {double res = 0;double var1,var2;if (opt == '+') {var1 = dataStack.pollFirst();var2 = dataStack.pollFirst();res = var1 + var2;} else if (opt == '-') {var1 = dataStack.pollFirst();var2 = dataStack.pollFirst();res = var2 - var1;} else if (opt == '*') {var1 = dataStack.pollFirst();var2 = dataStack.pollFirst();res = var1 * var2;} else if (opt == '/') {var1 = dataStack.pollFirst();var2 = dataStack.pollFirst();res = var2 / var1;}return res;}/* 中缀表达式求解 */private static double mecalc(String content) {content = content + ")";int i = 0, len = content.length();char ch;operatorStack.addFirst('(');/* 循环迭代整个中缀表达式 */while (i < len) {ch = content.charAt(i);if (isDigit(ch)) {ResultAndIndex resultAndIndex = nextRealNumber(content, i);// System.out.println("parse => " + resultAndIndex.result);dataStack.addFirst(resultAndIndex.result);i = resultAndIndex.index;} else if (isSupportOperator(ch)) {int waiting = operatorIndexMap.get(ch);int top = operatorIndexMap.get(operatorStack.peekFirst());// System.out.println(actionTable[waiting][top]);switch (actionTable[waiting][top]) {case PUSH :operatorStack.addFirst(ch);i++;break;case POP_AND_CALC:char opt = operatorStack.pollFirst();double v = calc(opt);dataStack.addFirst(v);break;case POP_AND_NEXT:operatorStack.pollFirst();i++;break;case ERR:throw new RuntimeException();}} else {i++;}}return dataStack.pollFirst();}public static void main(String[] args) {HashMap<String, String> itemAndResult = new HashMap<>();try {/* 从文件中读取中缀表达式 */FileInputStream fis = new FileInputStream("item.txt");Scanner scanner = new Scanner(fis);while (scanner.hasNextLine()) {String line = scanner.nextLine();try {double v = mecalc(line);itemAndResult.put(line, String.valueOf(v));}catch (RuntimeException ex) {itemAndResult.put(line, "?");}}fis.close();/* 将计算结果再输出 */FileOutputStream fos = new FileOutputStream("item.txt");StringBuilder builder = new StringBuilder();for (Map.Entry<String, String> lines : itemAndResult.entrySet()) {builder.append(lines.getKey());builder.append(" = ");builder.append(lines.getValue());builder.append("\n");fos.write(builder.toString().getBytes(Charset.forName("UTF-8")));builder.delete(0, builder.length());}fos.close();} catch (IOException e) {e.printStackTrace();}}}

中缀表达式求解——全网最详细教程相关推荐

  1. Neo4j 全网最详细教程

    Neo4j的语法(3.5版本) 1.公式模式是语法范式 2.代码模式是实例,可按顺序复制执行 3.涉及所有可能的节点.节点类型.属性,关系,关系类型.属性的增删改查 4.学习本教程之后可以学习 neo ...

  2. 快速成为idc服务商----全网最详细教程----1

    本次要讲述的就如题目表述,成为idc服务商教程,而且是速成!成本肯定是有的,但是肯定低于收益! 如果您对idc不了解,我在这里给您简单介绍下: idc服务商 是什么?您知道服务器吗?如果您连服务器都不 ...

  3. Git版本控制器(涵盖GitHub\Gitee码云\GitLab),全网最详细教程

    Git(涵盖GitHub\Gitee码云\GitLab) 第1章 Git 概述 Git 是一个免费的.开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种 项目. Git 易于学习,占地面积 ...

  4. Fiddler抓包配置和使用(全网最详细教程)

    文章目录 Fiddler 基本工作原理 Fiddler界面熟悉 Fiddler设置过滤(抓取指定服务器地址的数据) Fiddler模拟接口测试(自定义请求) Fiddler打断点修改接口请求数据 Fi ...

  5. Tomcat下载安装及配置(全网最新详细教程)

    下载tomcat 下载地址:https://tomcat.apache.org/download-80.cgi 选择下载版本,这里用tomcat8版本来做教程的 根据自己的系统选择相对应的下载 下载完 ...

  6. 手把手教你构建一个web前端项目,全网最详细教程!

    为什么80%的码农都做不了架构师?>>>    1. 选择现成的项目模板还是自己搭建项目骨架 搭建一个前端项目的方式有两种:选择现成的项目模板.自己搭建项目骨架. 选择一个现成项目模 ...

  7. Win10安装Ubuntu18.04双系统,图文详解,全网最详细教程

    博主经历过多次双系统的安装与卸载,所以这次安装就记录下全过程,能让后面的同学少走弯路.本教程对笔记本电脑单硬盘和双硬盘通用. 安装目录 一.查看电脑信息 1.BIOS模式 2.查看硬盘数 二.制作系统 ...

  8. 魅族路由器(极速版)刷老毛子(padavad)固件-全网最详细教程

    写在前面: 魅族路由器极速版,想必有一些网友买过,本人也买过,但是因为魅族官方对路由器已经不维护了,固件万年不更新,插件也用不了了,所以我打着尝试得态度查找了相关刷路由器系统得教程,于是还真让我找到了 ...

  9. Hexo+GitHub搭建个人网站全网最详细教程

    前言 这个故事很长,还要从一只蝙蝠说起! 大二寒假在家闲来无事一直想做一个网站但是苦于没有门路,于是我去各大论坛搜索关于搭建网站的资料,这一搜还真给搜到了,于是这一套Hexo+GitHub搭建个人博客 ...

最新文章

  1. [置顶] ros的navigation之———gmapping应用详解(in ros)
  2. WebStorm V2017.1版用于Angular2开发的环境设置
  3. 网络配置之ifconfig及Ip命令详解
  4. 中文字号转换成英文的字号
  5. C语言实现方差variance计算(附完整源码)
  6. core webapi缩略图_.Net Core WebApi上传图片的两种方式
  7. Maven 单元测试
  8. javaweb----三层架构+
  9. P1082-扩欧模板同余方程【扩欧,数论】
  10. Neo4j:绘制“我的名字是……我在工作”图
  11. android 传感器的学习
  12. 华为鸿蒙全程,华为2020年全线启用鸿蒙系统
  13. Ubuntu 18.04/20.04 部署minikube
  14. .net session超时设置 sessionState的相关属性
  15. 认知无线电网络中的频谱切换理论
  16. DSP芯片TMS320C6678的spi挂载flash启动
  17. 用mui索引实现动态数据仿通讯录的功能
  18. PCIE学习笔记(五)PIO例程设计与仿真分析
  19. 怎么在看视频时保持电脑屏幕不灭,干货到,WIN10如何设置电脑屏幕一直亮着
  20. 没有投屏标志怎么投屏_没有TV投屏标示,手机电脑电视该如何实现投屏

热门文章

  1. 微信小程序获取app.js中的公共数据
  2. 洛谷P2392 kkksc03考前临时抱佛脚
  3. 如何为xshll设置背景图片以及透明度?你要的是不是这种效果?
  4. 【基础篇】nginx四大版本以及nginx安装
  5. 5 Android程序签名打包
  6. 「黑马程序员」微信小程序最新接口
  7. 做服装设计师需要具备哪些技能?
  8. oracle+dg常用命令,oracle DG管理命令备忘录
  9. php7.0安装windows_Windows 7 下如何配置PHP网站运行环境
  10. 2022.7.9 最有意义的战争