224. 基本计算器(困难)
答案整理 加深记忆 便于复习!
python eval()函数还没完全看懂。。。
题目描述
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
示例 1:
输入:s = “1 + 1”
输出:2
示例 2:
输入:s = " 2-1 + 2 "
输出:3
示例 3:
输入:s = “(1+(4+5+2)-3)+(6+8)”
输出:23
提示:
1 <= s.length <= 3 * 105
s 由数字、’+’、’-’、’(’、’)’、和 ’ ’ 组成
s 表示一个有效的表达式
方法:括号展开 + 栈
由于字符串除了数字与括号外,只有加号和减号两种运算符。因此,如果展开表达式中所有的括号,则得到的新表达式中,数字本身不会发生变化,只是每个数字前面的符号会发生变化。
因此,我们考虑使用一个取值为 {−1,+1} 的整数 sign 代表「当前」的符号。根据括号表达式的性质,它的取值:
- 与字符串中当前位置的运算符有关;
- 如果当前位置处于一系列括号之内,则也与这些括号前面的运算符有关:每当遇到一个以 − 号开头的括号,则意味着此后的符号都要被「翻转」。
考虑到第二点,我们需要维护一个栈 ops,其中栈顶元素记录了当前位置所处的每个括号所「共同形成」的符号。例如,对于字符串 1+2+(3-(4+5)):
- 扫描到 1+2 时,由于当前位置没有被任何括号所包含,则栈顶元素为初始值 +1;
- 扫描到 1+2+(3 时,当前位置被一个括号所包含,该括号前面的符号为 ++ 号,因此栈顶元素依然 +1+1;
- 扫描到 1+2+(3-(4 时,当前位置被两个括号所包含,分别对应着 + 号和 − 号,由于 + 号和 − 号合并的结果为 − 号,因此栈顶元素变为 -1。
在得到栈 ops 之后, sign 的取值就能够确定了:如果当前遇到了 + 号,则更新 sign ← ops.top();如果遇到了遇到了 − 号,则更新sign← - ops.top()。
然后,每当遇到 ( 时,都要将当前的 sign 取值压入栈中;每当遇到 ) 时,都从栈中弹出一个元素。这样,我们能够在扫描字符串的时候,即时地更新 ops 中的元素。
C++:
class Solution
{
public:
int calculate(string s)
{
stack<int> ops;
ops.push(1);
int sign = 1;//1代表加,下面如果是减,就变成-1,ret += sign*num这句代码实现了加法是加(数)本身,减法是加(数x负一)
int ret = 0;
int n = s.length();
int i = 0;
while (i < n)
{
if (s[i] == ' ') i++;
//利用正负符号将括号击穿,直接一遍就计算出来了
//下面几句话要多加品味
else if (s[i] == '+') { sign = ops.top(); i++; }
else if (s[i] == '-') { sign = -ops.top(); i++; }
else if (s[i] == '(') { ops.push(sign); i++; }//遇到左括号时就将左括号前一个运算符(记为sign1)压入栈内,
//接下来左括号后右括号前的数据都将受到该符号的影响,因为在此之后ops.top()返回的都是sign1,假设括号中间还有负号,负负得正,符合实际。
else if (s[i] == ')') { ops.pop(); i++; }//一旦遇到该左括号对应的右括号,sign1就被弹出栈了,sign1的使命就结束了。
else
{
long num = 0;
//while的作用是把可能有多位的字符串转为数字
while (s[i] >= '0'&&s[i] <= '9')//一个数字可能有很多位,比如有三位,要先单独将该三位字符串转为int型数字
{
//好比将字符 ‘290’转成数字290,先解析2,再解析9时,将2*10+9=29;再解析0时,将29*10+0=290;这样,就完成了字符’290‘到数字290的转变。
num = num * 10 + (s[i] - '0');//s[i] - '0' 字符转int
i++;
}
ret += sign*num;
}
}
return ret;
}
};
复杂度分析
时间复杂度:O(n),其中 n 为字符串 s 的长度。需要遍历字符串 s 一次,计算表达式的值。
空间复杂度:O(n),其中 n 为字符串 s 的长度。空间复杂度主要取决于栈的空间,栈中的元素数量不超过 n。
使用python eval()函数
直接使用python eval()函数,报错如下:
直接eval(s)的话会报MemoryError,考虑从内到外依次计算括号(目前还没太理解明白,下次再看)
class Solution:
def calculate(self, s: str) -> int:
while True:
a=s.rfind('(')
b=s.find(')',a)
if a==-1:
break
s=s[:a]+str(eval(s[a:b+1]))+s[b+1:]
return eval(s)
附
网友的疑惑,正好也解答了我的疑问