问题描述
给你一个字符串表达式 s
,请你实现一个基本计算器来计算并返回它的值。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval()
。
解题思路
逆波兰表示法是一种无需括号即可定义运算顺序的数学表达式表示方法,它利用栈的数据结构来处理操作符的优先级和括号,非常适合于解析和计算此类问题。使用 RPN 的好处包括:
- 直接和顺序的计算流程,无需处理优先级和括号。
- 实现简洁,只需一个栈即可完成所有操作。
实现步骤转换
接下来,讨论如何将题目要求转换为使用逆波兰表示法的实现步骤:
-
解析输入字符串:
- 从左到右扫描字符串。
- 忽略空格。
- 识别数字,并处理多位数。
- 识别和处理操作符和括号。
-
构建逆波兰表达式:
- 使用一个栈来处理操作符,确保表达式中的运算符按正确的顺序输出到最终的逆波兰表达式中。
- 遇到操作符时,比较栈顶操作符与当前操作符的优先级:
- 如果栈顶操作符优先级较高或相等,从栈中弹出并输出到逆波兰序列,直到满足条件后将当前操作符压栈。
- 括号特殊处理:左括号直接压栈,右括号则连续弹出栈顶操作符直到遇到左括号。
- 数字直接输出到逆波兰序列。
-
计算逆波兰表达式:
- 再次使用栈来计算逆波兰表达式的结果。
- 遇到数字则压栈。
- 遇到操作符则从栈中弹出所需数量的数字进行计算,然后将结果压回栈中。
- 表达式结束时,栈顶即为最终结果。
代码实现
class Solution {
public:
int calculate(string s) {
stack<int> vals; // 用于存储数值
stack<char> ops; // 用于存储操作符,包括括号
int currentNumber = 0;
int result = 0; // For the current parenthesis level
int sign = 1; // 1 表示正数,-1 表示负数
for (char c : s) {
if (isdigit(c)) {
currentNumber = currentNumber * 10 + (c - '0');
} else {
result += sign * currentNumber;
currentNumber = 0;
if (c == '+') {
sign = 1;
} else if (c == '-') {
sign = -1;
} else if (c == '(') {
// 把目前的结果和符号推入栈中
vals.push(result);
ops.push(sign);
result = 0;
sign = 1;
} else if (c == ')') {
// 结束当前层括号
int op = ops.top();
ops.pop();
int val = vals.top();
vals.pop();
result = val + op * result;
}
}
}
result += sign * currentNumber;
return result;
}
};