表达式压栈判定本质是用多个栈协同模拟计算过程,核心为运算符栈与操作数栈分工协作,配合token流式输入实现在线分析;需支持数字合成、括号匹配及一元/二元运算符区分。

表达式压栈判定(比如判断括号是否匹配、运算符优先级处理、中缀转后缀等)本质上是用栈模拟计算过程,而“并发栈”并不是标准术语——这里实际指**多个栈协同工作**(如操作数栈 + 运算符栈),配合**输入流逐字符/逐token读取**,实现边读入边分析的在线判定。关键不在“并发”,而在“分工协作”与“流式响应”。
明确两个核心栈的职责
在典型表达式处理(如中缀表达式求值或括号匹配增强版)中,建议固定使用两个栈:
-
运算符栈(opStack):只存运算符和左括号
(,用于暂存待定优先级关系的符号;遇到右括号或高优先级运算符时触发弹栈计算或匹配。 - 操作数栈(valStack):存数字、变量或子表达式结果;每当运算符栈弹出一个运算符,就从该栈弹出对应数量操作数(如二元运算弹2个),执行运算后将结果压回。
输入流要支持“可回溯”或“预读一位”
纯顺序读取(如 System.in.read())容易卡在边界判断(例如读到 12+3 中的 1 就停,无法识别完整数字)。实用做法是:
- 用
BufferedReader配合StringTokenizer或正则分词,把输入拆成 token 流(如"(", "12", "+", "3", ")"); - 若坚持字符流,维护一个
peekChar变量:每次read()前先检查它,处理完再更新;遇到数字开头时,循环读取直到非数字,合成完整数值; - 特别注意空格、负号(
-5中的-是一元运算符,需结合上下文判断,不能简单归为二元减号)。
压栈判定的核心逻辑模板
以中缀转后缀(Shunting Yard)为例,每读一个 token,按规则决策:
- 遇到数字 → 直接输出(或压入结果队列);
- 遇到左括号
(→ 压入opStack; - 遇到右括号
)→ 持续弹出opStack并输出,直到遇到((该左括号丢弃,不输出); - 遇到运算符(如
+,*)→ 比较其与opStack栈顶的优先级:
• 若栈为空 / 栈顶是(/ 当前运算符优先级更高 → 压栈;
• 否则弹出栈顶并输出,再重新比较新栈顶,直到满足压栈条件。
整个过程无需等待全部输入,输入流结束且 opStack 清空即完成判定(若未清空,说明括号不匹配或缺少运算符)。
一个小而完整的括号+四则运算判定示例(Java片段)
判断字符串是否为合法中缀表达式(仅含 0-9, +-*/(), 空格),并隐含计算可行性:
<font size="2">String expr = "(1+2)*3";<br>Deque<character> opStack = new ArrayDeque();<br>boolean expectOperand = true; // 下一个应是操作数(防 "++"、")+" 等非法)<br><br>for (int i = 0; i char c = expr.charAt(i);<br> if (Character.isWhitespace(c)) continue;<br> if (Character.isDigit(c)) {<br> while (i i--; // 补回,for 循环会自增<br> expectOperand = false;<br> } else if (c == '(') {<br> opStack.push(c);<br> expectOperand = true;<br> } else if (c == ')') {<br> if (opStack.isEmpty() || opStack.peek() != '(') return false;<br> opStack.pop();<br> expectOperand = false;<br> } else if ("+-*/".indexOf(c) >= 0) {<br> if (expectOperand) { // 允许一元负号,但此处简化判为非法<br> return false;<br> }<br> expectOperand = true;<br> }<br>}<br>return opStack.isEmpty() && !expectOperand;</character></font>
这段代码用一个布尔标志替代了第二个栈,体现“轻量分工”思想:栈管结构嵌套,状态变量管语法位置约束。











