尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LeetCode-Go 题解:227. Basic Calculator II 单栈一次遍历实现四则混合运算求值

LeetCode-Go 题解:227. Basic Calculator II 单栈一次遍历实现四则混合运算求值 LeetCode-Go 题解227. Basic Calculator II 单栈一次遍历实现四则混合运算求值【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go本文基于 LeetCode-Go 仓库中 leetcode/0227.Basic-Calculator-II/README.md 展开深入剖析「Basic Calculator II」这道经典表达式的求值题在 Go 中的栈实现利用单个栈与前置运算符preSign思想先处理乘除、再合并加减一次遍历即可完成含空格的四则混合运算求值。读完本文你将掌握这套可复用的单栈求值模板并理解其与第 224 题含括号版计算器之间的递进关系。题目概述与约束给定一个字符串表达式s计算并返回它的值其中整数除法向零截断只保留整数部分。Example 1s 32*2→ 输出7Example 2s 3/2 → 输出1Example 3s 35 / 2 → 输出5约束条件来自原题文档1 s.length 3 * 10^5s由整数和运算符(, -, *, /)组成运算符之间以任意数量的空格分隔s表示一个合法表达式表达式中所有整数均为[0, 2^31 - 1]范围内的非负整数答案保证能够放入一个 32 位整数值得注意的两个细节输入表达式中不存在负数常量负数通过-运算符作用于后续操作数产生整数除法仅保留整数部分例如3/2 1这与多数编程语言中整数除法向零截断的语义一致意味着-3/2这类除法同样向零截断。核心解题思路先乘除、后加减原文档给出了非常清晰的思路主线这道题是第 224 题的加强版。第 224 题中只有加减运算和括号这一题增加了乘除运算。由于乘除运算的优先级高于加减所以先计算乘除运算将算出来的结果再替换回原来的算式中。最后只剩下加减运算于是题目降级成了第 224 题。具体算法如下把加减运算符号后面的数字压入栈中遇到乘除运算直接将它与栈顶的元素计算并将计算后的结果放回栈顶若读到一个运算符或者遍历到字符串末尾即认为是遍历到了数字末尾处理完该数字后更新preSign为当前遍历的字符遍历完字符串s后将栈中元素累加即为该字符串表达式的值。这种做法的巧妙之处在于用「延迟结算」消除优先级问题。遇到/-时并不立即计算而是把带符号的数字压栈减法压入负数遇到*//时立即与栈顶元素结算因为乘除的优先级最高此时结算不会影响后续结果。最终栈中只剩下一串正负整数求和即可。时间复杂度 O(n)空间复杂度 O(n)其中 n 为字符串长度栈中最多存储 O(n) 个操作数。Go 源码逐行解析仓库中的完整实现位于 leetcode/0227.Basic-Calculator-II/227. Basic Calculator II.go核心函数如下func calculate(s string) int { stack, preSign, num, res : []int{}, , 0, 0 for i, ch : range s { isDigit : 0 ch ch 9 if isDigit { num num*10 int(ch-0) } if !isDigit ch ! || i len(s)-1 { switch preSign { case : stack append(stack, num) case -: stack append(stack, -num) case *: stack[len(stack)-1] * num default: stack[len(stack)-1] / num } preSign ch num 0 } } for _, v : range stack { res v } return res }逐行拆解其状态机逻辑状态变量初始化stack[]int{}动态切片栈用于暂存待累加的带符号操作数preSign记录「当前数字之前的运算符」初始化为。这个初始值非常关键——表达式第一个数字前没有显式符号但按加法压栈恰好等价于「首项为正」省去了首元素特判num正在拼接的当前数字连续数字字符按十进制累乘res最终结果累加器。数字拼接阶段if isDigit { num num*10 int(ch-0) }当字符为数字时把num左移一位乘 10再叠加当前位完成多位整数的解析例如52会被解析为5*10 2 52。结算触发条件if !isDigit ch ! || i len(s)-1 {这是一个值得注意的复合条件等价于(!isDigit ch ! ) || (i len(s)-1)两个条件触发结算遇到运算符非数字且非空格——说明当前数字已完整读取遍历到字符串末尾——最后一个数字后面没有运算符必须强制结算。空格既不参与数字拼接也不触发结算只被跳过。range遍历得到的是i字节索引与chrune由于题目保证输入只含 ASCII 字符数字、运算符、空格字节索引即字符索引i len(s)-1的判断是正确的。按 preSign 分派结算switch preSign { case : stack append(stack, num) case -: stack append(stack, -num) case *: stack[len(stack)-1] * num default: stack[len(stack)-1] / num }把num原样压栈-把-num压栈转化为负数后续统一求和*stack栈顶元素原地乘numdefault即/栈顶元素原地除以numGo 整数除法即向零截断天然满足题目要求。注意preSign是数字之前的运算符例如表达式32*2读到时触发结算此时preSign仍是初始值把3压栈随后更新preSign 读到*时触发结算把2压栈更新preSign *读到末尾2时按*执行stack[len(stack)-1] * 2栈顶由2变为4。最终栈为[3, 4]求和得7。结算后的状态推进preSign ch num 0更新前置运算符为当前字符并清零num开始解析下一个数字。结果汇总for _, v : range stack { res v } return res最后把栈中所有元素累加。由于栈中只保存带符号的操作数乘除已在入栈/栈顶结算阶段完成这里的累加即是最终答案。与 224 题 Basic Calculator 的对比与降级关系原文档明确指出本题是 第 224 题 Basic Calculator 的加强版224 题只有加减与括号227 题加入了乘除但去掉了括号。因此 227 题的解法先把乘除结算掉把表达式「降级」为纯加减问题而这正是 224 题已经解决的问题形态。对比仓库中 224 题的实现 leetcode/0224.Basic-Calculator/224. Basic Calculator.go 可以发现两者的差异与联系224 题calculate(s string)使用container/list作为栈处理、-、(、)四种符号。遇到(时把「当前结果 result」与「符号状态 sign」压栈进入括号内的新计算域遇到)时按result * sign 之前结果弹出恢复。它不需要 preSign 机制因为加减可以直接累加进result。227 题没有括号但引入乘除后不能再即时累加必须用preSign延迟结算把加减数字入栈、乘除数字与栈顶合并。两题的共同点是都基于「栈」这一数据结构做运算符优先级管理。可以说掌握了 224 的括号处理与 227 的乘除 preSign 机制就覆盖了 LeetCode「基本计算器」系列224、227、772 等中最核心的两类优先级处理手段。测试用例验证仓库为该题编写了完整的测试位于 leetcode/0227.Basic-Calculator-II/227. Basic Calculator II_test.go测试结构遵循仓库统一的question227/para227/ans227表驱动风格输入期望输出覆盖点32*27乘号优先级高于加号3/21整数除法向零截断 35 / 2 5运算符两侧带空格1 12空格与加法 2-1 2 3减法与混合空格2-5/62减法与除法混合2 - 0 2其中2-5/6是很有代表性的边界用例5/6向零截断为0因此表达式等价于2-0 2验证了整数除法截断与减法压栈-5先入栈再被除法原地结算为-0的正确性。运行测试的方式与仓库其他题目一致。仓库根目录的 gotest.sh 展示了全量测试命令go test -covermodeatomic -coverprofilecoverage.txt ./leetcode/...若只需运行本题测试可进入对应目录执行cd leetcode/0227.Basic-Calculator-II go test -v边界情况与易错点总结整数除法向零截断Go 的/运算符对整数本身就向零截断与题目语义一致无需额外处理。但若自行实现时使用math.Floor等浮点手段会得到-1而非0对-5/6而言必须避免减法压入负数-后面紧跟的数字以负数形式入栈保证了最终求和逻辑的统一也使得2-12这类表达式天然正确末尾数字强制结算i len(s)-1分支不可或缺否则最后一个数字永远不会进入栈中preSign 初始化必须为否则表达式首项无法入栈空格处理空格既不影响数字拼接也不触发结算仅被ch ! 条件过滤支持任意数量空格大输入规模s.length可达3 * 10^5单次线性遍历 栈操作均为 O(1) 均摊可以轻松应对该规模答案保证在 32 位整数范围内Go 的int在 64 位平台上为 64 位不会溢出。小结LeetCode-Go 仓库对 227 题给出的解法是一个极简而优雅的单栈模板一个栈、一个前置运算符、一次遍历。它把「优先级」问题转化为「结算时机」问题——低优先级的加减延迟入栈高优先级的乘除立即结算最终栈内只余待求和的带符号整数。这套思路不仅适用于本题也是处理无括号四则表达式求值的通用范式与仓库中 224 题的括号栈解法形成互补共同构成「Basic Calculator」系列的两块基石。参考实现与测试解题源码单元测试原题文档224 题括号版解法仓库通用数据结构含 Stack【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表