
思路:栈解法。用栈来处理括号带来的优先级问题,将复杂的表达式拆解成多个简单的、不带括号的子表达式分别计算。
1.解题过程:
(1)如果是数字字符'0' ~ '9',那么就连续读取数字,构建完整的数值。
(2)如果是加号'+',那么就将当前的数字累加到结果中,并重置符号为正(下一个数字是正数)。
(3)如果是减号'-',逻辑同加号,将当前的数字累加到结果中,并重置符号为负(下一个数字是负数)。
(4)如果是左括号'(',遇到左括号,先保存现场,然后重置符号。
(5)如果是右括号')',遇到右括号,先把括号内的数字和符号计算完毕,然后合并回括号前的现场。
(6)别忘了加上最后一个数字。
2.复杂度分析:
(1)时间复杂度:O(n)。
(2)空间复杂度:O(n)。
附代码:
java
class Solution {
public int calculate(String s) {
LinkedList<Integer> stack = new LinkedList<>();
// 当前已经计算好的结果
int res = 0;
// 当前数字前面的正负号
int preSign = 1;
// 当前正在读取的数字
int num = 0;
for (char c : s.toCharArray()) {
// 遇到数字,连续读取数字构建完整的整数
if (c <= '9' && c >= '0') {
num = num * 10 + (c - '0');
}
// 遇到加号,把当前已读到数字累加到结果res
// 然后重置已读数字num为0,重置符号为正(下一个数字是正数)
else if (c == '+') {
res += num * preSign;
num = 0;
preSign = 1;
}
// 遇到减号,把当前已读到的数字累加到结果res
// 然后重置已读数字num为0,重置符号为负(下一个数字是负数)
else if (c == '-') {
res += num * preSign;
num = 0;
preSign = -1;
}
// 遇到左括号,先保存括号前的结果和括号前的符号入栈
// 然后重置当前已经计算好的结果为0,以开始计算括号内的结果
// 然后重置符号为正(括号内的第一个数字默认为正)
else if (c == '(') {
stack.push(res);
stack.push(preSign);
res = 0;
preSign = 1;
}
// 遇到右括号,加上括号内剩下的最后一个数字(因为遇到操作符才将当前数字加入结果,而最后一个数字没有遇到操作符,需要单独手动处理),然后括号里的数字已加减完毕
// 然后乘上括号前的符号
// 然后加上括号前的计算结果
else if (c == ')') {
res += num * preSign;
// 括号外如果有数字会从新的num开始读取,否则最后return就是加个0,不会多加导致结果错误
num = 0;
// 这里不需要重置preSign
// 因为遇到右括号代表括号内计算完毕,之后括号外的计算会使用从栈中弹出的符号,而不是preSign
// 下一次遇到操作符时,根据之前的逻辑,也会会重新设置preSign
res *= stack.pop();
res += stack.pop();
}
}
// 由于遇到操作符才将当前数字加入结果
// 而最后一个数字没有遇到操作符
// 因此需要单独手动处理
return res + preSign * num;
}
}
ACM模式:
java
import java.util.LinkedList;
import java.util.Scanner;
class Solution {
public int calculate(String s) {
LinkedList<Integer> stack = new LinkedList<>();
// 当前已经计算好的结果
int res = 0;
// 当前数字前面的正负号
int preSign = 1;
// 当前正在读取的数字
int num = 0;
for (char c : s.toCharArray()) {
// 遇到数字,连续读取数字构建完整的整数
if (c <= '9' && c >= '0') {
num = num * 10 + (c - '0');
}
// 遇到加号,把当前已读到数字累加到结果res
// 然后重置已读数字num为0,重置符号为正(下一个数字是正数)
else if (c == '+') {
res += num * preSign;
num = 0;
preSign = 1;
}
// 遇到减号,把当前已读到的数字累加到结果res
// 然后重置已读数字num为0,重置符号为负(下一个数字是负数)
else if (c == '-') {
res += num * preSign;
num = 0;
preSign = -1;
}
// 遇到左括号,先保存括号前的结果和括号前的符号入栈
// 然后重置当前已经计算好的结果为0,以开始计算括号内的结果
// 然后重置符号为正(括号内的第一个数字默认为正)
else if (c == '(') {
stack.push(res);
stack.push(preSign);
res = 0;
preSign = 1;
}
// 遇到右括号,加上括号内剩下的最后一个数字
// 然后乘上括号前的符号,然后加上括号前的计算结果
else if (c == ')') {
res += num * preSign;
// 括号外如果有数字会从新的num开始读取,否则最后return就是加个0,不会多加导致结果错误
num = 0;
// 这里不需要重置preSign
// 因为遇到右括号代表括号内计算完毕,之后括号外的计算会使用从栈中弹出的符号,而不是preSign
// 下一次遇到操作符时,根据之前的逻辑,也会会重新设置preSign
res *= stack.pop();
res += stack.pop();
}
}
// 由于遇到操作符才将当前数字加入结果
// 而最后一个数字没有遇到操作符,因此需要单独手动处理
return res + preSign * num;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取表达式字符串(可能包含空格)
String s = scanner.nextLine();
// 计算结果
Solution solution = new Solution();
int result = solution.calculate(s);
// 输出结果
System.out.println(result);
scanner.close();
}
}