- 中缀表达式指的是"1+2-3*4/5"这种其实就是我们通常见到的书写算式顺序,要计算中缀表达式则首先要将字符串转换成后缀表达式并存储在一个队列当中,即1 2 3 4 5 / * - +(空格只是为了隔开方便理解),可以看到数字都集中在了前面,算术符号则集中在后面。然后计算后缀表达式的方式便是从开头遍历,遇到数字则入栈,遇到符号便pop()两个数字出来进行运算,然后再入栈,最后只剩一个数据在栈顶即最终结果。
- 要注意的细节是,读取的数字可能不只一位,可能为负数,可能为小数,按题目具体情况分析
- 一开始读取的字符串当中可能有多余的空格,但题目可能不会提醒,遍历的时候要顺便进行特判
- 运算符号之间的优先等级通过map建立映射关系:$(*==/) > (+==-)$ 当前符号优先级高于栈顶操作符则入栈,如果低于或等于则将不停出栈直至栈空了或者符合当前符号优先级高于栈顶的符号优先级。(因为越早进入队列的符号意味着越早进行运算,所以优先级高的自然早过优先级低的,相同优先级的时候影响的是结合律:$(a*b/c) != (a*(b/c))$) 因此也相同的时候也要出栈。
- 遍历一遍过后,如果栈中还剩符号,则依次全部出栈并push进队列中形成后缀表达式。
- 如果题目出现括号的话,那么遍历的时候就加入一个判断,如果是左括号'(',就像其他符号一样入栈,但是遇到右括号')'的时候就不入栈,然后直接就符号不停地出栈,直到遇到左括号就停止出栈。注意括号是不会出现在后缀表达式中的,影响的只是计算的顺序。
- 在计算的过程中可能会出现非法运算,注意进行特判
#include <iostream> #include <string> #include <map> #include <stack> #include <queue> using namespace std; struct node { double num; // 也可以改成 int 具体情况具体分析 char op; // 存储符号 bool flag; // true代表数字 false代表符号 }; string str; stack s; queue q; map<char,int> m; void Change() // 中缀表达式转换为后缀表达式 { double num; node temp; for(int i=0;i<str.length();) { if(str[i]>='0' && str[i]<='9') { temp.flag = 1; temp.num = str[i++] - '0'; while(i<str.length() && str[i]>='0' && str[i]<='9') { temp.num = temp.num*10 + (str[i]-'0'); ++i; } q.push(temp); } else if(str[i]==' ') { ++i; } else { temp.flag = 0; while(!s.empty() && m[str[i]]<=m[s.top().op]) { q.push(s.top()); s.pop(); } temp.op = str[i]; s.push(temp); ++i; } } while(!s.empty()) //转换结束后栈为空,队列保存的是一个完整的后缀表达式 { q.push(s.top()); s.pop(); } } double cal() { double temp1,temp2; while(!q.empty()) //将队列遍历,最终结果保存在栈里 { node head = q.front(); q.pop(); if(head.flag == 1) { s.push(head); } else if(head.flag == 0) { node b = s.top(); //第一次弹出的是第二操作数 s.pop(); node a = s.top(); //第二次弹出的是第一操作数 s.pop(); node c; c.flag = 1; if(head.op == '+') { c.num = a.num + b.num; } else if(head.op == '-') { c.num = a.num - b.num; } else if(head.op == '*') { c.num = a.num * b.num; } else if(head.op == '/') //有可能出现除以零的非法,要特判,不过这里没写 { c.num = a.num / b.num; } s.push(c); } } return s.top().num; } int main() { m['+'] = m['-'] = 0; //设置优先级 m['*'] = m['/'] = 1; getline(cin,str); Change(); cout << cal() << endl; return 0; }