Entry类 这个类对表达式的合法性进行了粗筛:

  1. package com.hy;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6.  
  7. // 此类用于把算术表达式送入解析器
  8. public class Entry {
  9. public static void main(String[] args) throws IOException{
  10. // 取得用户输入的表达式
  11. BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  12. String rawExpression = null;
  13. System.out.print("请输入算术表达式:");
  14. rawExpression = br.readLine();
  15.  
  16. // 得到合法的算术表达式
  17. String expression="";
  18. for(int i=0;i<rawExpression.length();i++){
  19. // 拿到表达式的每个字符
  20. char c=rawExpression.charAt(i);
  21. //System.out.print(c+",");
  22.  
  23. if(Character.isDigit(c) || c=='+' || c=='-' || c=='*' || c=='/' || c=='(' || c==')' || c=='.'){
  24. //System.out.print(c);
  25. expression+=c;
  26. }else{
  27. System.out.print(" "+c+"不是合法的算术表达式字符.");
  28. System.exit(0);
  29. }
  30. }
  31.  
  32. // 送去解析
  33. Lexer p=new Lexer(expression);
  34. //p.print();
  35.  
  36. //
  37. Tree t=new Tree(p.getInfixList());
  38. try {
  39. System.out.println(t.evaluate());
  40. } catch (Exception e) {
  41. // TODO Auto-generated catch block
  42. e.printStackTrace();
  43. }
  44. }
  45. }

执行结果 以下测试用例都通过了:

  1. 请输入算术表达式:1+2+3
  2. 6.0
  3.  
  4. 请输入算术表达式:1+2*3
  5. 7.0
  6.  
  7. 请输入算术表达式:2*(3+4)
  8. 14.0
  9.  
  10. 请输入算术表达式:1+2*(6-4)
  11. 5.0
  12.  
  13. 请输入算术表达式:1+2*(5-4)+6-7
  14. 2.0
  15.  
  16. 请输入算术表达式:(1+2)*3-4*(6-5)
  17. 5.0
  18.  
  19. 请输入算术表达式:(1+2)*(3+4)
  20. 21.0
  21.  
  22. 请输入算术表达式:1.1*5+(3+4)*10
  23. 75.5

Lexer类 这个类起词法分析器的作用,其核心利器是正则表达式,分词完毕后得到一个含有中序表达式的列表,如 ”1.2,+,3,*,4“:

  1. package com.hy;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.regex.Matcher;
  6. import java.util.regex.Pattern;
  7.  
  8. // 此类用于将算术表达式解析成包含操作数和操作符的链表,扮演分词器的角色
  9. public class Lexer {
  10. private List<String> list;// 用于存储中序表达式的链表
  11.  
  12. public List<String> getInfixList() {
  13. return list;
  14. }
  15.  
  16. public Lexer(String expression){
  17. list=new ArrayList<String>();
  18.  
  19. // 使用正则表达式分词
  20. String regExp = "(\\d+(\\.*)\\d*)|(\\+)|(\\-)|(\\*)|(\\/)|(\\()|(\\))";
  21.  
  22. Pattern pattern=Pattern.compile(regExp);
  23. Matcher matcher=pattern.matcher(expression);
  24. while(matcher.find()){
  25. list.add(matcher.group(0));
  26. }
  27. }
  28.  
  29. public void print(){
  30. for(String str:list){
  31. System.out.println(str);
  32. }
  33. }
  34. }

