0%

算法系列:计算器问题

计算器问题大约在大二数据结构的课程上学过,当时是使用栈来解决,此外还需要设置很多符号的优先级,以此判断是否弹栈,代码写的很长很麻烦。今天又遇到了这种题,也有了简单的解法,做一个整理。

计算器的难点在于先算乘除后算加减,如果有小括号,需要先算括号里面的内容。因此从简往难,一点点的来看问题如何解决。

无括号

无括号相对简单一些,我们只需要考虑先算乘除后算加减即可。那么思路就是:如果当前符号是加法或减法,那么将符号连带数字压入栈中,比如 +10 或 -11;如果遇到的是乘法或除法,那么就需要把栈尾的元素拿出来,做乘法或除法运算在放入栈中。最后,对栈内的元素求和即可。

我们来看程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
int calculate(string s) {
int idx = 0;
return subcal(s, idx);
}
int subcal(string s, int& idx) {
int res = 0;
stack<int> stk;
int num = 0;
char sign = '+';
for (int i = 0; i < s.size(); i++) {
char c = s[i];
if (isdigit(c)) {
num = 10 * num + (c - '0');
}
if (!isdigit(c) && c != ' ' || i == s.size() - 1) {
if (sign == '+') {
stk.push(num);
} else if (sign == '-') {
stk.push(-num);
} else if (sign == '*') {
auto tmp = stk.top();
stk.pop();
stk.push(tmp * num);
} else if (sign == '/') {
auto tmp = stk.top();
stk.pop();
stk.push(tmp / num);
}
sign = c;
num = 0;
}
}
while (stk.size()) {
res += stk.top();
stk.pop();
}
return res;
}
};

程序中有很多的细节,一点点来分析:

  • sign 初始值为 +,这样如果开头的数字是正数,那么这个数会被放入栈中;如果开头的数字是负数,那么栈中先放入 0,在放入负数,不影响。
  • 符号 c 的判断不构成 if-else-if 关系,因为当 i=s.size()-1 的时候,需要处理最后一个数字。即,c 是数字且是最后一个字符的情况下,需要经过这两个分支的处理。

有括号

右括号就比较烦人,因为括号的优先级高于一切,需要找到最内部的括号,逐层往外回退得到答案。而这,也让人容易联想到传说中的递归。

那么递归需要记录关于括号的哪些东西呢?想法自然是遇到一个左括号,那么就计算括号内部的东西,遇到右括号返回。之后跳过括号里面的内容,计算下一个表达式。括号中的括号也是同理。我们来看程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Solution {
public:
int calculate(string s) {
int idx = 0;
return subcal(s, idx);
}
int subcal(string s, int& idx) {
int num = 0;
char sign = '+';
stack<int> stk;
for (int i = 0; i < s.size(); i++) {
char c = s[i];
if (isdigit(c))
num = 10 * num + (c - '0');
if (c == '(') {
num = subcal(s.substr(i+1), idx);
i += (idx + 2);
c = s[i];
}
if (!isdigit(c) && c != ' ' || i == s.size() - 1) {
if (sign == '+')
stk.push(num);
else if (sign == '-')
stk.push(-num);
else if (sign == '*') {
auto tmp = stk.top();
stk.pop();
stk.push(num * tmp);
}
else if (sign == '/') {
auto tmp = stk.top();
stk.pop();
stk.push(tmp / num);
}
sign = c;
num = 0;
}
if (c == ')') {
idx = i;
break;
}
}
int res = 0;
while(stk.size()) {
res += stk.top();
stk.pop();
}
return res;
}
};

同样来解读一下:

  • 如果遇到右括号,记录索引,表示当前括号的内容计算完了,退出。
  • 如果遇到左括号,那么计算左括号后面子串的内容得到数字,i=i+(idx+2) 的意思是,跳到右括号后面第一个字符继续计算,+2 为什么是后面第一个字符呢?因为 substr(i+1) 了,这里向后移动了一位,对于 i 来说,+2 才是后面的第一位。
感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章