【LeetCode-772】基本计算器 III
问题
实现一个基本的计算器来计算简单的表达式字符串(带括号的四则运算)。
表达式字符串只包含非负整数,算符 +、-、*、/ ,左括号 ( 和右括号 ) 。整数除法需要 向下截断 。
你可以假定给定的表达式总是有效的。
示例
输入:s = "2*( - 5+5*2)/ 3+(6/2+8)"
输出:14
解答
class Solution {
public:
int calculate(string s) {
trim(s);
mid2post(s);
return post2res();
}
private:
vector<string> postfix;
stack<char> opStk;
unordered_map<char, int> getPr{{'(', 0}, {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
void trim(string& s){ // 将'(-'转换为'(0-',避免特殊情况讨论
int i = 0;
for (char j : s)
if (j != ' ') s[i++] = j;
s.resize(i);
int pos = s.find("(-");
while(pos != -1){
s.replace(pos, 2, "(0-");
pos = s.find("(-");
}
}
void mid2post(string s) {
int n = s.size();
for (int i = 0; i < n; i++) {
if (isdigit(s[i])) { //当前是数字
string curNum;
while (i < n && isdigit(s[i]))
curNum += s[i++];
i--;
postfix.push_back(curNum);
}
else if (s[i]=='(') opStk.push('('); //当前是左括号
else if (s[i]==')'){ //当前是右括号
while(!opStk.empty() && opStk.top()!='('){
postfix.push_back(string(1, opStk.top()));
opStk.pop();
}
opStk.pop(); //左括号也要弹出来
}
else { //当前是 + - * / 这些符号
// 维护一个严格单调递增栈opStk,对于不比当前符号优先级高的,可以加入后缀表达式直接计算
while (!opStk.empty() && getPr[opStk.top()] >= getPr[s[i]]) {
postfix.push_back(string(1, opStk.top()));
opStk.pop();
}
opStk.push(s[i]);
}
}
while (!opStk.empty()) {
postfix.push_back(string(1, opStk.top()));
opStk.pop();
}
}
int post2res() {
stack<int> numStk;
numStk.push(0);
for (string& cur : postfix) {
if (isdigit(cur[0])) numStk.push(stoi(cur));
else {
int num2 = numStk.top(); numStk.pop();
int num1 = numStk.top(); numStk.pop();
switch (cur[0]) {
case '+': numStk.push(num1 + num2); break;
case '-': numStk.push(num1 - num2); break;
case '*': numStk.push(num1 * num2); break;
case '/': numStk.push(num1 / num2);
}
}
}
return numStk.top();
}
};
重点思路
需要实现中缀转后缀,然后后缀求解最终结果两个步骤。本方法的重难点是第一步:中缀转后缀。中缀表达式就是我们平时看到的普通的式子,符号在数字中间;后缀表达式计算机更容易编程实现(符号优先级体现在字符顺序中),符号在数字后面。
中缀转后缀
这一步使用两个数据结构:
opStk
用于存储符号的栈,该栈括号内符号优先级严格递增,即在括号内为一个符号优先级单调栈。这里解释一下为什么需要维护单调递增栈。后缀表达式中,越靠前的符号,其符号优先级越高(因为会先算),而单调递增栈的特点是能找到下一个比当前值小的数,在这之前的栈顶元素都大于等于当前值。那么对于这个符号来说,出栈的符号优先级都不比这个符号低,需要先计算,可以立即加入后缀表达式中;postfix
用于存储转换后的后缀表达式。
整个转换流程如下。
- 输入的是数字
- 直接加入
postfix
。
- 直接加入
- 输入的是左括号
- 左括号直接加入
opStk
,用于限制输入右括号时的符号弹出数量。
- 左括号直接加入
- 输入的是右括号
- 弹出
opStk
中所有符号,直至遇到左括号。
- 弹出
- 输入的是运算符
- 确定优先级顺序:
(
<+ -
<* /
; - 当
opStk
为空或者s[i]
优先级大于opStk
栈顶元素时,s[i]
入栈opStk
; - 当
s[i]
优先级小于等于opStk
栈顶元素时,将大于或等于s[i]
优先级的符号依次出栈至后缀表达式postfix
中。
- 确定优先级顺序:
后缀转结果
这一步相比起来就简单很多了,使用一个栈numStk
存储数字,当遇到符号时,使用该符号运算栈顶两个元素,先出栈的在符号右边。