中缀表达式转后缀表达式(了解思路)
前面介绍了后缀表达式如何转中缀表达式,那么思考一下中缀表达式如何转后缀表达式
以下面的两个中缀表达式为例
- 不带括号的中缀表达式
2 + 3 * 4 - 5 / 6
- 带括号的中缀表达式
2 + 3 * (3 + 4 - 5) / 6
转后缀表达式基本思路与后缀表达式大体思路相反:
- 当遇到操作符时,操作符进栈
- 当遇到操作数时取操作符进行运算
但是需要考虑到下面的问题:
- 运算符优先级,什么时候该取操作符进行计算(不是单单遇到操作数就取操作符进行计算)
- 遇到括号需要提升优先级时应该如何处理
对于上面的两个问题提出两种解决方案:
- 处理运算符优先级时遵循两个原则
- 栈为空时,直接进操作符
- 栈不为空时,如果即将进入的操作符比当前栈内的操作符优先级高,那么就让该操作符进栈,而不是取操作符进行计算;如果即将进入的操作符比当前栈内的操作符优先级低或者相等,那么就可以让栈内当前操作符出栈进行计算,再让遇到的操作符进栈
- 遇到括号需要提升优先级可以采用递归子问题的方式解决,第一个原因是因为再递归中可以重现开辟一个栈,这个栈只需要存储当前括号内的操作符即可,第二个原因是因为括号内的表达式可以看作一个新的计算式,这个新的计算式也包括对应的运算符优先级,所以本质还是走第一个方案
具体分析如下图所示
📌
进行分析前的前置知识:
一个操作符的优先级不是由该操作符本身决定的,而应该是由相邻的操作符彼此直接优先级等级决定的。例如对于等式2+3*4
,当计算时根据从左往右计算的规则,第一个看到的操作符是+
,但是此时并不能进行计算,需要确定后面紧接的操作符是否比当前的+
优先级高,很明显,*
的优先级会高于+
号,但是现在也不可以进行计算,因为*
的操作符优先级的确比+
高,但是并不确定紧挨着*
的操作符的优先级是否比*
高,接下来看*
后面的操作符优先级,但是由于此时走到了算式结尾,所以现在可以确定*
可以开始计算,当*
计算完毕后则开始计算+
- 考虑不带括号的计算式
基本思路实现代码:
class solution
{
public:
//获取当前运算符优先级
int getPriority(string& s1, string& top)
{
if ((s1 == "*" || s1 == "/") && (top == "+" || top == "-"))
{
return 1;
}
return 0;
}
//不带括号的中缀表达式转后缀表达式_直接打印后序遍历
void MiddleExpreToRpnNonbraces(vector<string>& v)
{
stack<string> s;
for (auto& str : v)
{
//操作符处理
if ((str == "/" || str == "*" || str == "-" || str == "+") && (s.empty() || getPriority(str, s.top())))
{
s.push(str);
}
else
{
//可能存在当前操作符优先级小于栈顶操作符,出一次操作符后的栈顶操作符可能和当前操作符优先级相同,判断不止一次不能用if
while (!s.empty() && (str == "/" || str == "*" || str == "-" || str == "+") && !getPriority(str, s.top()))
{
cout << s.top() << " ";
s.pop();
}
//如果出现优先级相同的时候可能栈内数据全部出完,此时需要重新进一次数据
if((str == "/" || str == "*" || str == "-" || str == "+") && s.empty())
{
s.push(str);
}
if (!(str == "/" || str == "*" || str == "-" || str == "+"))
{
cout << str << " ";
}
}
}
//栈内此时肯定还有操作符,所以需要依次弹出
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
}
};
📌
上面的思路为基本的思路,但是存在一种优化思路,因为不考虑括号时,一共就4个操作符,而这四个操作符只存在*
和/
比+
和-
优先级高,所以当前元素为*
或/
时即可取出进行计算
优化后的代码:
class solution
{
public:
//获取当前运算符优先级
int getPriority(string& s1)
{
if (s1 == "*" || s1 == "/")
{
return 1;
}
return 0;
}
//不带括号的中缀表达式转后缀表达式_直接打印后序遍历
void MiddleExpreToRpnNonbraces(vector<string>& v)
{
stack<string> s;
for (auto& str : v)
{
//操作符处理
if ((str == "/" || str == "*" || str == "-" || str == "+") && (s.empty() || getPriority(str)))
{
s.push(str);
}
else
{
//可能存在当前操作符优先级小于栈顶操作符,出一次操作符后的栈顶操作符可能和当前操作符优先级相同,判断不止一次不能用if
while (!s.empty() && (str == "/" || str == "*" || str == "-" || str == "+") && !getPriority(str))
{
cout << s.top() << " ";
s.pop();
}
//如果出现优先级相同的时候可能栈内数据全部出完,此时需要重新进一次数据
if((str == "/" || str == "*" || str == "-" || str == "+") && s.empty())
{
s.push(str);
}
if (!(str == "/" || str == "*" || str == "-" || str == "+"))
{
cout << str << " ";
}
}
}
//栈内此时肯定还有操作符,所以需要依次弹出
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
}
};
- 考虑带括号的计算式
class solution
{
public:
//获取当前运算符优先级
int getPriority(const string& s1, const string& top)
{
if (((s1 == "(" && (top == "*" || top == "/") && (top != "+" || top != "-")))
|| ((s1 == "(" && (top != "*" || top != "/") && (top == "+" || top == "-")))
|| (s1 != "(" && (s1 == "*" || s1 == "/") && (top == "+" || top == "-")))
{
return 1;
}
return 0;
}
//带括号的中缀表达式转后缀表达式_直接打印后序遍历
void MiddleExpreToRpn(vector<string>& v, size_t& i)
{
stack<string> s;
for (; i < v.size(); i++)
{
//处理操作符
if ((v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && (s.empty() || getPriority(v[i], s.top())))
{
s.push(v[i]);
}
//当遇到左括号时
else if (v[i] == "(")//左括号进入递归操作符栈
{
i++;
//需要在递归中同时改变i的值
MiddleExpreToRpn(v, i);
}
//当遇到右括号时结束递归
else if (v[i] == ")")
{
//结束递归前需要对栈内操作符进行处理
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
return;
}
else
{
//可能存在当前操作符优先级小于栈顶操作符,出一次操作符后的栈顶操作符可能和当前操作符优先级相同,判断不止一次不能用if
while (!s.empty() && (v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && !getPriority(v[i], s.top()))
{
cout << s.top() << " ";
s.pop();
}
//如果出现优先级相同的时候可能栈内数据全部出完,此时需要重新进一次数据
if ((v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && s.empty())
{
s.push(v[i]);
}
//可能存在一个操作符优先级高于当前栈顶的操作符
if ((v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && (s.empty() || getPriority(v[i], s.top())))
{
s.push(v[i]);
}
if (!(v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+"))
{
cout << v[i] << " ";
}
}
}
//栈内此时肯定还有操作符,所以需要依次弹出
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
}
};