

文章目录
摘要
题目给出一个嵌套的三元表达式(条件 ? expr1 : expr2),条件仅为字符 'T' 或 'F',表达式中的值为单字符(比如 '0'..'9'、字母或另一个三元表达式)。任务是解析该字符串,返回最终的值(仍为单字符)。
这道题看上去像是写一个小解析器,但用一个简单的栈 + 从右向左扫描技巧,就能优雅地解决所有嵌套情况,代码短、易理解。

描述
输入是一个合法的三元表达式字符串,例如:
txt
"T?2:3"
返回 "2"。
更复杂的嵌套例子:
txt
"T?2:F?3:4"
先从语义看:T ? 2 : (F ? 3 : 4),结果是 2。
或者
txt
"F?1:T?4:5"
这个等价于 F ? 1 : (T ? 4 : 5),结果是 4。
注意表达式里条件只会是 T 或 F,值都是单个字符(题目保证),因此我们可以把每个操作符 ? 和 : 看作定界符,解析时关键是如何正确匹配嵌套的 ? : 组合。
题解答案
常见且高效的思路是 从右向左扫描 字符串,并用栈保存"已解析的表达式值":
-
从右向左有个好处:当我们遇到
?时,栈顶正好保存着?对应的true分支表达式(最早被推入栈)和false分支表达式(次之); -
因为我们只关心每次三元表达式的结果(它本身是一个单字符表达式),我们可以把解析过的子表达式的结果也当作一个值压入栈;
-
具体操作:
-
遍历字符,从右往左;
-
遇到
:------ 跳过(不需要压栈); -
遇到普通字符(条件
T/F以外的字符) ------ 将其作为字符串压入栈; -
遇到
?------ 说明当前是形如cond ? expr1 : expr2的分割点:- 弹出栈顶作为
expr1(true 分支的值) - 再弹出作为
expr2(false 分支的值) - 读取
?左边的字符作为cond(注意我们从右向左扫描,cond就在当前位置i-1) - 根据
cond选择expr1或expr2,将选择的结果压回栈 - 跳过那个已经读取的
cond字符(i 减 1)
- 弹出栈顶作为
-
-
最后,栈顶即为最终结果(单字符字符串)
这个思路简洁、直观,能正确处理任意嵌套深度。

可运行 Swift 实现
下面是一个完整、可直接粘到 Playground 或 Swift 文件中运行的实现,并附带几个测试示例:
swift
import Foundation
class Solution {
func parseTernary(_ expression: String) -> String {
if expression.isEmpty { return "" }
let chars = Array(expression)
var stack: [String] = []
var i = chars.count - 1
while i >= 0 {
let ch = chars[i]
if ch == ":" {
// 冒号用于分隔,不入栈
} else if ch == "?" {
// 遇到 '?', 取出 trueExpr 和 falseExpr(栈中存的是已解析的表达式结果)
guard !stack.isEmpty else { return "" }
let trueExpr = stack.removeLast()
guard !stack.isEmpty else { return "" }
let falseExpr = stack.removeLast()
// 条件在 '?' 左侧一个字符
let condIndex = i - 1
guard condIndex >= 0 else { return "" }
let cond = chars[condIndex]
// 选取合适的分支并将结果入栈
let chosen = (cond == "T") ? trueExpr : falseExpr
stack.append(chosen)
// 跳过条件字符 (cond),因为已经处理
i -= 1
} else {
// 普通字符(数字、字母 or 'T'/'F' 作为 condition 需要在遇到 '?' 时才读取)
stack.append(String(ch))
}
i -= 1
}
// 解析完成后,栈顶就是最终结果(单字符)
return stack.last ?? ""
}
}
// Demo 测试
let solver = Solution()
let tests = [
"T?2:3",
"F?1:T?4:5",
"T?2:F?3:4",
"T?T?F:5:3", // 嵌套 condition/value 情况
"F?T?1:2:T?3:4" // 更多嵌套组合
]
for expr in tests {
print("\(expr) -> \(solver.parseTernary(expr))")
}
题解代码详解(逐行讲解)
-
把输入表达式
String转成Array<Character>,以便按索引访问。 -
使用
stack: [String]存放已经解析出来的子表达式结果(都作为单字符字符串存放)。 -
从字符串末尾开始向前扫描:
-
遇到
':':只是分隔符,忽略; -
遇到普通字符(数字或字母或
T/F),把它作为"一个值"压入栈; -
遇到
?:- 弹出两个值
trueExpr(栈顶)和falseExpr(次顶)。注意顺序:因为我们从右向左扫描,最先遇到并压入栈的是expr2(false),随后是expr1(true),所以弹出的第一个为trueExpr,第二个为falseExpr; - 读取
?左边的那个字符作为条件cond(因为我们是从右到左,此时i-1是条件字符); - 根据条件选择
trueExpr或falseExpr并把选择的字符串压入栈(作为该子表达式的新值); - 记得
i -= 1跳过条件字符;
- 弹出两个值
-
-
扫描完毕后,栈顶就是最终结果。
关键点总结:
- 从右向左扫描是核心技巧。它保证当你遇到
?时,对应的两个分支表达式已经被解析并保存在栈里; - 我们只保存"每个子表达式的最终值"而不是它的内部结构,这一点受益于题目保证表达式最终结果为单字符;
- 索引管理要小心:遇到
?时需要额外跳过左侧的条件字符(i -= 1)。
示例测试及结果
运行上面 Demo,你会看到可能类似的输出(具体结果按表达式而定):
txt
T?2:3 -> 2
F?1:T?4:5 -> 4
T?2:F?3:4 -> 2
T?T?F:5:3 -> 3
F?T?1:2:T?3:4 -> 3
来解读几个示例:
T?2:3:条件为T,结果选2;F?1:T?4:5:等价F ? 1 : (T ? 4 : 5),条件F,返回后半(T ? 4 : 5)的结果4;T?2:F?3:4:等价T ? 2 : (F ? 3 : 4),条件T,返回2;T?T?F:5:3:解析为T ? (T ? F : 5) : 3。内层(T ? F : 5)返回F,外层T ? F : 3返回F,使用我们的实现会以字符串 "F" 返回;但上面的 demo 弹出的可能为字符代表值,视测试表达式内容而定。
(注意:例子中混合使用字母和数字作为值都可,最终输出是对应的字符串)
时间复杂度
我们每个字符最多被访问一次,压入/弹出栈都是 O(1) 操作,因此整体时间复杂度为:
O(n),其中 n 是表达式长度。
空间复杂度
额外空间主要用于栈,最坏情况下(比如没有 ?/:,或者很多独立的值)需要存放 O(n) 个单字符字符串。
空间复杂度:O(n)。
总结
- 这道题虽然看起来像写解析器,但用从右向左扫描 + 栈的技巧可以写出简洁高效的解法;
- 关键点是:遇到
?时,栈上已经有true分支与false分支的结果,可以直接弹出并根据条件选择; - 算法时间复杂度线性,易于实现,适合在面试和工程场景中使用;
- 如果表达式里的值不再是单字符(比如多位数或者包含复杂子表达式),思路上仍然类似,只是 token 化和栈元素的管理要更复杂一些(需要处理多字符 token)。