[Java]算术表达式求值之一(中序表达式转后序表达式方案)
第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html
入口类,这个类的主要用途是粗筛用户输入的算术表达式:
package com.hy;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
// 此类用于把算术表达式送入解析器
public class Inlet {
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==')' ){
//System.out.print(c);
expression+=c;
}else{
System.out.print(" "+c+"不是合法的算术表达式字符.");
System.exit(0);
}
}
// 送去解析
Parser p=new Parser(expression);
//p.print();
// 转为后序表达式
Trans t=new Trans(p.getList());
//t.print();
// 计算结果
Calculator c=new Calculator(t.getPostfixList());
System.out.print(expression+"="+c.getResult());
}
}
算术表达式解析器类,它主要起一个词法分析器的作用,由于算术表达式词法较简单,因此逐字读入处理也能完成任务,他的输入是如23+4*(5+6)这种算术表达式,处理完成以后得到23,+,4,*,(,5,+,6,)这些包含操作数和操作符的列表:
package com.hy;
import java.util.ArrayList;
import java.util.List;
// 此类用于将算术表达式解析成包含操作数和操作符的链表
public class Parser {
private List<String> list;// 用于存储表达式的链表
public List<String> getList() {
return list;
}
public Parser(String expression){
list=new ArrayList<String>();
String str="";
for(int i=0;i<expression.length();i++){
char c=expression.charAt(i);
if(Character.isDigit(c)){
str+=c;
}else{
if(str.length()>0){// 此判断是因为有+(这种符号相连的情况
//System.out.println(str);
list.add(str);
str="";
}
//System.out.println(c);
list.add(String.valueOf(c));
}
}
if(str.length()>0){// 此判断是因为可能表达式不是以=结尾
//System.out.println(str);
list.add(str);
str="";
}
}
public void print(){
for(String str:list){
System.out.println(str);
}
}
}
将中序表达式转后序表达式的转换类,他接收来自Parser的包含操作符和操作数的列表,然后根据规则将算术表达式转化成后序表达式,利用的数据结构是栈java.util.Statck,转化的规则如下:
见到操作数->直接送到postfixList中
见到操作符->将栈顶输出,直到栈顶优先级小于该操作符,最后把该操作符压入栈
见到左括号 ->入栈
见到右括号 ->将栈中在左括号之后的操作符全部输出
(以上'栈'在代码中指的是Trans类的成员变量Stack)
package com.hy;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
// 此类用于将中序表达式转译成后序表达式
public class Trans {
private Stack<String> stack;// 用于存储操作符的栈
private List<String> postfixList;// 用于存储后序表达式的链表
public List<String> getPostfixList() {
return postfixList;
}
public Trans(List<String> list){
stack=new Stack<String>();
postfixList=new ArrayList<String>();
for(String str:list){
// 这个分支是当前项是操作符号的情况
if(str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("(") || str.equals(")") ){
String opThis=str;
if(stack.size()==0){
// 如果栈为空,直接把操作符推入栈
stack.push(opThis);
}else if(str.equals("(")){
// 如果操作符是左括号,直接推入栈
stack.push(opThis);
}else if(str.equals(")")){
// 如果操作符是右括号,则往前找左括号,将左括号之后的操作符放到后续表达式列表中
while(stack.peek().equals("(")==false){ // stack.peek()是取栈顶元素而不弹出
postfixList.add(stack.pop());
}
stack.pop();// 左括号丢弃,由此完成了去括号的过程
}else{
// 看栈顶元素,如果它优先级大于等于当前操作符的优先级,则弹出放到后续表达式列表中
while( stack.size()>0 && (getOpLevel(stack.peek())>=getOpLevel(opThis)) ){
postfixList.add(stack.pop());
}
stack.push(opThis);// 当前操作符入栈
}
}else{
// 这个分支是当前项是操作数的情况
postfixList.add(str);// 操作数直接入栈
}
}
// 将栈中余下的操作符弹出放到后续表达式列表中
while(stack.size()>0){
String opTop=stack.pop();
postfixList.add(opTop);
}
}
// 取得操作符的等级
private int getOpLevel(String op){
if(op.equals("+") || op.equals("-") ){
return 0;
}else if(op.equals("*") || op.equals("/") ){
return 1;
}
return -1;
}
public void print(){
for(String str:postfixList){
System.out.print(str);
}
}
}
计算后续表达式运算结果类,它接受经过Trans类处理的postfixList,又采用了栈进行辅助,计算结果方式是见到操作数先入栈,见到操作符则从栈中弹出两个操作数进行运算,得到结果后再入栈,执行完毕后弹出栈的顶项(必是最后一项)即是算术表达式的最终结果:
package com.hy;
import java.util.List;
import java.util.Stack;
// 此类用于计算后续表达式的值
public class Calculator {
private Stack<String> stack;
public Calculator(List<String> list){
stack=new Stack<String>();
for(String str:list){
// 这个分支是当前项是操作符号的情况
if(str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("(") || str.equals(")") ){
float op2=Float.parseFloat(stack.pop());
float op1=Float.parseFloat(stack.pop());
float result=0;
if(str.equals("+")){
result=op1+op2;
}else if(str.equals("-")){
result=op1-op2;
}else if(str.equals("*")){
result=op1*op2;
}else if(str.equals("/")){
result=op1/op2;
}
stack.push(String.valueOf(result));
}else{
// 如果是操作数先直接入栈
stack.push(str);
}
}
}
// 取得结果
public String getResult(){
return stack.peek();
}
}
运行结果:
请输入算术表达式:(2+3)*6-20 (2+3)*6-20=10.0 请输入算术表达式:13-5*(1+2) 13-5*(1+2)=-2.0 请输入算术表达式:1+2+3+4+5+6+7+8+9+10 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]算术表达式求值之一(中序表达式转后序表达式方案)的更多相关文章
- [Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)
Entry类 这个类对表达式的合法性进行了粗筛: package com.hy; import java.io.BufferedReader; import java.io.IOException; ...
- C++之字符串表达式求值
关于字符串表达式求值,应该是程序猿们机试或者面试时候常见问题之一,昨天参加国内某IT的机试,压轴便为此题,今天抽空对其进行了研究. 算术表达式中最常见的表示法形式有 中缀.前缀和 后缀表示法.中缀表示 ...
- [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)
Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
- java实现算术表达式求值
需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...
- C/C++ 语言中的表达式求值(原文作者:裘宗燕)
经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?”m = 1; n = m+++m++;最近有位不相识的朋友发email给我,问为什么在某个C++系统里,下面表达式打印出两个4, ...
- Dijkstra的双栈算术表达式求值算法
这次来复习一下Dijkstra的双栈算术表达式求值算法,其实这就是一个计算器的实现,但是这里用到了不一样的算法,同时复习了栈. 主体思想就是将每次输入的字符和数字分别存储在两个栈中.每遇到一个单次结束 ...
- 蓝桥杯算法训练 java算法 表达式求值
问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例输出 - ...
- C/C++ 语言中的表达式求值
在此,首先向裘老师致敬! 裘宗燕:C/C++ 语言中的表达式求值 经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?” m = 1; n = m+++m++; 最近有位不相识的朋友 ...
- Java描述表达式求值的两种解法:双栈结构和二叉树
Java描述表达式求值的两种解法:双栈结构和二叉树 原题大意:表达式求值 求一个非负整数四则混合运算且含嵌套括号表达式的值.如: # 输入: 1+2*(6/2)-4 # 输出: 3.0 数据保证: 保 ...
随机推荐
- js截取字符串相关的知识点
截取字符串中的数字 1.使用parseInt() var str ="4500元"; var num = parseInt(str); console.log(num);//450 ...
- ansible的基本学习-安装和简单的配置测试
当下有许多的运维自动化工具(配置管理),例如:ansible.saltstack.puppet.fabric等 ansible 是一种集成it系统的配置管理.应用部署.执行特定任务的开源平台,是ans ...
- 记一次生产环境presto删表失败的问题
场景,开发用java程序连接presto创建一个表,这个表在hdfs的权限为: 然后用presto去删除这个表 报错,没有权限删除,查看上一级目录权限,发现权限正常 直连hive删表 发现正常. 然后 ...
- JVM内存区域划分总结
发现网上有两个版本的JVM内存划分,一个是按照<深入理解JVM虚拟机>上的版本,包含程序计数器等,按照是否线程共享划分. 另一个我觉得更好记一些,也更适合我自己,在这里记录一下. 首先上思 ...
- kubeadm安装k8s1.13
1.环境介绍: centos 7.4.1708 关闭selinux和iptable,环境很重要! 主机 ip地址 cpu核数 内存 swap host解析 k8s-master 10.0.0.11 2 ...
- 一,python简介 笔记
python历史 1,1989年圣诞节,Guido von Rossum开始编写python语言编译器 2,1991年2月,第一个python编译器诞生,是c语言实现的,后面又出现了c#和java版本 ...
- skywalking 6.1 简明指南
skywalking 分布式系统的应用程序性能监视工具,专为微服务.云本机架构和基于容器(Docker.K8s.Mesos)架构而设计 背景 随着微服务架构的流行,一些微服务架构下的问题也会越来越突出 ...
- 自学Python-基于tcp协议的socket
自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...
- 03-spring框架—— AOP 面向切面编程
3.1 动态代理 动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(不是真实定义的类)在程序运行时由 JVM 根据反射等机制动态生成的.代理对象与目标 ...
- Django学习系列15:把POST请求中的数据存入数据库
要修改针对首页中的POST请求的测试.希望视图把新添加的待办事项存入数据库,而不是直接传给响应. 为了测试这个操作,要在现有的测试方法test_can_save_a_post_request中添加3行 ...