std::stack 是唯一合理的选择,因其天然匹配rpn“见数压栈、见符弹两算一”的lifo语义;std::istringstream分词最可靠,可完整保留带符号数字并跳过任意空白;运算符仅识别四字面量,数字一律用std::stoll转long long防溢出。

逆波兰表达式求值不需要语法树,std::stack 就够了;硬塞 TreeNode 构建逻辑进求值函数,八成会崩在空指针、栈越界或内存泄漏上。
为什么 std::stack 是唯一合理的选择
RPN 的语义就是“见数压栈、见符弹两算一”,它天然匹配 std::stack 的 LIFO 行为。用 std::vector 模拟栈看似灵活,但容易漏掉 pop_back(),导致后续 back() 访问越界;用递归或手写链表栈则纯属加戏——既没收益,又增风险。
-
std::stack::top()和pop()时间复杂度稳定为 O(1),无额外开销 - 所有中间结果用
long long存储,避免std::stoi溢出(如"1000000000 1000000000 +") - 除法必须手动实现向零截断:
(a ,不能直接写 <code>a / b - 每次调用
top()前必须检查stk.size() >= 2,否则未定义行为
std::istringstream 分词比手写 split 可靠得多
逐字符扫描或按空格 find 切分,会把 "-123" 拆成 "-" 和 "123",结果运算符栈里多出一个非法 "-",后续所有计算全错。而 std::istringstream 自动跳过任意空白(空格、制表、换行),且完整保留带符号数字。
- 写法固定:
std::istringstream iss(expr); std::string token; while (iss >> token) { ... } - 只对
"+"、"-"、"*"、"/"四个字面量做运算符判定,其余一律走std::stoll(token) - 别用
std::stoi:遇到"-2147483648"这类边界值可能抛异常;std::stoll对合法输入不抛异常,更稳 - 若需校验 token 是否纯数字,可配合
token.find_first_not_of("0123456789+-"),但仅限强校验场景
真要建语法树,必须另起函数,不能和求值混在一起
如果题目明确要求返回 TreeNode*,那就拆成两个函数:std::vector<:string> tokenize(const std::string&)</:string> 和 TreeNode* buildTreeFromRPN(const std::vector<:string>&)</:string>。混在一个函数里,栈操作和指针管理必然打架。
- 建树栈里存的是
TreeNode*或std::shared_ptr<treenode></treenode>,不是数值 - 遇到数字 token:新建叶节点,
left = right = nullptr,压入节点栈 - 遇到运算符 token:先弹右子节点,再弹左子节点(RPN 结构决定的顺序),新节点的
left指向后者,right指向前者 - 建树过程不涉及任何计算,也不该检查除零;那是求值阶段的事
- 用
std::vector替代std::stack可方便调试打印中间节点,但生产环境仍推荐std::stack
最常被忽略的点是:分词正确性决定了整个流程的生死。哪怕建树逻辑完全正确,只要 "-123" 被拆开,后续所有节点的 val 就全错了——这不是算法问题,是输入解析没兜住。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











