波兰式:将四则运算的表达式解析成一棵二叉树,后续遍历之,得到的字符串序列就是逆波兰式,也叫后缀表达式。
一开始考虑时,首先想到建立两个栈,一个栈是数字栈,一个栈是操作符栈,但是实现了一下发现不可行,重新分析发现理论上也是错误的。
考虑到后缀表达式的性质,可以很容易的确定根节点和右子树根节点(如果有的话)。四则运算表达式的二叉树的每个非叶子结点一定有两个孩子。
于是考虑使用递归,即:
1.找到根节点;
2.如果根节点数字,则返回其值;
3.如果根节点是算符op,则
计算右子树值;
计算左子树值;
返回:左子树值op右子树值
但是在上面的3中,左子树的根节点位置是无法再访问其根节点时确定的,解决办法就是在递归过程中,不断"摘掉"每个访问过的点,这样一来当右子树计算结束时,右子树的所有节点都被“摘下”,于是此时tokens中最顶端的结点一定就是左子树的根节点。这大概也就是为什么题目中的方法默认采用非const引用参数的原因吧。
class Solution {
//实现字符串op的四则运算算符效果
int operation(int n1, int n2, const string& op) {
switch ((op.c_str())[0]) {
case '+': return n1 + n2;
case '-': return n1 - n2;
case '*': return n1 * n2;
case '/': return n1 / n2;
}
}
public:
//非常精巧的递归,基本思想是利用后续遍历计算树的性质
int evalRPN(vector<string>& tokens) {
//每一次递归视作是从根节点开始计算一个树形算式,由于tokens中是后序序列,因此根节点位于最末端
string str = tokens.back(); //读取根节点值
//摘下根节点,这是由于递归时,只能预测有字数根节点的位置,无法预测左子树根节点位置,
//因此必须不断摘掉树的节点,直到该字数计算完毕为止
vec.pop_back();
//如果根节点是算符,则先计算左子树值,再计算右子树值,最后用根节点算符对两个数运算,返回
//如果根节点是数字,则说明到达叶子节点,直接看做常量表达式,返回
if (str == "+" || str=="-" || str=="*" || str=="/") {
int r_num = evalRPN(tokens);
int l_num = evalRPN(tokens);
return operation(l_num, r_num, str);
}
else return atoi(str.c_str());
}
};