Tree类 输入一个中序表达式列表,得到构建好的树,这棵树就是算术表达式的语法树。递归的build函数是本类代码的核心:

  1. package com.hy;
  2.  
  3. import java.util.List;
  4.  
  5. // 以算术表达式为基础构建一棵二叉树
  6. public class Tree {
  7. // 根节点
  8. private Node root;
  9.  
  10. // infixList为分词完毕的中序表达式列表
  11. public Tree(List<String> infixList){
  12. root=build(infixList,0,infixList.size());
  13. }
  14.  
  15. // 构建一棵树,从根节点构建起
  16. private Node build(List<String> list,int start,int end){
  17. int depth=0;//记录深度,进一层括号加一,退出来减一
  18. int plusDivideRightmostPos=-1;// 记录最右边的加减号位置
  19. int multiDivideRightmostPos=-1;// 记录最右边的乘除号位置
  20.  
  21. // 操作数
  22. if(start==end-1){
  23. // 下标相差一,说明找到的是没有子节点的叶子节点,也即操作数节点
  24. Node leafNode=new Node(NodeType.Digit,list.get(start));
  25. return leafNode;
  26. }
  27.  
  28. // 这个循环是为了找括号外最右边的运算符位置
  29. for(int i=start;i<end;i++){
  30. String operatorText=list.get(i);// 获得操作符的文字,如果是操作数直接ignore
  31.  
  32. if(operatorText.equals("(")){
  33. depth++;
  34. }else if(operatorText.equals(")")){
  35. depth--;
  36. }else if(operatorText.equals("+") || operatorText.equals("-") ){
  37. if(depth==0){
  38. plusDivideRightmostPos=i;
  39. }
  40. }else if(operatorText.equals("*") || operatorText.equals("/") ){
  41. if(depth==0){
  42. multiDivideRightmostPos=i;
  43. }
  44. }
  45. }
  46.  
  47. int rightMost=-1;
  48.  
  49. if(plusDivideRightmostPos==-1 && multiDivideRightmostPos==-1){
  50. // 整个算式被多余的括号括起来了,去掉这层多余的括号再做
  51. return build(list,start+1,end-1);
  52. }
  53.  
  54. // 优先取加减号的位置,因为它的计算优先级最低,应当最后算
  55. rightMost=plusDivideRightmostPos;
  56.  
  57. if(plusDivideRightmostPos==-1 && multiDivideRightmostPos>0){
  58. // 括号外只有乘除号,如(1+2)*(3+4),这时只有取乘除号位置,
  59. rightMost=multiDivideRightmostPos;
  60. }
  61.  
  62. // 如果能走到这里,则最右边括号外的运算符位置已经找到了,可以开始构建节点
  63. String operator=list.get(rightMost);
  64. Node nodeOper=new Node(operator);// 这里创建的节点都是操作符节点,不是最终的叶子节点
  65.  
  66. // 以最右边的操作符为界,分两侧构建左右子节点
  67. nodeOper.setLeftNode(build(list,start,rightMost));
  68. nodeOper.setRightNode(build(list,rightMost+1,end));
  69.  
  70. // 返回构建完的节点
  71. return nodeOper;
  72. }
  73.  
  74. // 取二叉树的值
  75. public float evaluate() throws Exception{
  76. return this.root.getValue();
  77. }
  78. }

Node类 这是如教科书似的二叉树定义:

  1. package com.hy;
  2.  
  3. // 二叉树节点类
  4. public class Node {
  5. private NodeType type;
  6. private float value;
  7. private Node leftNode;// 左节点
  8. private Node rightNode;// 右节点
  9.  
  10. public Node(){
  11. type=NodeType.Undifined;
  12. value=0.0f;
  13. leftNode=null;
  14. rightNode=null;
  15. }
  16.  
  17. public Node(String nodeTypeText){
  18. if(nodeTypeText.equals("+")){
  19. this.type=NodeType.OP_Plus;
  20. }else if(nodeTypeText.equals("-")){
  21. this.type=NodeType.OP_Minus;
  22. }else if(nodeTypeText.equals("*")){
  23. this.type=NodeType.OP_Multi;
  24. }else if(nodeTypeText.equals("/")){
  25. this.type=NodeType.OP_Divide;
  26. }else{
  27. this.type=NodeType.Undifined;
  28. }
  29.  
  30. value=0.0f;
  31. leftNode=null;
  32. rightNode=null;
  33. }
  34.  
  35. public Node(NodeType type){
  36. this.type=type;
  37. value=0.0f;
  38. leftNode=null;
  39. rightNode=null;
  40. }
  41.  
  42. public Node(NodeType type,String str){
  43. this.type=type;
  44. this.value=Float.valueOf(str);
  45. leftNode=null;
  46. rightNode=null;
  47. }
  48.  
  49. public Node(NodeType type,float value,Node leftNode,Node rightNode){
  50. this.type=type;
  51. this.value=value;
  52. this.leftNode=leftNode;
  53. this.rightNode=rightNode;
  54. }
  55.  
  56. public Node(NodeType type,Node leftNode,Node rightNode){
  57. this.type=type;
  58. this.value=0;
  59. this.leftNode=leftNode;
  60. this.rightNode=rightNode;
  61. }
  62.  
  63. public float getValue() throws Exception{
  64. if(this.type==NodeType.Digit){
  65. return value;
  66. }else if(this.type==NodeType.OP_Divide){
  67. return leftNode.getValue()/rightNode.getValue();
  68. }else if(this.type==NodeType.OP_Minus){
  69. return leftNode.getValue()-rightNode.getValue();
  70. }else if(this.type==NodeType.OP_Multi){
  71. return leftNode.getValue()*rightNode.getValue();
  72. }else if(this.type==NodeType.OP_Plus){
  73. return leftNode.getValue()+rightNode.getValue();
  74. }else{
  75. throw new Exception("Not initialize");
  76. }
  77. }
  78.  
  79. public void setLeftNode(Node leftNode) {
  80. this.leftNode = leftNode;
  81. }
  82.  
  83. public void setRightNode(Node rightNode) {
  84. this.rightNode = rightNode;
  85. }
  86.  
  87. public Node getLeftNode() {
  88. return leftNode;
  89. }
  90.  
  91. public Node getRightNode() {
  92. return rightNode;
  93. }
  94.  
  95. public String toString(){
  96. if(this.type==NodeType.Digit){
  97. return String.valueOf(value)+" ";
  98. }else if(this.type==NodeType.OP_Divide){
  99. return "/ ";
  100. }else if(this.type==NodeType.OP_Minus){
  101. return "- ";
  102. }else if(this.type==NodeType.OP_Multi){
  103. return "* ";
  104. }else if(this.type==NodeType.OP_Plus){
  105. return "+ ";
  106. }else{
  107. return "? ";
  108. }
  109. }
  110.  
  111. public NodeType getType() {
  112. return type;
  113. }
  114.  
  115. public void setType(NodeType type) {
  116. this.type = type;
  117. }
  118.  
  119. public void setValue(float value) {
  120. this.value = value;
  121. }
  122. }

