227. 基本计算器 II
思路分析
-
题目大意:给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。
-
有几个注意点:
- 运算符有
+ - * /
,其中/
是整除。 - 没有负数。
- 数字的范围是整型。
- 运算符有
-
刚开始想用 栈 ,在写代码的过程中发现了一种自认为 优美 的解题方法。具体想法如下。
-
比如,计算
s = "3+2*2"
,显然应该优先计算2*2
,一种通俗的思路是:先算乘除,再算加减 。如果我们算完乘除,那么s
会变成s'=3+4
,也就是我们算完所有的乘除以后,表达式中就只会有加减了,那么从左到右计算即可。 -
不妨假设表达式中只有加减法。那么,计算
s
的值是不是就很简单了?我们可以计ret
存放当前的计算结果,ret_sign
表示加号或者减号。- 比如,
s = "1+2-4"
。如果当前位置在1
处,那么,ret=0
,ret_sign='+'
(此时可以想象在s前面虚拟出来一个0+);假设当前位置是2
,那么,ret=1
,ret_sign='+'
;假设当前位置在4
处,那么此时ret=3
,ret_sign='-'
。 - 显然,ret的初始值为0,ret_sign的初始值为+(不妨定义ret_sign=1时为+,ret_sign=0时为-)。
- 比如,
-
现在,我们再次做一个假设,如果表达式中只有乘除,问题是不是又一下子明朗了?不妨设
tmp
存放当前的计算结果,tmp_sign
表示乘号或者除号。- 举个栗子,比如,
s = 2*2/4
。如果当前位置在1
处,那么,tmp=1
,tmp_sign='*'
(此时可以想象在s前面虚拟出来一个1*);假设当前位置是2
,那么,tmp=2
,tmp_sign='*'
;假设当前位置在4
处,那么此时tmp=4
,tmp_sign='/'
。 - 显然,tmp的初始值为1,tmp_sign的初始值为*(不妨定义tmp_sign=1时为*,ret_sign=1时为/)。
- 举个栗子,比如,
-
但是,表达式中可能会有加减,也可能有乘除。但是,表达式
s
可以写成如下通用格式(暂时不考虑空格):s = s1[+-]s2[+-]s3[+-]...si...sn
,其中si
是一个数字或者是只有乘除的表达式。- 显然,对于
s
,我们可以认为只有加减法。对于任意一个si
,只有乘除法。
- 显然,对于
-
我们认为
s
只有加减法的前提条件是s
中得有加减号,不排除s = 1234
这种s
中没有加减号的情况。但是,我们可以使用一个奇淫技巧"哨兵",我们让s=s+0
,那么就能保证s
中一定有加减号了。 -
有了上述思路,我么可以写出AC代码:
class Solution {
public:
int calculate(string s) {
s = s + "+0";
int n = s.size();
int pos = 0;
int ret = 0;
int tmp = 1;
int ret_sign = 1; // 1 +; 0 -
int tmp_sign = 1; // 1 *; 0 /
while (pos < n){
if (s[pos] == ' '){
pos ++;
}else if (s[pos] >= '0' && s[pos] <= '9'){
int num = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9'){
num = num * 10 + (s[pos] - '0'); // 注意1
pos ++;
}
if (tmp_sign == 1){
tmp = tmp * num;
}else{
tmp = tmp / num;
}
}else{
if (s[pos] == '+'){
if (ret_sign == 1){
ret += tmp;
}else{
ret -= tmp;
}
ret_sign = 1;
tmp = 1;
tmp_sign = 1;
}else if (s[pos] == '-'){
if (ret_sign == 1){
ret += tmp;
}else{
ret -= tmp;
}
ret_sign = 0;
tmp = 1;
tmp_sign = 1;
}else if (s[pos] == '*'){
tmp_sign = 1;
}else {
tmp_sign = 0;
}
pos ++;
}
}
return ret;
}
};
执行用时:4 ms, 在所有 C++ 提交中击败了99.44%的用户
内存消耗:8.1 MB, 在所有 C++ 提交中击败了80.06%的用户
-
代码略长,有很多代码可以简化,这里不再简化。比如:
if (ret_sign == 1){ ret += tmp; }else{ ret -= tmp; }
可以简化成一行:
ret += (ret_sign ? -tmp : tmp)
。 -
代码注释中的 注意1:注意,如果不加括号,可能在计算
num * 10 + s[pos]
时就会超出int范围。 -
复杂度分析:
- 时间复杂度:
O(n)
。一次遍历。 - 空间复杂度:
O(1)
。定义了可数个变量。
- 时间复杂度:
LeetCode官方题解
-
具体思路是:先利用栈计算乘除(如果碰到减号,将数字的相反数压栈),然后计算栈中数字的和。两次遍历,一次乘除,一次加减。时间复杂度是
O(n)
。使用了栈,空间复杂度是O(n)
。 -
由于篇幅关系,这里不再赘述。
2020.3.11 23:57
今晚巅峰赛输了一晚上,晚上十点半才开始写博客,真是让人赧然啊。