对于一个简单的加减乘除表达式,怎么去计算其结果呢? 比如 21 + 3 - 5 * 0
。|不包含 (
,)
|
这里使用的实现思路是:
- 准备两个栈,分别用于存放 数字和 操作符。(为了便于后续表达,分别命名为
numStack, opStack
); - 然后就是遍历这个字符串,从前往后的提取里面的数字和符号,进行下一步的操作:
- 如果提取到的是数字,直接存入
numStack
; - 如果提取到的是符号,就先判断符号栈是否为空:
- 如果是空的,直接存入到
opStack
; - 如果符号栈不是空,就比较符号栈的栈顶元素与当前符号的优先级:
- 如果当前符号的优先级更高,直接存入符号栈;
- 如果当前的不是更高,就取出
opStack
的栈顶符号,以及numStack
的两个栈顶元素,进行数据的+-*/
的操作,并将计算结果继续存到numStack
中;(比如取出的数字先是a1
,然后是a2
,取出的符号是/
,就进行a2
/a1
)并且当前的符号存入到opStack
中;
- 如果是空的,直接存入到
- (这时候已经完成对输入的字符串的遍历了,不过
opStack
目前不是空,还得继续处理。)然后就开始出栈的处理:循环遍历这个符号栈opNums
, 取出一个符号的时候,立即取出两个数据,进行数据的+-*/
的操作,然后把这个计算结果继续存入到numStack
中,直到opStack
里面没有元素了为止; - 这时候,
opStack#size() == 0
, 但是numStack.size() ==1
, 这个numStack
里面的这个唯一的元素就是计算结果了。
- 如果提取到的是数字,直接存入
这里给出对应的实现:
考虑到精度问题,这里使用了
BigDemical
, 然后封装了一下操作符逻辑。然后在提取数字的时候,使用了一点正则表达式;
这里有一个问题需要注意,这里默认运算符是+-*/
这样的只占一位的字符,比如要用**
这表示次方(a^b)的话, 就会出问题。~
import java.math.BigDecimal;
import java.math.RoundingMode;
enum Operator {
ADD("+", 1),
REDUCE("-", 1),
MULTI("*", 2),
DIVIDE("/", 2);
private final String operate;
private final int priority;
Operator(String operate, int priority) {
this.operate = operate;
this.priority = priority;
}
public String getOperate() {
return operate;
}
public int getPriority() {
return priority;
}
public static Operator convert(String operate)
throws IllegalOperationException {
return switch (operate) {
case "+" -> ADD;
case "-" -> REDUCE;
case "*" -> MULTI;
case "/" -> DIVIDE;
default -> throw new IllegalOperationException("illegal operator.");
};
}
public static int calculate(Operator op, int prev, int cur) {
return switch (op) {
case ADD -> prev + cur;
case REDUCE -> prev - cur;
case MULTI -> prev * cur;
case DIVIDE -> prev / cur;
};
}
public static BigDecimal calculate(Operator op, BigDecimal prev, BigDecimal cur) {
return switch (op) {
case ADD -> prev.add(cur);
case REDUCE -> prev.subtract(cur);
case MULTI -> prev.multiply(cur);
case DIVIDE -> prev.divide(cur, RoundingMode.HALF_UP);
};
}
static class IllegalOperationException extends Exception {
public IllegalOperationException(String message) {
super(message);
}
}
}
import java.math.BigDecimal;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SimpleCalculator {
private final Stack<BigDecimal> numbers = new Stack<>();
private final Stack<Operator> operations = new Stack<>();
private final Pattern numPattern = Pattern.compile("\\d*\\.?\\d*");
public String apply(String input) {
input = input.replaceAll(" ", "");
Matcher numMatcher = numPattern.matcher(input);
while (numMatcher.find()) {
int start = numMatcher.start();
int end = numMatcher.end();
if (start != end) {
double num = Double.parseDouble(input.substring(start, end));
numbers.push(BigDecimal.valueOf(num));
if (end + 1 < input.length()) {
String sub = input.substring(end, end + 1);
try {
Operator action = Operator.convert(sub);
if (operations.isEmpty()) {
operations.push(action);
} else {
Operator old = operations.pop();
boolean higher = action.getPriority() - old.getPriority() > 0;
if (higher) {
operations.push(old);
operations.push(action);
} else {
// current operation is lower than old
// so do op(prev, current);
BigDecimal current = numbers.pop();
BigDecimal prev = numbers.pop();
BigDecimal result = Operator.calculate(old, prev, current);
numbers.push(result);
operations.push(action);
}
}
} catch (Operator.IllegalOperationException e) {
e.printStackTrace();
}
}
}
}
// after loop
while (!operations.isEmpty()) {
Operator op = operations.pop();
BigDecimal cur = numbers.pop();
BigDecimal prev = numbers.pop();
BigDecimal result = Operator.calculate(op, prev, cur);
numbers.push(result);
}
System.out.println("=== result---:");
System.out.printf("numbers: %s\n", numbers);
System.out.printf("operations : %s\n", operations);
return numbers.pop().stripTrailingZeros().toPlainString();
}
public static void main(String[] args) {
SimpleCalculator cal = new SimpleCalculator();
String input = "2+3 -5";
String result = cal.apply(input);
System.out.printf("result of [%s]--:[%s]\n", input, result);
String other = "3.5 * 2 + 5/2";
String result2 = cal.apply(other);
System.out.printf("result of [%s]--:[%s]\n", other, result2);
}
}
实现的部分结束了,可以看一下输出效果:
=== result—:
result of [2+3 -5]–:[0]
=== result—:
result of [3.5 * 2 + 5/2]–:[9.5]