题目描述
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
给定的字符串表达式由数字、‘+’、‘-’、‘(’、‘)’、‘ ’组成。
比如给定字符串"1 +1", 函数需要返回结果2。
解法:双栈
首先看到这个题应该会想到后缀表达式的计算过程,设置一个nums栈存放数字,遇到运算符就取nums栈顶两个元素计算,然后放回栈中。但是题目给的是一个中缀表达式,计算需要考虑优先级,因此可以增加一个ops栈来存放操作符,而与后缀表达式不同的是,计算过程中根据不同的ops执行不同的操作步骤。
遍历一个字符串表达式的具体过程如下:
数字: 直接放入nums栈。 这里在代码中需要注意,有些数字可能是由好几位组成,比如123+123, 读取的时候一定要注意将123整体读进去。
左括号: 直接入ops栈。 左括号表示括号内计算的开始,直接入栈,不用执行任何操作。
右括号:按照后缀表达式的方法计算,直到遇到左括号。 右括号与之对应的左括号构成了一个子式,这个子式在遇到右括号时就可以开始计算。
加减: 遇到加减,先判断ops栈顶是否还是加减,如果是的话,说明nums中有数字还没有计算,按照后缀表达式的方法计算就好,计算完成后将这一位的加减入栈。
在实现过程中还有一些细节需要注意:
- 所给表达式中可能存在一些空格,需要先将表达式中的空格清除。
- 表达式首位可能是负号,这样操作符出栈时,nums栈也出栈,可能会报栈为空的错误,所以可以往表达式首位放一个0,避免分情况讨论。
- 与第二点类似,可能出现连续符号的情况,比如1+(-2),为避免报错,可以在-号入栈时,nums中加一个0。
代码:
class Solution {
public:
void cleanSpace(string& s){
int pos = s.find(" ");
while (pos != -1) {
s.replace(pos, 1, "");
pos = s.find(" ");
}
}
void calc(stack<int>& nums, stack<char>& ops){
if(nums.size()<2 || ops.empty()) return;
int a=nums.top();
nums.pop();
int b=nums.top();
nums.pop();
char op=ops.top();
ops.pop();
nums.push(op=='+'?a+b:b-a);
}
int calculate(string s) {
stack<int> nums;
stack<char> ops;
cleanSpace(s);
// 清除空格
nums.push(0);
int n=s.size();
for(int i=0; i<n; i++){
char c=s[i];
if(c=='('){
ops.push(c);
}
else if(c==')'){
// 右括号的话计算到碰到左括号
while(!ops.empty()){
if(ops.top()!='('){
// 计算没有遇到终点
calc(nums, ops);
}
else{
// 遇到左括号 运算结束
ops.pop();
// 左括号弹出
break;
// 跳出循环
}
}
}
else if(isdigit(c)){
// 如果是数字
// 需要考虑一下连续数字的情况
int num=0;
int j=i;
while(j<n && isdigit(s[j])){
// j在数组范围内而且j位置处是数字如果j开始是数字
num = num*10 + (s[j]-'0');
j++;
}
// 计算出一整串数字 最后j停留在数字的下一位上
i=j-1;
nums.push(num);
}
else{
// 如果是正负号 看看它的左边有没有左括号
if(i>0 && (s[i-1]=='(' || s[i-1]=='+' || s[i-1]=='-')){
nums.push(0);
}
// 正负号别着急入栈 看看ops栈顶是不是+-,如果是的话先计算
while(!ops.empty() && ops.top()!='('){
calc(nums, ops);
}
ops.push(c);
}
}
if(!ops.empty()){
calc(nums, ops);
}
return nums.top();
}
};