NodeType枚举

  1. package com.hy;
  2.  
  3. // 节点类型
  4. public enum NodeType {
  5. Undifined,
  6. OP_Plus,
  7. OP_Minus,
  8. OP_Multi,
  9. OP_Divide,
  10. Digit,
  11. }

到这里,将算术表达式求值的两种方式--转后序表达式或二叉树求值都已经实现过了,虽然其词法分析器和语法分析器还稚嫩,但万丈高楼总需平地起。

参考文献:

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]算术表达式求值之三(中序表达式转二叉树方案 支持小数)的更多相关文章

  1. 表达式求值(noip2015等价表达式)

    题目大意 给一个含字母a的表达式,求n个选项中表达式跟一开始那个等价的有哪些 做法 模拟一个多项式显然难以实现那么我们高兴的找一些素数代入表达式,再随便找一个素数做模表达式求值优先级表 - ( ) + ...

  2. [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)

    Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...

  3. [Java]算术表达式求值之一(中序表达式转后序表达式方案)

    第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html 入口类,这个类的主要用途是粗筛用户输入的算术表达式: package com.h ...

  4. java实现算术表达式求值

    需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...

  5. [Java]将算术表达式(中序表达式Infix)转成后续表达式Postfix

    Inlet类: package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.In ...

  6. C语言中缀表达式求值(综合)

    题前需要了解的:中缀.后缀表达式是什么?(不知道你们知不知道,反正我当时不知道,搜的百度) 基本思路:先把输入的中缀表达式→后缀表达式→进行计算得出结果 栈:"先进先出,先进后出" ...

  7. 【zzuli-1923】表达式求值

    题目描述 假设表达式定义为:1. 一个十进制的正整数 X 是一个表达式.2. 如果 X 和 Y 是 表达式,则 X+Y, X*Y 也是表达式; *优先级高于+.3. 如果 X 和 Y 是 表达式,则 ...

  8. 利用栈实现算术表达式求值(Java语言描述)

    利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...

  9. [Java]算术表达式组建二叉树,再由二叉树得到算式的后序和中序表达式

    Entry类: package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.In ...

随机推荐

  1. Android-分享多图到微信好友

    /** * 微信分享(多图片) */ private static void shareWeChatByImgList(String kDescription, List<File> im ...

  2. django概念理解

    STATIC_URL 和 STATICFILES_DIRS 区别  static_url指定浏览器上访问静态文件的url前缀,也就是'/static/'前缀的都会认为是静态文件,django不解析,直 ...

  3. mysql8安装

    1.先卸载当前系统中已安装的mariadb rpm -qa | grep mariadb rpm -e --nodeps 文件名 2.安装mysql依赖包 yum install gcc gcc-c+ ...

  4. 读书笔记《SpringBoot编程思想》

    目录 一. springboot总览 1.springboot特性 2.准备运行环境 二.理解独立的spring应用 1.应用类型 2.@RestController 3.官网创建springboot ...

  5. (九)全志平台Tina系统量产前adb shell设密码的方法

    全志平台Tina系统量产前adb shell设密码的方法[适用范围] 全志平台Tina系统 [问题现象] 通常产品量产后都想要以安全方式封闭adb shell,不允许用户或其他开发者使用,因此需要以安 ...

  6. 内置函数 lambda sorted filter map 递归

    一 lambda 匿名函数 为了解决一些简单的需求而设计的一句话函数 # 计算 n 的 n次方 def func(n): return n**n print(func(10)) f = lambda ...

  7. Type反射遍历类的属性

    <?xml version="1.0" encoding="utf-8" ?> <configuration> <startup& ...

  8. openGL中的gl,glu,glut

    OpenGL函数库相关的API有核心库(gl).实用库(glu).辅助库(aux).实用工具库(glut).窗口库(glx.agl.wgl)和扩展函数库等.gl是核心,glu是对gl的部分封装.glx ...

  9. regex正则

    1 正则表达式基本语法 两个特殊的符号^和$.他们的作用是分别指出一个字符串的开始和结束.例子如下: ^The:表示所有以”The”开始的字符串(”There”,”The cat”等): of des ...

  10. Web界面开发必看!Kendo UI for jQuery编辑功能指南第一弹

    Kendo UI for jQuery最新试用版下载 Kendo UI目前最新提供Kendo UI for jQuery.Kendo UI for Angular.Kendo UI Support f ...