表达式计算(栈的经典应用)
题目信息:
1.可能会出现负号
2.可能出现多余括号
3.其中运算符仅包含+,-,*,/,^
(加 减 乘 整除 乘方)
4.数据保证不会出现大于或等于2^31的答案。
思路:
1.建立两个栈,一个用来保存数字信息(num),一个用来保存符号信息(ops)
2.在给定表达式的头和尾分别加上‘( ’ 和 ‘ )’,使得表达式整体上是一个 (......) 的形式 ,这样就可以确保最后彻底计算,因为本题可能会出现冗余的括号,所以要在给定表达式的左端多加括号
3.运算符优先级(递增顺序):(+, -),(*,/),(^),(())
特殊的,如果是‘( ’ 直接压入栈,对于‘ )’,不需要入栈,如果 ‘ - ’ 表示负号,不需要入栈
4.遍历整个表达式
(1)当遍历到的字符为数字时,计算数字的总和,因为数字可能是多位数,继续便利,直到遍历到符号,将数字总和压入 num 栈
(2)如果是字符,则要判断是否需要运算符出栈计算,并且如果不是' )'需要入栈,因为在遇到 ‘ )’时,我们肯定要计算一个括号内的内容(优先级最高),所以‘ )’没有入栈的必要
模拟:对于表达式 3 + 4 * 5 ^ 2 + 1,在遍历到 ‘ + ’ 时,不能计算,入栈,因为还不确定后面是否有优先级更高的运算符,例如这里,如果遍历到‘ + ’时直接计算 3 + 4的值,显然是错误的,因为后面还有个 ‘ * ’ 号,同理,在遍历到 ‘ * ’ 时,仍然不能计算 ‘ * ’,因为还不能确定后面是否还有优先级更高的 ‘ ^ ’,并且由于 ' * ' 都不能计算,‘ + ’ 也就不能计算,接下来遍历到符号 ‘ + ’,因为 ‘ + ’的优先级最小,所以前面的所有符号就都可以出栈计算了。
特殊的,‘( ’无需考虑计算直接入栈即可,遍历到 ‘ ) ’需要把‘( ’后面的所有符号出栈计算,因为双括号包括的区域优先级最高。
总结:while(pos栈顶优先级 >= 当前符号优先级)栈顶出栈计算;因为表达式是按优先级从小到大计算的,对于优先级相同的,按先后顺序
5.一个例外:‘ - ’ 不一定是减运算法,还有可能表示一个负数
例如:
(1):-(...)
(2):-1 + 5,-1 - 5,-1 * 5,-1 / 5,-1 ^ 2
(3):5 * -1,6 / -1,5 ^ -1
(4):6 *(-1 + 5)等等
可以发现,只要 ‘ - ’ 前面不是一个数字(例如4 - 5不符合),或者一个右括号(例如 5 - () ),那么这个 ‘ - ’ 就是负号,但是对于情况:-(...),如果我们没有把负号入栈就会发生错误,因为 ‘ - ’ 后面紧跟了一个 ‘( ’,没有数字,所以在实际操作过程中我们相当于把这个 ‘ - ’ 忽略掉了
例如:(-(1+1)+2),如果没有对该情况特殊处理,这里就相当于((1+1)+2) = 4
处理的小技巧,把情况:-()改为情况:-1*()即可,即把负号这个单目运算符直接变成 -1 和 * 两个符号
AC代码
#include <iostream>
#include <string>
#include <stack>
using namespace std;
stack <int> num;
stack <char> ops;
void cal()
{
int b = num.top(); num.pop();
int a = num.top(); num.pop();
char c = ops.top(); ops.pop();
if(c == '+') a = a + b;
else if(c == '-') a = a - b;
else if(c == '*') a = a * b;
else if(c == '/') a = a / b;
else // c == '^'
{
int d = 1;
while(b--) d *= a; //不要忘了b要--
a = d;
}
num.push(a);
}
int main()
{
string str, left, right;
cin >> str;
for(int i = 0; i < str.length(); i ++ ) left += '(';
str = left + str + ')';
// cout << str << endl;
for(int i = 0; i < str.length(); i ++ )
{
char c = str[i];
if(c >= '0' && c <= '9') //数字
{
int t = 0, j = i;
while(str[j] >= '0' && str[j] <= '9') //不要写成str[i]
{
t = t * 10 + str[j] - '0'; //不要写成str[i]
j ++ ;
}
i = j - 1;
num.push(t);
}
else //运算符
{
if(c == '(') ops.push(c);
else if (c == '+' || c == '-')
{
if (c == '-' && !(str[i - 1] >= '0' && str[i - 1] <= '9') && str[i - 1] != ')')
{
if (str[i + 1] == '(') // ?-(...)??-1 * (...)
{
num.push(-1);
ops.push('*');
}
else
{
int j = i + 1, t = 0;
while (str[j] >= '0' && str[j] <= '9') //不要写成str[i]
{
t = t * 10 + str[j] - '0'; //不要写成str[i]
j ++ ;
}
num.push(-t);
i = j - 1;
}
}
else
{
while (ops.top() != '(') cal();
ops.push(c);
}
}
else if(c == '*' || c == '/')
{
while(ops.top() == '^' && ops.top() == '*' || ops.top() == '/') cal();
ops.push(c);
}
else if(c == '^')
{
while(ops.top() == '^') cal();
ops.push(c);
}
else //s[i] == ')'
{
while(ops.top() != '(' ) cal();
ops.pop(); //不要忘了删除左括号
}
}
}
cout << num.top() << endl;
return 0;
}