OJ - 中缀表达式转后缀表达式
题目难度:简单
OJ链接:中缀表达式转后缀表达式_牛客网 (nowcoder.com)
将中缀表达式转为后缀表达式(逆波兰表达式),输入 a+b*c/d-a+f/b 输出 abc*d/+a-fb/+
要求:语言不限;输入输出均为单个字符串;操作数用单个小写字母表示,操作符只需支持 +-*/,按照四则运算顺序确定优先级,不包含括号。
示例:
输入:a+b*c/d-a+f/b
输出:abc*d/+a-fb/+ // 操作符跟在它要运算的操作数的后面,按优先级排列
解题思路
不包含括号的中缀转后缀 - 算法思路:比如:1 + 2 * 3
-
定义栈 st
-
遍历中缀表达式:
-
遇到操作数,直接输出(存储),作为后缀表达式的一部分。
-
遇到操作符 s,入栈前先判断下:
如果栈不为空,且 s 的优先级 低于或等于 栈顶操作符的优先级,则先将栈顶的操作符出栈(重复此步骤,继续与栈顶的操作符比较,直到栈为空,或者 s 的优先级 高于 栈顶的操作符,才停止比较)
然后再将当前操作符 s 入栈。
-
-
遍历结束后,依次弹出栈中剩余的所有操作符,得到后缀表达式。
代码如下:不包含括号的中缀转后缀
#include<iostream>
#include<string>
#include<stack>
using namespace std;
// 判断优先级(s1是否高于s2)
bool Priority(char s1, char s2)
{
if (s1 == '*' || s1 == '/')
if (s2 == '+' || s2 == '-') return true;
return false;
}
int main()
{
string str;
cin >> str; // 读入中缀表达式
stack<char> st; // 定义一个栈
string ret; // 存放后缀表达式
// 遍历中缀表达式
for (const auto& s : str) {
if (s == '+' || s == '-' || s == '*' || s == '/') // 操作符
{
// 操作符s在入栈前,先判断下
// 如果栈不为空,且s的优先级低于或等于栈顶操作符的优先级,则先将栈顶的操作符出栈
while (!st.empty() && !Priority(s, st.top())) {
ret.push_back(st.top()); // 栈顶操作符出栈(存储到ret中)
st.pop();
}
// 直到栈为空,或者s的优先级高于栈顶的操作符,才停止循环
st.push(s); // 然后将当前操作符s入栈
}
else // 操作数
{
ret.push_back(s); // 直接输出(存储到ret中)
}
}
// 遍历结束后,依次弹出栈中剩余的所有操作符
while (!st.empty())
{
ret.push_back(st.top());
st.pop();
}
// 输出后缀表达式
cout << ret << endl;
return 0;
}
更加复杂的中缀表达式转后缀表达式:
包含括号的中缀转后缀 - 算法思路:比如:1 + 2 * ( 3 - 4 ) + 5
-
定义栈 st
-
遍历中缀表达式:
-
遇到操作数,直接输出(存储),作为后缀表达式的一部分。
-
遇到操作符 s,入栈前先判断下:
如果栈不为空,且 s 的优先级 低于或等于 栈顶操作符的优先级,则先将栈顶的操作符出栈(重复此步骤,继续与栈顶的操作符比较,直到栈为空,或者 s 的优先级 高于 栈顶的操作符,才停止比较)
然后再将当前操作符 s 入栈。
-
遇到前括号 '(' 时,设置 flag = 1,表示括号中的所有操作符优先级被提高,遇到后括号 ')' 时,设置 flag = 0,说明括号结束了。
-
-
遍历结束后,依次弹出栈中剩余的所有操作符,得到后缀表达式。
后缀表达式的运算 - 算法思路:比如:1 2 3 * + 4 -
- 定义栈 st
- 遍历后缀表达式:
- 遇到操作数,入栈
- 遇到操作符,连续取出两个栈顶的数据出来,进行运算,再把运算结果入栈
- 遍历结束,输出 st 栈顶数据,就是最终运算结果。
后缀表达式的运算 - 画图演示:
OJ - 逆波兰表达式求值(⭐中等)
题目难度:中等
OJ链接:150. 逆波兰表达式求值 - 力扣(LeetCode)
根据逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意:两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
知识补充:逆波兰表达式
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
-
我们平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。这种表达式计算机不知道如何去计算。
-
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。这种表达式计算机可以去计算。
逆波兰表达式主要有以下两个优点:
-
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
-
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
解题思路
程序处理中缀表达式时,不太好运算,因为运算符的优先级问题
那如何解决呢,把中缀表达式转换成后缀表达式(逆波兰表达式),然后对后缀表达式进行运算。
运算思路上面已讲解。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st; // 定义一个栈
for (const auto& str : tokens) { // 遍历后缀表达式
if (str == "+" || str == "-" || str == "*" || str == "/") { // 操作符
int left = 0, right = 0; // 左右操作数
right = st.top(); // 连续取出两个栈顶的操作数
st.pop();
left = st.top();
st.pop();
if (str == "+") st.push(left + right); // 运算结果入栈
if (str == "-") st.push(left - right);
if (str == "*") st.push(left * right);
if (str == "/") st.push(left / right);
}
else { // 操作数
st.push(stoi(str)); // 入栈(注意:要转成数字)
}
}
return st.top(); // 栈顶元素即运算结果
}
};