第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html

入口类,这个类的主要用途是粗筛用户输入的算术表达式:

  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 Inlet {
  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==')' ){
  24. //System.out.print(c);
  25. expression+=c;
  26. }else{
  27. System.out.print(" "+c+"不是合法的算术表达式字符.");
  28. System.exit(0);
  29. }
  30. }
  31.  
  32. // 送去解析
  33. Parser p=new Parser(expression);
  34. //p.print();
  35.  
  36. // 转为后序表达式
  37. Trans t=new Trans(p.getList());
  38. //t.print();
  39.  
  40. // 计算结果
  41. Calculator c=new Calculator(t.getPostfixList());
  42. System.out.print(expression+"="+c.getResult());
  43. }
  44. }

算术表达式解析器类,它主要起一个词法分析器的作用,由于算术表达式词法较简单,因此逐字读入处理也能完成任务,他的输入是如23+4*(5+6)这种算术表达式,处理完成以后得到23,+,4,*,(,5,+,6,)这些包含操作数和操作符的列表:

  1. package com.hy;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. // 此类用于将算术表达式解析成包含操作数和操作符的链表
  7. public class Parser {
  8. private List<String> list;// 用于存储表达式的链表
  9.  
  10. public List<String> getList() {
  11. return list;
  12. }
  13.  
  14. public Parser(String expression){
  15. list=new ArrayList<String>();
  16.  
  17. String str="";
  18. for(int i=0;i<expression.length();i++){
  19. char c=expression.charAt(i);
  20.  
  21. if(Character.isDigit(c)){
  22. str+=c;
  23. }else{
  24. if(str.length()>0){// 此判断是因为有+(这种符号相连的情况
  25. //System.out.println(str);
  26. list.add(str);
  27. str="";
  28. }
  29.  
  30. //System.out.println(c);
  31. list.add(String.valueOf(c));
  32. }
  33. }
  34.  
  35. if(str.length()>0){// 此判断是因为可能表达式不是以=结尾
  36. //System.out.println(str);
  37. list.add(str);
  38. str="";
  39. }
  40. }
  41.  
  42. public void print(){
  43. for(String str:list){
  44. System.out.println(str);
  45. }
  46. }
  47. }

将中序表达式转后序表达式的转换类,他接收来自Parser的包含操作符和操作数的列表,然后根据规则将算术表达式转化成后序表达式,利用的数据结构是栈java.util.Statck,转化的规则如下:

见到操作数->直接送到postfixList中
见到操作符->将栈顶输出,直到栈顶优先级小于该操作符,最后把该操作符压入栈
见到左括号 ->入栈
见到右括号 ->将栈中在左括号之后的操作符全部输出
(以上'栈'在代码中指的是Trans类的成员变量Stack)

  1. package com.hy;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.Stack;
  6.  
  7. // 此类用于将中序表达式转译成后序表达式
  8. public class Trans {
  9. private Stack<String> stack;// 用于存储操作符的栈
  10. private List<String> postfixList;// 用于存储后序表达式的链表
  11.  
  12. public List<String> getPostfixList() {
  13. return postfixList;
  14. }
  15.  
  16. public Trans(List<String> list){
  17. stack=new Stack<String>();
  18. postfixList=new ArrayList<String>();
  19.  
  20. for(String str:list){
  21. // 这个分支是当前项是操作符号的情况
  22. if(str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("(") || str.equals(")") ){
  23. String opThis=str;
  24.  
  25. if(stack.size()==0){
  26. // 如果栈为空,直接把操作符推入栈
  27. stack.push(opThis);
  28. }else if(str.equals("(")){
  29. // 如果操作符是左括号,直接推入栈
  30. stack.push(opThis);
  31. }else if(str.equals(")")){
  32. // 如果操作符是右括号,则往前找左括号,将左括号之后的操作符放到后续表达式列表中
  33.  
  34. while(stack.peek().equals("(")==false){ // stack.peek()是取栈顶元素而不弹出
  35. postfixList.add(stack.pop());
  36. }
  37.  
  38. stack.pop();// 左括号丢弃,由此完成了去括号的过程
  39. }else{
  40. // 看栈顶元素,如果它优先级大于等于当前操作符的优先级,则弹出放到后续表达式列表中
  41. while( stack.size()>0 && (getOpLevel(stack.peek())>=getOpLevel(opThis)) ){
  42. postfixList.add(stack.pop());
  43. }
  44.  
  45. stack.push(opThis);// 当前操作符入栈
  46. }
  47.  
  48. }else{
  49. // 这个分支是当前项是操作数的情况
  50. postfixList.add(str);// 操作数直接入栈
  51. }
  52. }
  53.  
  54. // 将栈中余下的操作符弹出放到后续表达式列表中
  55. while(stack.size()>0){
  56. String opTop=stack.pop();
  57. postfixList.add(opTop);
  58. }
  59. }
  60.  
  61. // 取得操作符的等级
  62. private int getOpLevel(String op){
  63. if(op.equals("+") || op.equals("-") ){
  64. return 0;
  65. }else if(op.equals("*") || op.equals("/") ){
  66. return 1;
  67. }
  68.  
  69. return -1;
  70. }
  71.  
  72. public void print(){
  73. for(String str:postfixList){
  74. System.out.print(str);
  75. }
  76. }
  77. }

