一、题目分析
输入一个字符串,是正整数、加(+)、减(-)、乘(*)、除(/)的算术表达式,设计一个计算器,计算该算术表达式的结果,表达式不包含括号()。整数除法仅保留整数部分。
二、使用步骤
1.解题思路
以表达式"2+3*4*5-10/6"计算为例,遍历该表达式进行计算的过程有如下几种情况:
- 当前遇到的字符是运算符:由于未知后续运算符的优先级,所以将该运算符压入用于保存运算符的栈中,在后续遍历过程中再次遇到运算符,与当前栈顶的运算符进行比较,如果当前运算符的优先级小于或等于栈顶运算符的优先级,说明上一个运算符与相关数字可以进行运算。
- 当前遇到的字符是数字位:该位置可能是一个单独的数字也可能是两位数的十位,需要继续向后遍历几位,确定遇到下一个运算符之前的数字的大小,将计算好的数字压入用于保存数字的栈中。
2.代码实现
利用一个unordered_map结构记录四个运算符的优先级。
class Solution {
public:
//记录运算符号优先级的表
unordered_map<char, int> table{
{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
int operate(int a, int b, char op){
if(op == '/' && a != 0) return a / b;
else if(op == '-') return a - b;
else if(op == '*') return a * b;
else if(op == '+') return a + b;
else
return 0;
}
int calculate(string s) {
//移除表达式中的空格
s.erase(remove(s.begin(), s.end(), ' '), s.end());
int res = 0; //保存表达式结果
int i = 0;
stack<int> numStk; //保存数值的栈
stack<char> opStk; //保存运算符的栈
while(i < s.size()){
int num = 0;
if(isdigit(s[i])){ //当前位为数字位
while(isdigit(s[i])){
num = num * 10 +(s[i] - '0');
++i;
}
numStk.push(num);
}else{ //当前位为运算符
while(!opStk.empty() && table[s[i]] <= table[opStk.top()]){ //当前运算符的优先级低于运算符栈栈顶运算符的优先级,可以计算上一步运算
int b = numStk.top(); numStk.pop();
int a = numStk.top(); numStk.pop();
numStk.push(operate(a, b, opStk.top()));
opStk.pop();
}
opStk.push(s[i]); ++i;
}
}
//从两个栈中还原最终结果
while(!opStk.empty() && !numStk.empty()){
int b = numStk.top(); numStk.pop();
int a = numStk.top(); numStk.pop();
numStk.push(operate(a, b, opStk.top()));opStk.pop();
}
res = numStk.top();
return res;
}
};
注意事项:
- 题目中假设给出的表达式都是有效的,因此代码中省略了部分判断
- 除法运算/的除数不能为零
- 测试用例输入的字符串是含有空格的,因此第一步需要将给出字符串中的空格全部删除,删除字符串中空格采用erase和remove两个函数,具体用法如下:
- remove
ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& val);
将[fist, last)范围内所有等于val的元素删除,删除是通过将等于val元素替换为下一个不等于val的元素来完成的,函数返回缩短后新范围的末尾。
- erase
采用string类型的成员函数erase:
iterator erase(const_iterator first, const_iterator last);
删除[first, last)范围内的字符。