1. java使用双栈实现一个计算器
基本逻辑
- 双栈分别存储数值(
nums
)和操作符(ops
) - 在实现时需要注意优先级
- 如果当前操作符是 ‘
)
’ 时,需要在遍历过程中把括号内数据做一次计算 - 如果当前遍历的操作符如果是
+ -
,且操作符栈ops的操作符是* /
,则需要优先计算*
和/
的逻辑
- 上边都做完的话,最终都是简单的
+ -
逻辑了,直接计算即可
BigDicemal版本
public class calculatorBigdecimal {
public static void main(String[] args) {
String expression0 = "(2-5+5)*2-3";
System.out.println("(2-5+5)*2-3 = " + calculate(expression0));
String expression1 = "(2*5+5)*2-3";
System.out.println("(2*5+5)*2-3 = " + calculate(expression1));
String expression2 = "10+5*(2-2)";
System.out.println("10+5*(2-2) = " + calculate(expression2));
String expression3 = "10+5/(4-1)"; //除不尽转化为BigDecimal
System.out.println("10+5/(4-1) = " + calculate(expression3));
String expression6 = "2-5+2-3";
System.out.println("2-5+2-3 = " + calculate(expression6));
}
public static BigDecimal calculate(String expression) {
Stack<BigDecimal> nums = new Stack<>();
Stack<Character> ops = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (Character.isDigit(c)) {
int j = i;
while (j < expression.length() && Character.isDigit(expression.charAt(j))) {
j++;
}
nums.push(new BigDecimal(expression.substring(i, j)));
i = j - 1;
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
while (ops.peek() != '(') {
nums.push(calculateByOps(ops.pop(), nums.pop(), nums.pop()));
}
ops.pop();
} else if (isOperator(c)) {
while (!ops.empty() && needCalculatePre(c, ops.peek())) {
nums.push(calculateByOps(ops.pop(), nums.pop(), nums.pop()));
}
ops.push(c);
}
}
while (!ops.empty()) {
nums.push(calculateByOps(ops.pop(), nums.pop(), nums.pop()));
}
return nums.pop();
}
private static boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
private static boolean needCalculatePre(char op1, char op2) {
if (op2 == '(' || op2 == ')') {
return false;
}
return (op2 == '*' || op2 == '/') && (op1 == '+' || op1 == '-') || (op2 == '+' || op2 == '-') && (op1 == '+' || op1 == '-');
}
private static BigDecimal calculateByOps(char op, BigDecimal b, BigDecimal a) {
switch (op) {
case '+':
return a.add(b);
case '-':
return a.subtract(b);
case '*':
return a.multiply(b).setScale(4, BigDecimal.ROUND_HALF_UP);
case '/':
if (b.compareTo(BigDecimal.ZERO) == 0) {
throw new UnsupportedOperationException("Cannot divide by zero");
}
return a.divide(b,4, RoundingMode.HALF_UP);
default:
throw new UnsupportedOperationException("Unknown operator " + op);
}
}
}
Double版本
public class calculator2 {
public static void main(String[] args) {
String expression0 = "(2-5+5)*2-3";
System.out.println("(2-5+5)*2-3 = " + calculate(expression0));
String expression1 = "(2*5+5)*2-3";
System.out.println("(2*5+5)*2-3 = " + calculate(expression1));
String expression2 = "10+5*(2-2)";
System.out.println("10+5*(2-2) = " + calculate(expression2));
String expression3 = "10+5/(4-1)"; //除不尽转化为BigDecimal
System.out.println("10+5/(4-1) = " + new BigDecimal(calculate(expression3)).setScale(4, BigDecimal.ROUND_HALF_UP));
}
public static Double calculate(String expression) {
//第一个栈:存数字
Stack<Double> nums = new Stack<>();
//第二个栈:存操作符合
Stack<Character> ops = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (Character.isDigit(c)) {
int j = i;
//如果是数字,下标右移,拼接第一个数字,存入数字栈nums中
while (j < expression.length() && Character.isDigit(expression.charAt(j))) {
j++;
}
//把第一个数字压栈
nums.push(Double.parseDouble(expression.substring(i, j)));
//重置下角标为最新位置,继续向下遍历
i = j - 1;
} else if (c == '(') {
//如果是'(' ,直接压入操作符栈ops
ops.push(c);
} else if (c == ')') {
//如果是')' ,弹出数字栈nums中两个值,弹出一个操作符栈ops的一个操作符,计算括号内的值
while (ops.peek() != '(') {
nums.push(applyOp(ops.pop(), nums.pop(), nums.pop()));
}
//最后弹出操作符栈ops的'('
ops.pop();
} else if (isOperator(c)) {
//如果是操作符号,当前遍历的操作符如果是 + - ,且操作符栈ops的操作符是 * /,则需要优先计算 *和/的逻辑
while (!ops.empty() && hasPrecedence(c, ops.peek())) {
nums.push(applyOp(ops.pop(), nums.pop(), nums.pop()));
}
//最后再把当前遍历的 + - 操作符,压入操作符栈ops
ops.push(c);
}
}
//最终都剩下了 + - 操作符,计算剩下的内容
while (!ops.empty()) {
nums.push(applyOp(ops.pop(), nums.pop(), nums.pop()));
}
return nums.pop();
}
private static boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
private static boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')') {
return false;
}
//第一个条件是乘除法 和 加减法的优先级问题
//第二个条件是避免(2-5+5)*2-3 这种括号内 会先计算5+5的问题
return (op2 == '*' || op2 == '/') && (op1 == '+' || op1 == '-') || (op2 == '+' || op2 == '-') && (op1 == '+' || op1 == '-') ;
}
private static Double applyOp(char op, Double b, Double a) {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0) {
throw new UnsupportedOperationException("Cannot divide by zero");
}
return a / b;
default:
throw new UnsupportedOperationException("Unknown operator " + op);
}
}
}
以上使用双栈实现的计算器,测试代码运行结果如下