计算后续表达式运算结果类,它接受经过Trans类处理的postfixList,又采用了栈进行辅助,计算结果方式是见到操作数先入栈,见到操作符则从栈中弹出两个操作数进行运算,得到结果后再入栈,执行完毕后弹出栈的顶项(必是最后一项)即是算术表达式的最终结果:

  1. package com.hy;
  2.  
  3. import java.util.List;
  4. import java.util.Stack;
  5.  
  6. // 此类用于计算后续表达式的值
  7. public class Calculator {
  8. private Stack<String> stack;
  9.  
  10. public Calculator(List<String> list){
  11. stack=new Stack<String>();
  12.  
  13. for(String str:list){
  14. // 这个分支是当前项是操作符号的情况
  15. if(str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("(") || str.equals(")") ){
  16. float op2=Float.parseFloat(stack.pop());
  17. float op1=Float.parseFloat(stack.pop());
  18. float result=0;
  19.  
  20. if(str.equals("+")){
  21. result=op1+op2;
  22. }else if(str.equals("-")){
  23. result=op1-op2;
  24. }else if(str.equals("*")){
  25. result=op1*op2;
  26. }else if(str.equals("/")){
  27. result=op1/op2;
  28. }
  29.  
  30. stack.push(String.valueOf(result));
  31. }else{
  32. // 如果是操作数先直接入栈
  33. stack.push(str);
  34. }
  35. }
  36. }
  37.  
  38. // 取得结果
  39. public String getResult(){
  40. return stack.peek();
  41. }
  42. }

运行结果:

  1. 请输入算术表达式:(2+3)*6-20
  2. (2+3)*6-20=10.0
  3.  
  4. 请输入算术表达式:13-5*(1+2)
  5. 13-5*(1+2)=-2.0
  6.  
  7. 请输入算术表达式:1+2+3+4+5+6+7+8+9+10
  8. 1+2+3+4+5+6+7+8+9+10=55.0

到这里,基本上算是实现了Javascript里的eval函数,当然还有需要完善的地方,比如用正则表达式对输入的算术表达式进行预验证,用二叉树形成语法结构等,这些留待日后完成。可以想象如果没有利用后续表达式助力,代码不知会写得多么复杂难懂。由此可知除了分解问题外,合适的数学工具也是改善代码的重要手段。

喝水不忘挖井人,我的参考资料如下:

1.Java数据结构与算法(第二版) [美]Robert Lafore著

2.栈的应用--中序表达式转后序表达式  https://www.cnblogs.com/bgmind/p/3989808.html

--END--2019年9月2日13点35分

