[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 ...
随机推荐
- U盘加载速度慢的解决方法
在日常的生活和工作中,我们经常用U盘存储一些文件和程序.然而,一些朋友发现U盘有时候在使用过程中的识别加载速度非常缓慢.是U盘出故障了吗?其实不尽然,下面就为大家分享一下如何快速解决U盘加载缓慢的方法 ...
- Win10系统的开机启动项如何去关闭?
我们在使用电脑时会安装许多的应用程序,而有些应用程序会默认在电脑开机时自行启动,不仅影响开机速度,还会在开机后占用电脑内存资源. 那么在Win10系统中,我们该如何查看有哪些开机启动项呢?我们又该怎么 ...
- 采用kubeadm部署工具,部署kubernetes1.16.3
安装kubenetes有5种部署工具,分别是kubeadm.kops.KRIB.Kubespray.本实验采用的是kubeadm部署工具.如有想了解其他部署工具,请点击这里 环境说明 角色/主机名 系 ...
- SQL语句复习【专题八】
SQL语句复习[专题八] 序列 Sequence.数据库对象是 oracle 专有的.作用:可以将某一列的值使用序列,来实现自动增长的功能.访问序列的值.[序列有两个属性 nextval currva ...
- linux 启动tomcat
操作步骤: 第一步:进入tomcat的bin目录 cd /usr/local/tomcat/bin 第二步:使用tomcat关闭命令 ./shutdown.sh 第三步:查看tomcat是否关闭 ps ...
- 自动匹配输入的内容(AutoCompleteTextView及MultiAutoCompleteTextView)
自动匹配输入的内容 AutoCompleteTextView 1.功能动态匹配输入的内容,如百度搜索引擎当输入文本时,可以根据内容显示匹配的热门信息 2.属性:android:completionTh ...
- java 发布订阅
https://www.cnblogs.com/coderdxj/p/9627310.html java 观察这模式(发布订阅模式) 观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象 ...
- Python之subprocess模块的使用
1.subprocess调用系统的命令 #!/usr/bin/env python # -*- coding: utf-8 -*- import subprocess import sys compl ...
- shareSDK集成遇到的问题汇总
问题一.平台添加应用时需要输入的签名(下图)的获取方式 第一步.通过android studio生成签名保存在本地 第二步.查看签名的信息 问题二.由于同事集成了QQ登录和微信登录,后来又需要加入微博 ...
- [Python]token的生成及验证
hmac模块(仅在python3中可以使用) 简介: HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出. 典型应用: HMAC的一 ...