LeetCode 227. Basic Calculator II
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。
注意: 不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
示例 1:
输入: s = "3+2*2"
输出: 7
示例 2:
输入: s = " 3/2 "
输出: 1
示例 3:
输入: s = " 3+5 / 2 "
输出: 5
提示:
1 <= s.length <= 3 * 105s由整数和算符('+', '-', '*', '/')组成,中间由一些空格隔开s表示一个 有效表达式- 表达式中的所有整数都是非负整数,且在范围
[0, 231 - 1]内 - 题目数据保证答案是一个 32-bit 整数
算法1
(栈模拟) O(n)
本题不包含括号,对于四个运算符加减乘除,我们可以将减号看成是数字前的负号,这样就只剩下了加号和乘除号,而对于乘除号则可以在扫描过程中直接计算出来并将中间结果保存,这样扫描完后只需要做加法就行了。具体做法是可以开一个栈记录中间结果和扫描得到的数字,以及一个变量sign记录上一个运算符,当遇到一个操作符时我们计算上一个运算符。这样遍历完字符串后再对栈中的数字求和就是答案了。
另外我们在字符串结尾的时候还需要对上一个操作符进行一次运算,这里为了方便我们可以直接在字符串末尾补上任意一个操作符。
AC 代码
class Solution {
public:
int calculate(string s) {
stack<int> nums;
int num = 0;
char sign = '+';
s += '+';
for (int i = 0; i < s.size(); i ++ ) {
if (isdigit(s[i])) num = num * 10 + (s[i] - '0');
if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') {
if (sign == '+') {
nums.push(num);
} else if (sign == '-') {
nums.push(-num);
} else if (sign == '*') {
int n = nums.top();
nums.pop();
nums.push(n * num);
} else if (sign == '/') {
int n = nums.top();
nums.pop();
nums.push(n / num);
}
num = 0;
sign = s[i];
}
}
int res = 0;
while (nums.size()) {
res += nums.top();
nums.pop();
}
return res;
}
};
算法2
(栈模拟) O(n)
同样是栈模拟,不过不同于算法1中对减法变加法并且最后计算加法的思路,我们也可以在扫描过程中直接计算加减法。首先我们用两个栈,一个数字栈保存数字和中间结果,一个符号栈保存遇到的操作符,然后我们是在遇见操作数的时候对表达式进行求值。比如对于"1 + 2 * 3"我们遇见2的时候不能先计算栈顶的加号,因为并不能确定后面还有没有优先级更高的乘除号,但是对于"1 + 2 + 3 * 4",我们遇见3前面的+号的时候我们就确定可以计算1 + 2了。
总结起来就是当遇到运算符时,只要栈顶符号的优先级不低于新符号,就不断取出栈顶并输出,最后把新符号进栈。优先级为乘除 > 加减 > 左括号。
这里可以直接用 AcWing 3302. 表达式求值 的模板。
AC 代码
class Solution {
public:
stack<int> num;
stack<char> op;
void eval() {
auto b = num.top(); num.pop();
auto a = num.top(); num.pop();
auto c = op.top(); op.pop();
int x;
if (c == '+') x = a + b;
else if (c == '-') x = a - b;
else if (c == '*') x = a * b;
else x = a / b;
num.push(x);
}
int calculate(string s) {
unordered_map<char, int> pr{{'(', 0}, {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
for (int i = 0; i < s.size(); i++) {
auto c = s[i];
if (c == ' ') continue;
if (isdigit(c)) {
int x = 0, j = i;
while (j < s.size() && isdigit(s[j]))
x = x * 10 + (s[j++] - '0');
i = j - 1;
num.push(x);
} else if (c == '(') {
op.push(c);
} else if (c == ')') {
while (op.top() != '(') eval();
op.pop();
} else {
while (op.size() && pr[op.top()] >= pr[c]) eval();
op.push(c);
}
}
while (op.size()) eval();
return num.top();
}
};