题目
(1)给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
(2)整数除法仅保留整数部分。
(3)示例1如下:
输入:s = “3+2*2”
输出:7
(4)示例2如下:
输入:s = " 3+5 / 2 "
输出:5
解决思路
- 用双栈实现基本计算器,其中一个栈为操作数栈,另外一个栈为运算符栈。
- 第一步:先将表达式拆分为两部分,一部分只包含操作数,另外一部分只包含运算符。
- 第二步:若当前即将入栈的运算符oper1的优先级小于等于栈顶运算符oper2的优先级时,则先根据栈顶的运算符oper2对操作数栈顶元素和倒数第二个元素进行运算,然后将结果放入操作数栈中。
- 第三步:将运算符栈的栈顶元素oper2弹出。然后判断此时运算符栈的栈顶元素oper2_new的优先级和即将入栈的运算符oper1的优先级,若oper1的优先级小于等于oper2_new的优先级,则重复第二步。
- 第四步:将即将入栈的运算符oper1压入运算符栈中。
- 详细步骤如下所示:
代码
- C++代码
# include <stdio.h>
# include <string>
# include <stack>
using namespace std;
class Solution {
public:
// 获取运算符优先级
int level(char s) {
switch (s) {
case '@':
return -1;
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0; // 0表示当前字符是数字,用于将字符转换成数值
}
}
// 根据运算符进行计算
int myCalculate(int a, int b, char oper) {
switch (oper) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
default:
return 0;
}
}
int calculate(string s) {
stack<int> num; // 存储操作数的栈。
stack<char> operation; // 存储运算符的栈。
int n = 0; // 某个具体的数值(字符串数值转换为整数)。
// 技巧:自定义一个操作符@,使其运算优先级最低,并将@拼接在字符串的最后,用于简化操作。
// 当遍历字符串遇到@操作符时,会先对操作数栈中剩下的两个数进行运算,然后再将结果压入操作数栈中。
// 由于使用了操作符@,简化了返回结果前的最后一步操作流程。 如果没有操作符@,则要设定条件来判断哪个操作符是最后一个操作符。
// 然后将剩余的操作符对应的操作运算完,再返回最终结果。
s += '@';
// 1. 遍历字符串中的每个字符,将数值和运算符分开,并根据运算符进行相应的操作
for (int i = 0; i < s.size(); i++) {
if (' ' == s[i]) {
continue;
}
// 2. 当前字符为数值
// 第一步:获取字符串中的数值(剩下的字符为运算符)。
if (0 == level(s[i])) {
n = n * 10 + (s[i] - '0'); // 将字符串转换成数值,如将“123”变成整数123
continue;
}
num.push(n); // 将数值放入操作数栈
n = 0; // 将值设置为0,便于计算下一个值。
// 3. 当前字符为运算符,根据运算符进行过相应的运算
// 第二步:当前运算符的优先级小于等于栈顶元素的优先级时,先根据栈顶运算符对栈顶和倒数第二个元素进行运算。
// 并将运算结果压入操作数栈中,然后从运算符栈中弹出栈顶运算符,然后将当前运算符压入运算符栈。
while (!operation.empty() && level(s[i]) <= level(operation.top())) {
int b = num.top();
num.pop();
int a = num.top();
num.pop();
num.push(myCalculate(a, b, operation.top())); // 将运算结果压入栈中。
operation.pop(); // 第三步:将运算符栈的栈顶元素弹出。
}
// 第四步:将即将入栈的运算符压入运算符栈中。
// 注意:要等运算符栈顶中优先级高的运算符有运算完后,再将当前运算符压入运算符栈中。
operation.push(s[i]);
}
// 因为使用了自定义的@运算符,所以返回结果前不需要进行复杂的操作,只需返回栈顶元素即可。
return num.top();
}
};
int main() {
string s = "3+2*2";
Solution *solution = new Solution();
printf("%d\n", solution->calculate(s));
return 0;
}
- Python代码
# -*- coding: utf-8 -*-
from typing import List
import math
class Solution:
def __init__(self):
pass
# 获取运算符优先级
def level(self, s: str) -> int:
if '@' == s:
return -1
elif '+' == s or '-' == s:
return 1
elif '*' == s or '/' == s:
return 2
else:
return 0 # 0表示当前字符是数字,用于将字符转换成数值
# 根据运算符进行计算
def myCalculate(self, a: int, b: int, oper: str) -> int:
if '+' == oper:
return a + b
elif '-' == oper:
return a - b
elif '*' == oper:
return a * b
elif '/' == oper:
return math.floor(a / b)
def calculate(self, s: str) -> int:
num: List[int] = [] # 存储操作数的栈。
operation: List[str] = [] # 存储运算符的栈。
n: int = 0 # 某个具体的数值(字符串数值转换为整数)。
# 技巧:自定义一个操作符 @,使其运算优先级最低,并将 @ 拼接在字符串的最后,用于简化操作。
# 当遍历字符串遇到 @ 操作符时,会先对操作数栈中剩下的两个数进行运算,然后再将结果压入操作数栈中。
# 由于使用了操作符 @,简化了返回结果前的最后一步操作流程。 如果没有操作符 @,则要设定条件来判断哪个操作符是最后一个操作符。
# 然后将剩余的操作符对应的操作运算完,再返回最终结果。
s += '@'
# 1. 遍历字符串中的每个字符,将数值和运算符分开,并根据运算符进行相应的操作
for i in s:
if ' ' == i:
continue
# 2. 当前字符为数值
# 第一步:获取字符串中的数值(剩下的字符为运算符)。
if 0 == self.level(i):
# 将字符串转换成数值,如将“123”变成整数123
n = n * 10 + int(i)
continue
num.append(n) # 将数值放入操作数栈
n = 0 # 将值设置为0,便于计算下一个值。
# 3. 当前字符为运算符,根据运算符进行过相应的运算
# 第二步:当前运算符的优先级小于等于栈顶元素的优先级时,先根据栈顶运算符对栈顶和倒数第二个元素进行运算。
# 并将运算结果压入操作数栈中,然后从运算符栈中弹出栈顶运算符,然后将当前运算符压入运算符栈。
while operation and self.level(i) <= self.level(operation[-1]):
b = num.pop() # 栈顶元素
a = num.pop() # 倒数第二个元素
num.append(self.myCalculate(a, b, operation[-1])) # 将运算结果压入栈中。
operation.pop() # 第三步:将运算符栈的栈顶元素弹出。
# 第四步:将即将入栈的运算符压入运算符栈中。
# 注意:要等运算符栈顶中优先级高的运算符有运算完后,再将当前运算符压入运算符栈中。
operation.append(i)
# 因为使用了自定义的@运算符,所以返回结果前不需要进行复杂的操作,只需返回栈顶元素即可。
return num[-1]
def main():
solution = Solution()
s = "3+2*2"
print(solution.calculate(s))
if __name__ == "__main__":
main()
说明
- 对应LeetCode第227题。
- 链接:https://leetcode-cn.com/problems/basic-calculator-ii/