[Java]算术表达式求值之一(中序表达式转后序表达式方案)的更多相关文章

  1. [Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)

    Entry类 这个类对表达式的合法性进行了粗筛: package com.hy; import java.io.BufferedReader; import java.io.IOException; ...

  2. C++之字符串表达式求值

    关于字符串表达式求值,应该是程序猿们机试或者面试时候常见问题之一,昨天参加国内某IT的机试,压轴便为此题,今天抽空对其进行了研究. 算术表达式中最常见的表示法形式有 中缀.前缀和 后缀表示法.中缀表示 ...

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

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

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

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

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

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

  6. C/C++ 语言中的表达式求值(原文作者:裘宗燕)

    经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?”m = 1; n = m+++m++;最近有位不相识的朋友发email给我,问为什么在某个C++系统里,下面表达式打印出两个4, ...

  7. Dijkstra的双栈算术表达式求值算法

    这次来复习一下Dijkstra的双栈算术表达式求值算法,其实这就是一个计算器的实现,但是这里用到了不一样的算法,同时复习了栈. 主体思想就是将每次输入的字符和数字分别存储在两个栈中.每遇到一个单次结束 ...

  8. 蓝桥杯算法训练 java算法 表达式求值

    问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例输出 - ...

  9. C/C++ 语言中的表达式求值

    在此,首先向裘老师致敬! 裘宗燕:C/C++ 语言中的表达式求值 经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?” m = 1; n = m+++m++; 最近有位不相识的朋友 ...

  10. Java描述表达式求值的两种解法:双栈结构和二叉树

    Java描述表达式求值的两种解法:双栈结构和二叉树 原题大意:表达式求值 求一个非负整数四则混合运算且含嵌套括号表达式的值.如: # 输入: 1+2*(6/2)-4 # 输出: 3.0 数据保证: 保 ...

随机推荐

  1. Linux下nouveau操作和GPU的操作,nouveau拯救

    之前也提到了,旧机器上有一块NVIDIA的显卡,装了ubuntu16.04后一直没有安装驱动.这周算是有点时间,就差点作了死. 首先必须澄清,这个不是正确的安装过程,起码我就报了memory erro ...

  2. 23、GoAccess分析Nginx日志

    1.GoAccess基本概述 GoAccess是一个基于终端的快速日志分析器.其核心思想是实时快速分析和查看Web服务器统计信息. 1.安装简单: 2.操作容易: 3.界面酷炫:  2.GoAcce ...

  3. 小黄车ofo法人被限制出境,它究竟还能撑多久?

    因为季节的原因,现在正是骑车的好时候,而且北京也开通了一条自行车的专用路.但就是在这么好的时候,我们发现,路边的小黄车却越来越少了,而且它的麻烦还不断! ofo法人被限制出境 6月12日消息,据上海市 ...

  4. Arch Linux 安装 ibus-rime

    参考网站 default.custom.yaml 在方案選單中添加五筆.雙拼 rime-wubi 操作方式 # 删除原rime(可选) sudo pacman -Rs ibus-rime ibus-t ...

  5. spring ObjectFactory

    AspectJAwareAdvisorAutoProxyCreator implements PostProcessor postProcessorAfterInitialization(Object ...

  6. package+explorer不显示项目的问题

    昨天遇到了这个问题,百度了一下,怎么搞的都有,但是感觉都不理想,晚上的时候才在网上又发现这个方法,今天试了一下,效果还不错,分享一下. 点击 Window ---> Close All Pers ...

  7. mysql怎么查看数据库中表的大小

    查看mysql数据库大小的四种办法,分别有以下四种:第一种:进去指定schema 数据库(存放了其他的数据库的信息)use information_schema第二种:查询所有数据的大小select ...

  8. 关于不重启Tomcat自动加载改变的class文件

    修改server.xml,在Host标签下加入以下配置 <Context path="" docBase="FileManager" reloadable ...

  9. html访问全过程

    1)解析Web页面的URL,得到Web服务器的域名 2)通过DNS服务器获得Web服务器的IP地址 3)与Web服务器建立TCP连接 4)与Web服务器建立HTTP连接 5)从Web服务器获得URL指 ...

  10. SpringMvc (注解)中的上传文件

    第一步:导入commons-fileupload-1.3.1.jar 和commons-io-2.2.jar 架包 第二步:在applicationContext.xml中 配置 <bean i ...