RPN求值无需构建语法树,std::stack是唯一合理选择,因其天然契合LIFO特性;需用std::stoll防溢出,除法须手动实现向零取整,栈最大尺寸减1即为实际深度。

逆波兰表达式(RPN)求值本身不需要构建语法树,强行建树不仅冗余,还会掩盖运算优先级天然已由后缀顺序保证的本质;所谓“语法树深度分析”在 RPN 场景下是误用概念——RPN 本就是语法树的后序遍历结果,还原树只为教学演示,无实际计算价值。
为什么 std::stack 是 RPN 求值唯一合理的选择
RPN 的核心规则是:遇到数字压栈,遇到运算符弹出两个操作数(注意顺序:先弹的是右操作数),计算后压回结果。这个过程天然契合 LIFO 行为。
-
std::stack提供push、top、pop的 O(1) 操作,且语义清晰,无需手动管理索引 - 用
std::vector模拟栈容易写错弹出顺序(比如误用back()后忘了pop_back()),或在空栈时访问back()导致未定义行为 - 用递归模拟栈会因表达式过长触发栈溢出,且完全违背 RPN 的线性扫描本质
示例关键片段:
std::stack<long long> stk;
for (const auto& token : tokens) {
if (token == "+" || token == "-" || token == "*" || token == "/") {
long long b = stk.top(); stk.pop();
long long a = stk.top(); stk.pop();
stk.push(calc(a, b, token)); // 注意 a 是左操作数,b 是右操作数
} else {
stk.push(std::stoll(token));
}
}</long>
std::stoll 和 std::stoi 在 RPN 中必须谨慎选型
输入 token 可能是 "-2147483648" 这类超出 int 范围的字符串,而 RPN 计算中间结果也可能溢出 int(如 "1000000000 1000000000 +")。
-
std::stoi对超范围输入抛std::out_of_range异常,但 RPN 输入通常保证合法,异常处理反而增加分支开销 -
std::stoll返回long long,覆盖全部 32 位整数运算的中间结果,且标准保证其对合法数字字符串不会抛异常 - 若题目明确限定输入在
int范围内,仍建议用long long存储,避免乘法/加法溢出未被检测
除法截断方向必须显式处理为向零取整
C++ 的 / 对负数执行的是向负无穷取整(即 -3 / 2 == -2),但 RPN 题目(如 LeetCode 150)要求的是向零取整(即 -3 / 2 == -1)。
- 不能直接写
a / b,必须判断符号后用绝对值计算再加符号,或用trunc(static_cast(但浮点有精度风险)(a) / b) - 更安全的做法是:
(a - 注意
abs(INT_MIN)溢出,所以必须在long long上做abs
所谓“语法树深度”只是对 RPN 序列的误导性重构
RPN 字符串本身不携带括号或优先级信息,但可通过栈操作隐式还原二叉树结构:每次运算符弹出的两个操作数,就是它的左右子节点。若真要统计“深度”,只能在建树过程中递归计算,但该深度与求值无关,且不同建树方式(如是否合并单子节点)会导致结果不一致。
- 强行构建
TreeNode*树会显著增加内存分配和指针跳转开销,性能下降 3–5 倍 - 深度值取决于你如何定义“节点”——操作数算不算叶节点?一元负号是否引入新节点?题目从未定义,因此无标准答案
- 如果真需要分析结构,应直接对 token 序列做静态扫描:深度 ≈ 最大嵌套的“等待操作数数量”,即遍历中栈尺寸的最大值减 1
真正关键的不是树有多深,而是确保每次 pop 前栈里至少有两个元素——这一个检查比任何树遍历都重要,也最容易被忽略。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











