【题目】 给定一个字符串str,str是一个算数表达式,包含四则运算和括号()、[]、{}。要求返回计算结果。
【举例】 str="(3+1)*4/2",返回8。
【思路】 首选把中括号和大括号替换为小括号,接下来:
- 用递归处理括号内的表达式;
- 遍历字符数组,每取到一个完整的数就检查栈顶字符串;(1) 如果栈顶是*或者/即可优先弹栈计算;(2)如果是+或者-,入栈。
- 遍历完字符数组之后,栈中只剩下+和-的表达式。计算时可以从栈顶开始出栈,数字的符号后出栈;也可以将Deque看作双端队列,从队首出队,数字的符号会先出队。
总结一下:递归解决处理括号;入栈时先计算乘除,遍历完后计算加减,从而解决了符号的优先级问题。
【代码】
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String line = sc.nextLine();
line = line.replace("[", "(");
line = line.replace("]", ")");
line = line.replace("{", "(");
line = line.replace("}", ")");
int n = value(line.toCharArray(), 0)[0];
System.out.println(n);
}
}
private static int[] value(char[] chars, int i) {
Deque<String> deque = new LinkedList<>();
int pre = 0;
int[] bra = null;
while (i < chars.length && chars[i] != ')') {
// 取完整的数字
if (chars[i] >= '0' && chars[i] <= '9') {
pre = pre * 10 + chars[i] - '0';
} else if (chars[i] != '(') { // 遇到+-*/),表示数字结束,将数字放入deque,再将符号放入deque,重置pre
addNum(deque, pre);
deque.offerLast(String.valueOf(chars[i]));
pre = 0;
} else { // 遇到'(',进入递归
bra = value(chars, i + 1);
pre = bra[0];
i = bra[1];
}
i++;
}
addNum(deque, pre);
return new int[]{getNum(deque), i};
}
// 数字放入栈中。先看栈顶元素,若是*或/则进行计算,消除*/;若是+-则直接放入栈中
private static void addNum(Deque<String> deque, int v) {
if (deque.isEmpty()) {
deque.addLast(String.valueOf(v));
return;
}
String s = deque.peekLast();
if (s.equals("*") || s.equals("/")) {
deque.pollLast();
Integer v2 = Integer.parseInt(deque.pollLast());
v = s.equals("*") ? v * v2 : v2 / v;
}
deque.addLast(String.valueOf(v));
}
// 从只有+-的栈中计算结果
private static int getNum(Deque<String> deque) {
boolean add = true;
int res = 0;
while (!deque.isEmpty()) {
String s = deque.pollFirst();
if (s.equals("+")) {
add = true;
} else if (s.equals("-")) {
add = false;
} else {
int v = Integer.parseInt(s);
res = add ? (res + v) : (res - v);
}
}
return res;
}
}