1. 简介
通过栈实现的一个简易的计算器,利用了逆波兰表达式。
支持的功能:
- 支持 + - * / ()
- 多位数,支持浮点数、超大数
- 兼容处理,过滤任何空白字符,包括空格、制表符、换页符
2. 计算原理
1. 逆波兰表达式计算过程
以(3+4)*5-6为例 后缀表达式:3 4 + 5 * 6 -
- 从左至右扫描,将3和4压入堆栈;
- 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
- 将5入栈;
- 接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
- 将6入栈;
- 最后是-运算符,计算出35-6的值,即29,由此得出最终结果
2. 中缀表达式转后缀表达式(波兰表达式转逆波兰)
1. 实现思路
- 初始化两个栈:运算符栈operaStack和储存中问结果的rsStack;
- 从左至右扫捕中缀表达式:
- 遇到操作数时,将其压rsStack
- 遇到运算符时,比较其与operaStack栈顶运算符的优先级:
- 如果operaStack为空,或栈顶运算符为左括号"("则直接将此运算符入栈operaStack
- 否则,若优先级比栈顶运算符的高,也将运算符压入operaStack
- 否则;将operaStack栈顶的运算符弹出并压入到s2中,再次转到4)2步, 和operaStack中新的栈顶运算符相比较:
- 遇到括号时:
- 如果是左括号“(”,则直接压入operaStack
- 如果是右括号“)”, 则依次弹出operaStack栈顶的运算符,并压入s2,直到遇到左括号为 止,此时将这一对括号丢弃
- 重复步骤2至5, 直到表达式的最右边
- 将operaStack中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元索并输出,结果的逆序即为中缀表达式对应的后级表达式
3. 代码实现
/**
* 将普通的中缀表达式转为后缀表达式,并封装List集合
* @param oldExpression
* @return
*/
public static List<String> transform(String oldExpression){
Stack<String> rsStack = new Stack<>(); //记录中间结果
Stack<String> operaStack = new Stack<>(); //记录运算符
String temp = "";
String[] expressionArray = oldExpression.split("");
for (String item : expressionArray) {
// 是数字
if (item.matches("\\d|\\.")){
temp += item; //扫描多位数
if (item.equals(expressionArray[expressionArray.length-1])){
rsStack.push(temp);
temp = ""; // 新修改内容
}
}else { // 不是数字,是操作符
// 如果temp不为空,证明多位数扫描完毕
if (isNotNull(temp)){
rsStack.push(temp);
temp = "";
}
if (isOpe(item)){
if (operaStack.isEmpty() || "(".equals(operaStack.peek())){
operaStack.push(item);
}else {
while (priority(item) <= priority(operaStack.peek())){
rsStack.push(operaStack.pop());
if (operaStack.isEmpty()){
break;
}
}
operaStack.push(item);
}
}else if ("(".equals(item)){
operaStack.push(item);
}else if (")".equals(item)){
while (!"(".equals(operaStack.peek())){
rsStack.push(operaStack.pop());
}
operaStack.pop();
}
//@TODO 抛出异常,表达式存在非法字符
}
}
while (!operaStack.isEmpty()){
rsStack.push(operaStack.pop());
}
List<String> list = new ArrayList<>();
for (String s : rsStack) {
list.add(s);
}
return list;
}
3. 计算器源码
1. 主类源码
import java.math.BigDecimal;
import java.util.List;
import java.util.Stack;
/**
* 功能:
* 1. 支持 + - * / ()
* 2. 多位数,支持浮点数、超大数
* 3. 兼容处理,过滤任何空白字符,包括空格、制表符、换页符
* @author 叶子
* @Description 计算器
* @DevelopmentTools IntelliJ IDEA
* @Data 2020/12/8 星期二 16:38
*/
public class Calculator {
public static void main(String[] args) {
String expression = "(3+4)*5-6.1";
System.out.println(calculator(expression));
}
/**
* 根据逆波兰表达式进行计算
* @param expression
* @return
*/
public static String calculator(String expression){
List<String> expressionList = CalculatorUtil.transform(expression);
Stack<String> stack = new Stack<>();
for (String item : expressionList) {
if (item.matches("^-?[1-9]\\d*$") || item.matches("^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$")){
stack.push(item);
}else {
BigDecimal num2 = new BigDecimal(stack.pop());
BigDecimal num1 = new BigDecimal(stack.pop());
String temp = CalculatorUtil.compute(num1,num2,item).toString();
stack.push(temp);
}
}
return stack.pop();
}
}
2. 封装的工具类源码
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* @author 叶子
* @Description 计算器工具类
* @DevelopmentTools IntelliJ IDEA
* @Data 2020/12/8 星期二 16:38
*/
public class CalculatorUtil {
/**
* 将普通的中缀表达式转为后缀表达式
* @param oldExpression
* @return
*/
public static List<String> transform(String oldExpression){
oldExpression = oldExpression.replaceAll("\\s+","");
Stack<String> rsStack = new Stack<>(); //记录中间结果
Stack<String> operaStack = new Stack<>(); //记录运算符
String temp = "";
String[] expressionArray = oldExpression.split("");
for (String item : expressionArray) {
// 是数字
if (item.matches("\\d|\\.")){
temp += item; //扫描多位数
if (item.equals(expressionArray[expressionArray.length-1])){
rsStack.push(temp);
}
}else { // 不是数字,是操作符
// 如果temp不为空,证明多位数扫描完毕
if (isNotNull(temp)){
rsStack.push(temp);
temp = "";
}
if (isOpe(item)){
if (operaStack.isEmpty() || "(".equals(operaStack.peek())){
operaStack.push(item);
}else {
while (priority(item) <= priority(operaStack.peek())){
rsStack.push(operaStack.pop());
if (operaStack.isEmpty()){
break;
}
}
operaStack.push(item);
}
}else if ("(".equals(item)){
operaStack.push(item);
}else if (")".equals(item)){
while (!"(".equals(operaStack.peek())){
rsStack.push(operaStack.pop());
}
operaStack.pop();
}else {
throw new RuntimeException("非法字符!");
}
}
}
while (!operaStack.isEmpty()){
rsStack.push(operaStack.pop());
}
List<String> list = new ArrayList<>();
for (String s : rsStack) {
list.add(s);
}
return list;
}
/**
* 判断是否为合法运算符
* @param operation
* @return
*/
public static boolean isOpe(String operation){
return "+".equals(operation) || "-".equals(operation) || "*".equals(operation) || "/".equals(operation);
}
/**
* 计算运算符优先级
* 返回值越大,优先级越高
* 返回值为-1,说明是不是合法运算符
* @param ope
* @return
*/
public static int priority(String ope){
if ("+".equals(ope) || "-".equals(ope)){
return 0;
}else if ("*".equals(ope) || "/".equals(ope)){
return 1;
}else {
return -1;
}
}
/**
* 判断字符串是否为空
* @param target
* @return
*/
public static boolean isNotNull(String target){
return (target!=null) && (!"".equals(target));
}
/**
* 根据传递的数和运算符完成运算
* @param num1
* @param num2
* @param opera
* @return
*/
public static BigDecimal compute(BigDecimal num1, BigDecimal num2, String opera){
BigDecimal res = new BigDecimal("0");
switch (opera){
case "+":
res = num1.add(num2);
break;
case "-":
res = num1.subtract(num2);
break;
case "*":
res = num1.multiply(num2);
break;
case "/":
res = num1.divide(num2);
break;
default:
break;
}
return res;
}
}