[Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)
Entry类 这个类对表达式的合法性进行了粗筛:
package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; // 此类用于把算术表达式送入解析器 public class Entry { public static void main(String[] args) throws IOException{ // 取得用户输入的表达式 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String rawExpression = null; System.out.print("请输入算术表达式:"); rawExpression = br.readLine(); // 得到合法的算术表达式 String expression=""; for(int i=0;i<rawExpression.length();i++){ // 拿到表达式的每个字符 char c=rawExpression.charAt(i); //System.out.print(c+","); if(Character.isDigit(c) || c=='+' || c=='-' || c=='*' || c=='/' || c=='(' || c==')' || c=='.'){ //System.out.print(c); expression+=c; }else{ System.out.print(" "+c+"不是合法的算术表达式字符."); System.exit(0); } } // 送去解析 Lexer p=new Lexer(expression); //p.print(); // Tree t=new Tree(p.getInfixList()); try { System.out.println(t.evaluate()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
执行结果 以下测试用例都通过了:
请输入算术表达式:1+2+3 6.0 请输入算术表达式:1+2*3 7.0 请输入算术表达式:2*(3+4) 14.0 请输入算术表达式:1+2*(6-4) 5.0 请输入算术表达式:1+2*(5-4)+6-7 2.0 请输入算术表达式:(1+2)*3-4*(6-5) 5.0 请输入算术表达式:(1+2)*(3+4) 21.0 请输入算术表达式:1.1*5+(3+4)*10 75.5
Lexer类 这个类起词法分析器的作用,其核心利器是正则表达式,分词完毕后得到一个含有中序表达式的列表,如 ”1.2,+,3,*,4“:
package com.hy; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; // 此类用于将算术表达式解析成包含操作数和操作符的链表,扮演分词器的角色 public class Lexer { private List<String> list;// 用于存储中序表达式的链表 public List<String> getInfixList() { return list; } public Lexer(String expression){ list=new ArrayList<String>(); // 使用正则表达式分词 String regExp = "(\\d+(\\.*)\\d*)|(\\+)|(\\-)|(\\*)|(\\/)|(\\()|(\\))"; Pattern pattern=Pattern.compile(regExp); Matcher matcher=pattern.matcher(expression); while(matcher.find()){ list.add(matcher.group(0)); } } public void print(){ for(String str:list){ System.out.println(str); } } }
Tree类 输入一个中序表达式列表,得到构建好的树,这棵树就是算术表达式的语法树。递归的build函数是本类代码的核心:
package com.hy; import java.util.List; // 以算术表达式为基础构建一棵二叉树 public class Tree { // 根节点 private Node root; // infixList为分词完毕的中序表达式列表 public Tree(List<String> infixList){ root=build(infixList,0,infixList.size()); } // 构建一棵树,从根节点构建起 private Node build(List<String> list,int start,int end){ int depth=0;//记录深度,进一层括号加一,退出来减一 int plusDivideRightmostPos=-1;// 记录最右边的加减号位置 int multiDivideRightmostPos=-1;// 记录最右边的乘除号位置 // 操作数 if(start==end-1){ // 下标相差一,说明找到的是没有子节点的叶子节点,也即操作数节点 Node leafNode=new Node(NodeType.Digit,list.get(start)); return leafNode; } // 这个循环是为了找括号外最右边的运算符位置 for(int i=start;i<end;i++){ String operatorText=list.get(i);// 获得操作符的文字,如果是操作数直接ignore if(operatorText.equals("(")){ depth++; }else if(operatorText.equals(")")){ depth--; }else if(operatorText.equals("+") || operatorText.equals("-") ){ if(depth==0){ plusDivideRightmostPos=i; } }else if(operatorText.equals("*") || operatorText.equals("/") ){ if(depth==0){ multiDivideRightmostPos=i; } } } int rightMost=-1; if(plusDivideRightmostPos==-1 && multiDivideRightmostPos==-1){ // 整个算式被多余的括号括起来了,去掉这层多余的括号再做 return build(list,start+1,end-1); } // 优先取加减号的位置,因为它的计算优先级最低,应当最后算 rightMost=plusDivideRightmostPos; if(plusDivideRightmostPos==-1 && multiDivideRightmostPos>0){ // 括号外只有乘除号,如(1+2)*(3+4),这时只有取乘除号位置, rightMost=multiDivideRightmostPos; } // 如果能走到这里,则最右边括号外的运算符位置已经找到了,可以开始构建节点 String operator=list.get(rightMost); Node nodeOper=new Node(operator);// 这里创建的节点都是操作符节点,不是最终的叶子节点 // 以最右边的操作符为界,分两侧构建左右子节点 nodeOper.setLeftNode(build(list,start,rightMost)); nodeOper.setRightNode(build(list,rightMost+1,end)); // 返回构建完的节点 return nodeOper; } // 取二叉树的值 public float evaluate() throws Exception{ return this.root.getValue(); } }
Node类 这是如教科书似的二叉树定义:
package com.hy; // 二叉树节点类 public class Node { private NodeType type; private float value; private Node leftNode;// 左节点 private Node rightNode;// 右节点 public Node(){ type=NodeType.Undifined; value=0.0f; leftNode=null; rightNode=null; } public Node(String nodeTypeText){ if(nodeTypeText.equals("+")){ this.type=NodeType.OP_Plus; }else if(nodeTypeText.equals("-")){ this.type=NodeType.OP_Minus; }else if(nodeTypeText.equals("*")){ this.type=NodeType.OP_Multi; }else if(nodeTypeText.equals("/")){ this.type=NodeType.OP_Divide; }else{ this.type=NodeType.Undifined; } value=0.0f; leftNode=null; rightNode=null; } public Node(NodeType type){ this.type=type; value=0.0f; leftNode=null; rightNode=null; } public Node(NodeType type,String str){ this.type=type; this.value=Float.valueOf(str); leftNode=null; rightNode=null; } public Node(NodeType type,float value,Node leftNode,Node rightNode){ this.type=type; this.value=value; this.leftNode=leftNode; this.rightNode=rightNode; } public Node(NodeType type,Node leftNode,Node rightNode){ this.type=type; this.value=0; this.leftNode=leftNode; this.rightNode=rightNode; } public float getValue() throws Exception{ if(this.type==NodeType.Digit){ return value; }else if(this.type==NodeType.OP_Divide){ return leftNode.getValue()/rightNode.getValue(); }else if(this.type==NodeType.OP_Minus){ return leftNode.getValue()-rightNode.getValue(); }else if(this.type==NodeType.OP_Multi){ return leftNode.getValue()*rightNode.getValue(); }else if(this.type==NodeType.OP_Plus){ return leftNode.getValue()+rightNode.getValue(); }else{ throw new Exception("Not initialize"); } } public void setLeftNode(Node leftNode) { this.leftNode = leftNode; } public void setRightNode(Node rightNode) { this.rightNode = rightNode; } public Node getLeftNode() { return leftNode; } public Node getRightNode() { return rightNode; } public String toString(){ if(this.type==NodeType.Digit){ return String.valueOf(value)+" "; }else if(this.type==NodeType.OP_Divide){ return "/ "; }else if(this.type==NodeType.OP_Minus){ return "- "; }else if(this.type==NodeType.OP_Multi){ return "* "; }else if(this.type==NodeType.OP_Plus){ return "+ "; }else{ return "? "; } } public NodeType getType() { return type; } public void setType(NodeType type) { this.type = type; } public void setValue(float value) { this.value = value; } }
NodeType枚举
package com.hy; // 节点类型 public enum NodeType { Undifined, OP_Plus, OP_Minus, OP_Multi, OP_Divide, Digit, }
到这里,将算术表达式求值的两种方式--转后序表达式或二叉树求值都已经实现过了,虽然其词法分析器和语法分析器还稚嫩,但万丈高楼总需平地起。
参考文献:
1.算术表达式构造二叉树; 二叉树计算算术表达式 https://blog.csdn.net/qq120848369/article/details/5673969
2.算数表达式--二叉树 https://www.cnblogs.com/gw811/archive/2012/10/12/2720777.html
--END-- 2019年9月4日11点08分
[Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)的更多相关文章
- 表达式求值(noip2015等价表达式)
题目大意 给一个含字母a的表达式,求n个选项中表达式跟一开始那个等价的有哪些 做法 模拟一个多项式显然难以实现那么我们高兴的找一些素数代入表达式,再随便找一个素数做模表达式求值优先级表 - ( ) + ...
- [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)
Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...
- [Java]算术表达式求值之一(中序表达式转后序表达式方案)
第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html 入口类,这个类的主要用途是粗筛用户输入的算术表达式: package com.h ...
- java实现算术表达式求值
需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...
- [Java]将算术表达式(中序表达式Infix)转成后续表达式Postfix
Inlet类: package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.In ...
- C语言中缀表达式求值(综合)
题前需要了解的:中缀.后缀表达式是什么?(不知道你们知不知道,反正我当时不知道,搜的百度) 基本思路:先把输入的中缀表达式→后缀表达式→进行计算得出结果 栈:"先进先出,先进后出" ...
- 【zzuli-1923】表达式求值
题目描述 假设表达式定义为:1. 一个十进制的正整数 X 是一个表达式.2. 如果 X 和 Y 是 表达式,则 X+Y, X*Y 也是表达式; *优先级高于+.3. 如果 X 和 Y 是 表达式,则 ...
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
- [Java]算术表达式组建二叉树,再由二叉树得到算式的后序和中序表达式
Entry类: package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.In ...
随机推荐
- css and canvas实现圆形进度条
进度条效果: 话不多说,上代码 使用css动画实现,看到一篇博客的启发,稍微修改了下, css实现的原理是用两个半圆一开始隐藏,再分别旋转180度,最后成为一个整圆 半圆效果,一开始右边的半圆在盒 ...
- Delphi BuildCommDCBAndTimeouts函数
- Oracle【三表的联合查询】
,'北京','彰显大气'); ,'上海','繁华都市'); ,'广州','凸显舒适'); ,'深圳','年轻气氛'); ,'北上广深','不相信眼泪'); commit; ; ; ; ; ; 员工信息 ...
- CentOS7 xrdp 安装和设置
1) 安装 $ sudo yum install xrdp $ sudo yum install tigervnc $ sudo yum install tigervnc-server 2) 设置密码 ...
- slplunk原始数据和索引数据大小比较
DB目录总大小:2468MB 所有buckets的meta信息在.bucketManifest文件里: id,path,"raw_size","event_count&q ...
- Springboot + Mybatis + Ehcache
最近在做一个项目,为处理并发性较差的问题,使用了Mybatis二级缓存 但在多表联合查询的情况下,Mybatis二级缓存是存在着数据脏读的问题的 两天就是在想办法解决这个数据脏读的问题 考虑到简易性. ...
- Kafka、RabbitMQ、RocketMQ等 消息中间件 介绍和对比
文章目录 1.前言 2.概念 2.1.MQ简介 2.2.MQ特点 2.2.1.先进先出 2.2.2.发布订阅 2.2.3.持久化 2.2.4.分布式 3.消息中间件性能究竟哪家强? 3.1.Kafka ...
- BZOJ1030 [JSOI2007]文本生成器[DP+AC自动机]
我学到现在才是初三学弟的水平..哭 这里相当于求长度为$m$的,字符集$\{A...Z\}$的且不包含任一模式串的文本串个数.这是一个典型的AC自动机匹配计数问题. 设$f_{i,j}$表示在AC自动 ...
- curses is not supported on this machine:(curses 在pycharm(Windows)中的安装 )
curse在Windows下的pycharm中安装,curse是不能直接在Windows下跑的.需要安装相关环境,要根据直接project的编译器版本来选择下载相关的whl. 找到project的Sc ...
- ip端口协议,SSHj加密连接
SSH协议----一旦linux服务器开机,SSH协议就不断的侦听22端口,一旦有连接到达,就校验连接用户名和密码: 可以理解为连接ip端口为找到一栋大楼,发送用户名和密码为向大楼保安出示证件.SSH ...