SWE · Second Phone Coding

Snowflake 二叉树两节点路径:用 U/L/R 表示上、左、右

Phone Coding 题目给两节点路径需用上左右表示;本文从根建父索引、找共同祖先、拼接向上和向下路径。

题目重点:Phone Coding 题目给两节点路径需用上左右表示;本文从根建父索引、找共同祖先、拼接向上和向下路径。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练版输入一棵以 root 为根的二叉树、唯一整数节点值 start/destination;U 到父、L 到左孩子、R 到右孩子。
  • 路径可以经过最近公共祖先;start=destination 返回空串。缺失节点或重复值报错。题目未明确这些接口与错误行为。

解题路线

  1. 遍历树一次,建 value→(parentValue,从父到该节点的方向);同时检查节点值唯一。
  2. 从 start 向根走,记录其所有祖先;从 destination 向上直到碰到 start 的祖先,沿途收集父→子的 L/R 标签,这个交点就是 LCA。
  3. 从 start 向上走到 LCA,每条边输出 U;再反转 destination→LCA 途中收集的 L/R 标签,接在 U 后。
  4. 这避免错误地从根拼接两条完整路径;用独立的无向边带方向 BFS 对 10,000 棵随机树验证。

手动走一遍

树 1 的左孩子 2、右孩子 3;2 的右孩子 5,3 的左孩子 6。从 5 到 6:先 5→2→1 输出 UU,再 1→3→6 输出 RL,答案 UURL。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

"""Training contract: directions U/L/R between distinct nodes in a binary tree."""

from dataclasses import dataclass


@dataclass
class Node:
    value: int
    left: "Node | None" = None
    right: "Node | None" = None


def directions(root: Node | None, start: int, destination: int) -> str:
    if root is None:
        raise ValueError("empty tree")
    parents = {root.value: (None, "")}
    stack = [root]
    while stack:
        node = stack.pop()
        for child, edge in ((node.left, "L"), (node.right, "R")):
            if child is not None:
                if child.value in parents:
                    raise ValueError("duplicate node value")
                parents[child.value] = (node.value, edge)
                stack.append(child)
    if start not in parents or destination not in parents:
        raise ValueError("node not found")

    ancestors = set()
    node = start
    while node is not None:
        ancestors.add(node)
        node = parents[node][0]

    downward = []
    node = destination
    while node not in ancestors:
        parent, edge = parents[node]
        downward.append(edge)
        node = parent
    lca = node

    upward = []
    node = start
    while node != lca:
        upward.append("U")
        node = parents[node][0]
    return "".join(upward + downward[::-1])

复杂度

N 节点、树高 h:建索引 O(N) 时间/空间;两条向上链和输出 O(h) 时间/空间;整体 O(N) 时间、O(N) 空间。若需大量路径查询,可预处理 LCA 和深度,按查询量选二进制提升。

易错点与追问

容易失分的边界

  • 把从 root 到 start、destination 的完整路径直接串起来,没去掉共同前缀。
  • 向 destination 逆爬时收集的 L/R 没反转。
  • 用节点 value 作索引却允许重复值。
  • start 等于 destination 时误输出 U。
  • 把题目缺约束的删除节点 rollover 高度题混写进本解法。

继续深挖

  • 百万次两点路径查询,LCA 预处理怎样降低单次成本?
  • 树动态插入/删除后,父索引和 LCA 结构怎样更新?
  • 若节点没有唯一值,如何按对象 ID 表示输入?
  • 若要最短路径长度而非方向字符串,可只计算深度与 LCA 吗?
✓已复制微信号!