java实现算术表达式求值
需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类。
后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后缀表达式为32+,3*(2+1)的后缀表达式为:321+*,解决表达式求值首先需要根据字符串表达式求出后缀表达式,然后使用后缀表达式和操作数栈实现计算,计算的大致思想是从后缀表达式中取元素,如果元素是数值则加入到操作数栈中,如果是运算符则从操作数栈中取两个数来参与运算。后缀表达式的获取要借助于两个栈,一个是后缀表达式栈,一个是操作符栈,顺序扫描算术表达式,如果是数值则直接加入后缀表达式栈,如果是运算符则使用当前运算符和运算符栈中的栈顶元素做比较,如果当前运算符的优先级高则当前元素进入操作符栈,如果当前元素优先级低,则操作符栈顶元素出栈加入到后缀表达式栈中,一直到当前元素优先级高于操作符栈顶元素优先级则当前元素入操作符栈,目前只支持加减乘除和带小括号的运算。
import java.util.Collections;
import java.util.Stack;
public class Calculator {
private Stack<String> postfixStack = new Stack<String>();//后缀式栈
private Stack<Character> opStack = new Stack<Character>();//运算符栈
private int [] operatPriority = new int[] {0,3,2,1,-1,1,0,2};//运用运算符ASCII码-40做索引的运算符优先级
public static void main(String[] args) {
System.out.println(5+12*(3+5)/7.0);
Calculator cal = new Calculator();
String s = "5+12*(3+5)/7";
double result = cal.calculate(s);
System.out.println(result);
}
/**
* 按照给定的表达式计算
* @param expression 要计算的表达式例如:5+12*(3+5)/7
* @return
*/
public double calculate(String expression) {
Stack<String> resultStack = new Stack<String>();
prepare(expression);
Collections.reverse(postfixStack);//将后缀式栈反转
String firstValue ,secondValue,currentValue;//参与计算的第一个值,第二个值和算术运算符
while(!postfixStack.isEmpty()) {
currentValue = postfixStack.pop();
if(!isOperator(currentValue.charAt(0))) {//如果不是运算符则存入操作数栈中
resultStack.push(currentValue);
} else {//如果是运算符则从操作数栈中取两个值和该数值一起参与运算
secondValue = resultStack.pop();
firstValue = resultStack.pop();
String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
resultStack.push(tempResult);
}
}
return Double.valueOf(resultStack.pop());
}
/**
* 数据准备阶段将表达式转换成为后缀式栈
* @param expression
*/
private void prepare(String expression) {
opStack.push(',');//运算符放入栈底元素逗号,此符号优先级最低
char[] arr = expression.toCharArray();
int currentIndex = 0;//当前字符的位置
int count = 0;//上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
char currentOp ,peekOp;//当前操作符和栈顶操作符
for(int i=0;i<arr.length;i++) {
currentOp = arr[i];
if(isOperator(currentOp)) {//如果当前字符是运算符
if(count > 0) {
postfixStack.push(new String(arr,currentIndex,count));//取两个运算符之间的数字
}
peekOp = opStack.peek();
if(currentOp == ')') {//遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
while(opStack.peek() != '(') {
postfixStack.push(String.valueOf(opStack.pop()));
}
opStack.pop();
} else {
while(currentOp != '(' && peekOp != ',' && compare(currentOp,peekOp) ) {
postfixStack.push(String.valueOf(opStack.pop()));
peekOp = opStack.peek();
}
opStack.push(currentOp);
}
count = 0;
currentIndex = i+1;
} else {
count++;
}
}
if(count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {//最后一个字符不是括号或者其他运算符的则加入后缀式栈中
postfixStack.push(new String(arr,currentIndex,count));
}
while(opStack.peek() != ',') {
postfixStack.push(String.valueOf( opStack.pop()));//将操作符栈中的剩余的元素添加到后缀式栈中
}
}
/**
* 判断是否为算术符号
* @param c
* @return
*/
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' ||c == ')';
}
/**
* 利用ASCII码-40做下标去算术符号优先级
* @param cur
* @param peek
* @return
*/
public boolean compare(char cur,char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
boolean result = false;
if(operatPriority[(peek)-40] >= operatPriority[(cur) - 40]) {
result = true;
}
return result;
}
/**
* 按照给定的算术运算符做计算
* @param firstValue
* @param secondValue
* @param currentOp
* @return
*/
private String calculate(String firstValue,String secondValue,char currentOp) {
String result = "";
switch(currentOp) {
case '+':
result = String.valueOf(ArithHelper.add(firstValue, secondValue));
break;
case '-':
result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
break;
case '*':
result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
break;
case '/':
result = String.valueOf(ArithHelper.div(firstValue, secondValue));
break;
}
return result;
}
}
public class ArithHelper {
// 默认除法运算精度
private static final int DEF_DIV_SCALE = 16;
// 这个类不能实例化
private ArithHelper() {
}
/**
* 提供精确的加法运算。
*
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1, double v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
public static double add(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算。
*
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static double sub(double v1, double v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
public static double sub(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确的乘法运算。
*
* @param v1
* 被乘数
* @param v2
* 乘数
* @return 两个参数的积
*/
public static double mul(double v1, double v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
public static double mul(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
*
* @param v1
* 被除数
* @param v2
* 除数
* @return 两个参数的商
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
public static double div(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供精确的小数位四舍五入处理。
*
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/
public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
java.math.BigDecimal one = new java.math.BigDecimal("1");
return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
public static double round(String v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
java.math.BigDecimal b = new java.math.BigDecimal(v);
java.math.BigDecimal one = new java.math.BigDecimal("1");
return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
java实现算术表达式求值的更多相关文章
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
- [Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)
Entry类 这个类对表达式的合法性进行了粗筛: package com.hy; import java.io.BufferedReader; import java.io.IOException; ...
- 【算法】E.W.Dijkstra算术表达式求值
算术表达式求值 我们要学习的一个栈的用例同时也是展示泛型的应用的一个经典例子,就是用来计算算术表达式的值,例如 ( 1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) ) 如果将4乘以5,把3 ...
- Dijkstra的双栈算术表达式求值算法
这次来复习一下Dijkstra的双栈算术表达式求值算法,其实这就是一个计算器的实现,但是这里用到了不一样的算法,同时复习了栈. 主体思想就是将每次输入的字符和数字分别存储在两个栈中.每遇到一个单次结束 ...
- OpenJudge计算概论-简单算术表达式求值
/*===================================== 简单算术表达式求值 总时间限制: 1000ms 内存限制: 65536kB 描述 2位正整数的简单算术运算(只考虑整数运 ...
- page80-栈用例-算术表达式求值
表达式由括号, 运算符和操作数(数字)组成.我们根据以下4中情况从左到右逐个将这些实体送入栈处理. (1)将操作数压入操作数栈: (2)将运算符压入运算符栈: (3)忽略左括号: (4)在遇到右括号时 ...
- 算法手记(2)Dijkstra双栈算术表达式求值算法
这两天看到的内容是关于栈和队列,在栈的模块发现了Dijkstra双栈算术表达式求值算法,可以用来实现计算器类型的app. 编程语言系统一般都内置了对算术表达式的处理,但是他们是如何在内部实现的呢?为了 ...
- 栈的一个实例——Dijkstra的双栈算术表达式求值法
Dijkstra的双栈算术表达式求值法,即是计算算术表达式的值,如表达式(1 + ( (2+3) * (4*5) ) ). 该方法是 使用两个栈分别存储算术表达式的运算符与操作数 忽略左括号 遇到右括 ...
- [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)
Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...
随机推荐
- 浏览器加载外部js 的顺序,以及处理顺序。
问题, 有事候按F12打开 google的调试台后,查看network下面加载过来的资源是, 有些资源的状态处于 pending.. 个人理解: 浏览器是可以同时开启多个http 请求去加载外部的资源 ...
- 改变nova-compute默认位置的方法
# cat /etc/nova/nova.conf |grep -n state_path|grep -v '#'314:state_path=/var/lib/nova
- Android开发学习资源
https://developer.android.google.cn/training/index.html
- docker-compose 所带来的方便
docker-compose 是一款开源的docker 简化复杂容器环境的管理工具 . docker-compose 在结合Swarm 与 docker 进程化容器部署可以很方便的部署一套环境. 具体 ...
- phpmailer 的使用
[转载]http://blog.csdn.net/liruxing1715/article/details/7914974 <?php header('Content-Type:text/htm ...
- java中堆和堆栈的区别
java中堆和堆栈的区别(一) 1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. 2. 栈的优势是,存取 ...
- a标签的target的四个值
特殊的目标 有 4 个保留的目标名称用作特殊的文档重定向操作: _blank 浏览器总在一个新打开.未命名的窗口中载入目标文档. _self 这个目标的值对所有没有指定目标的 <a> 标签 ...
- sping 对 hibernate进行事务管理--Annotation, xml, 大多数使用XML
1. UserServiceTest.java: package com.bjsxt.service; import org.junit.Test; import org.springframewor ...
- iOS 图片拉伸
UIImage *img = [UIImage imageNamed:@"CGUnwrapRed_2"]; img = [img stretchableImageWithLeftC ...
- MySQL的IP处理函数inet_aton()和inet_ntoa()
给出一个作为字符串的网络地址的"点地址"(如127.0.0.1)表示,返回一个代表该地址数值的整数.地址可以是4或8比特地址. mysql> SELECT inet_aton ...