在这里插入图片描述
在这里插入图片描述

摘要

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

描述

输入是一个合法的三元表达式字符串,例如:

"T?2:3"

返回 "2"

更复杂的嵌套例子:

"T?2:F?3:4"

先从语义看:T ? 2 : (F ? 3 : 4),结果是 2

或者

"F?1:T?4:5"

这个等价于 F ? 1 : (T ? 4 : 5),结果是 4

注意表达式里条件只会是 TF,值都是单个字符(题目保证),因此我们可以把每个操作符 ?: 看作定界符,解析时关键是如何正确匹配嵌套的 ? : 组合。

题解答案

常见且高效的思路是 从右向左扫描 字符串,并用栈保存“已解析的表达式值”:

  • 从右向左有个好处:当我们遇到 ? 时,栈顶正好保存着 ? 对应的 true 分支表达式(最早被推入栈)和 false 分支表达式(次之);

  • 因为我们只关心每次三元表达式的结果(它本身是一个单字符表达式),我们可以把解析过的子表达式的结果也当作一个值压入栈;

  • 具体操作:

    • 遍历字符,从右往左;

    • 遇到 : —— 跳过(不需要压栈);

    • 遇到普通字符(条件 T/F 以外的字符) —— 将其作为字符串压入栈;

    • 遇到 ? —— 说明当前是形如 cond ? expr1 : expr2 的分割点:

      • 弹出栈顶作为 expr1(true 分支的值)
      • 再弹出作为 expr2(false 分支的值)
      • 读取 ? 左边的字符作为 cond(注意我们从右向左扫描,cond 就在当前位置 i-1
      • 根据 cond 选择 expr1expr2,将选择的结果压回栈
      • 跳过那个已经读取的 cond 字符(i 减 1)
  • 最后,栈顶即为最终结果(单字符字符串)

这个思路简洁、直观,能正确处理任意嵌套深度。

可运行 Swift 实现

下面是一个完整、可直接粘到 Playground 或 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))")
}

题解代码详解(逐行讲解)

  1. 把输入表达式 String 转成 Array<Character>,以便按索引访问。

  2. 使用 stack: [String] 存放已经解析出来的子表达式结果(都作为单字符字符串存放)。

  3. 从字符串末尾开始向前扫描:

    • 遇到 ':':只是分隔符,忽略;

    • 遇到普通字符(数字或字母或 T/F),把它作为“一个值”压入栈;

    • 遇到 ?

      • 弹出两个值 trueExpr(栈顶)和 falseExpr(次顶)。注意顺序:因为我们从右向左扫描,最先遇到并压入栈的是 expr2(false),随后是 expr1(true),所以弹出的第一个为 trueExpr,第二个为 falseExpr
      • 读取 ? 左边的那个字符作为条件 cond(因为我们是从右到左,此时 i-1 是条件字符);
      • 根据条件选择 trueExprfalseExpr 并把选择的字符串压入栈(作为该子表达式的新值);
      • 记得 i -= 1 跳过条件字符;
  4. 扫描完毕后,栈顶就是最终结果。

关键点总结:

  • 从右向左扫描是核心技巧。它保证当你遇到 ? 时,对应的两个分支表达式已经被解析并保存在栈里;
  • 我们只保存“每个子表达式的最终值”而不是它的内部结构,这一点受益于题目保证表达式最终结果为单字符;
  • 索引管理要小心:遇到 ? 时需要额外跳过左侧的条件字符(i -= 1)。

示例测试及结果

运行上面 Demo,你会看到可能类似的输出(具体结果按表达式而定):

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)。
Logo

加入「COC·上海城市开发者社区」,成就更好的自己!

更多推荐