1358:中缀表达式值 (expr)(中缀转后缀表达式+栈)

时间限制: 1000 ms 内存限制: 65536 KB 提交数: 3759 通过数: 1268 【题目描述】
输入一个中缀表达式(由 0-9 组成的运算数、加 + 减 - 乘 * 除 / 四种运算符、左右小括号组成。注意 “-”
也可作为负数的标志,表达式以 “@” 作为结束符),判断表达式是否合法,如果不合法,请输出
“NO”;否则请把表达式转换成后缀形式,再求出后缀表达式的值并输出。

注意:必须用栈操作,不能直接输出表达式的值。

【输入】 一行为一个以 @结束的字符串。

【输出】 如果表达式不合法,请输出 “NO”,要求大写。

如果表达式合法,请输出计算结果。

【输入样例】 1+2*8-9@ 【输出样例】 8

思路

  1. 传送门

代码


#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
using namespace std;

map<char, int> p;

struct Node
{
    double num;         //操作数
    char op;            //操作符
    bool flag;          //是否为操作数
};

stack<Node> st;          //符号栈
stack<Node> sa;          //存放操作数的栈,用来求后缀表达式的值
queue<Node> q;

void init()
{
    p['+'] = p['-'] = 1;
    p['*'] = p['/'] = 2;
}

/* 表达式中缀转后缀表达式
 * 0. 从左到右边里所给的中缀字符串,执行接下来的步骤
 * 1. 遇到操作数:直接添加到后缀表达式中/直接输出
 * 2. 遇到运算符:首先把符号栈中,从栈顶开始循环输出,直到栈顶的运算符的优先级小于当前运算符的有限接,之后把当前运算符压入栈中
 * 3. 遇到左括号:直接压入到符号栈中
 * 4. 遇到右括号:把此事符号栈中的字符循环输出,直到遇到左括号,并且把左括号输出
 */
void change(string s)
{
    int n = s.size() - 1;
    Node tmp;
    for(int i = 0; i < n; )
    {
        if(s[i] == '(') 
        {
            tmp.flag = 0;
            tmp.op = s[i];
            st.push(tmp);
            i ++;
        }
        else if(s[i] == ')')
        {
            while(st.size() && st.top().op != '(')
            {
                q.push(st.top());
                st.pop();
            }
            st.pop();
            i ++;               // 把剩下的'('给弹出 
        }
        else if(s[i] >= '0' && s[i] <= '9')
        {
            tmp.flag = 1;
            tmp.num = s[i] - '0';
            i ++;               //后移一位因为数字可能不是一位!!!
            while(i < n && s[i] >= '0' && s[i] <= '9')
            {
                tmp.num *= 10;
                tmp.num += s[i] - '0';
                i ++;
            }

            q.push(tmp);        //操作数最后进入后缀表达式
        }
        else        //遇到操作符,弹出栈顶优先级比该操作符大的运算符
        {
            tmp.flag = 0;
            while(st.size() && p[s[i]] <= p[st.top().op])
            {
                q.push(st.top());
                st.pop();
            }

            tmp.op = s[i];
            st.push(tmp);              //最后不要忘了把当前操作符压入栈中去
            i ++;
        }
    }

    //最后结束将栈中的所有操作符都输出
    while(st.size())
    {
        q.push(st.top());
        st.pop();
    }
}

/* 后缀表达式求值,利用一个栈来维护值
 * 1. 把位于 q 中的后缀表达式,从队头到队尾逐个取出,压入到 sa 栈中去
 * 2. 如果当前向 sa 中压入的是操作符 x,则把此时把 sa 中栈顶前两个元素a、b 取出来 进行 axb 操作
 * 2. 如果当前项 sa 中压入的事操作数,则报该操作数压入 sa 栈中
 */

double cal()
{
    double a, b;
    while(q.size())
    {
        Node cur = q.front(); q.pop();
        if(cur.flag)
        {
            sa.push(cur);
        }
        else
        {
            b = sa.top().num;
            sa.pop();           //弹出第二操作数
            a = sa.top().num;
            sa.pop();           //弹出第一操作数
            Node tmp;
            tmp.flag = 1;
            if(cur.op == '+')
            {
                tmp.num = a + b;
            }
            else if(cur.op == '-')
            {
                tmp.num = a - b;
            }
            else if(cur.op == '*')
            {
                tmp.num = a * b;
            }
            else if(cur.op == '/')
            {
                tmp.num = a / b;
            }

            sa.push(tmp);                //最后把计算的结果重新压入 sa 栈中去
        }
    }
    return sa.top().num;
}
bool check(string s)
{
    if((int)s.size() - 1 == 1) return ! p[s[0]];

    for(int i = 2; i < s.size() - 1; i ++)
        if(p[s[i]] && p[s[i - 1]]) return 0;

    int num = 0;
    for(int i = 0; i < s.size() - 1; i ++)
    {
        if(s[i] == '(') num ++;
        if(s[i] == ')') num --;
    }

    return num == 0;
}




int main()
{
    init();
    string s;
    cin >> s;
    if(! check(s)) 
    {
        cout << "NO" << endl;
        return 0;
    }

    change(s);
    cout << cal();

    return 0;
}



  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值