表达式计算(栈的经典应用)

题目引入:151. 表达式计算4 - AcWing题库

 题目信息:

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;
}

posted @ 2022-05-05 08:42  光風霽月  阅读(32)  评论(0编辑  收藏  举报