二叉树求LCA本质是后序遍历中通过返回值传递子树信息:遇p/q即返,左右非空则当前为LCA,单侧非空则透传,全空则返null;需注意终止条件含root==p/q、判空顺序及null语义统一。

二叉树中找两个节点的最近公共祖先(LCA),本质是利用路径信息定位交汇点。变量路径回溯不是额外步骤,而是递归过程中自然发生的“状态传递”——每次函数返回,就是在把子树中是否含目标节点的信息沿路径往上传,这个过程就是隐式回溯。
为什么用递归回溯法最实用
它不依赖父指针、不预存路径、不重复遍历,仅靠一次后序遍历就能完成判断。核心在于:每个节点只被访问一次,返回值承载语义——null 表示子树不含 p 或 q;非 null 表示找到了 p、q 中的一个,或已确定为 LCA。
- 遇到 p 或 q 就立即返回,避免无谓深入
- 左右子树都返回非空,说明 p 和 q 分居两侧,当前节点就是 LCA
- 只有一侧返回非空,说明 LCA 在那一侧,直接向上透传结果
- 全为空,说明当前子树不包含任一目标节点
关键细节决定是否真正理解
很多人写对代码却讲不清逻辑,常卡在三个地方:
- 终止条件必须包含 root == p 或 root == q:这是“自底向上”的起点,也是允许节点作为自身祖先的依据
- 左右结果判空顺序不能颠倒:先检查 left != null && right != null,再单独判 left 或 right,否则会漏掉跨子树情况
- 返回 null 的含义要统一:它只表示“本子树未发现有效信息”,不是错误,而是参与上层决策的合法信号
前端场景中怎么快速落地
DOM 树里找两个元素的最近公共祖先,可直接套用该逻辑(把 TreeNode 换成 Element,left/right 换成 firstChild/nextSibling 不适用,但 parent 属性可用);更常见的是用浏览器原生方法 Element.closest() 或 Node.contains() 辅助模拟。例如:
- 先获取 A 的所有祖先节点(从 A.parentNode 循环向上)
- 对每个祖先,用 B.contains(ancestor) 判断 B 是否在其子树中
- 第一个满足条件的祖先即为 LCA
这虽非最优时间复杂度,但代码简洁、可读性强,适合多数业务场景。
别忽略边界与约束
算法成立有前提:p 和 q 一定存在且互异,树中节点值唯一。实际工程中需前置校验:
- 检查 p 和 q 是否为有效节点,是否属于同一棵树(可通过比较根节点或 ownerDocument)
- 若不确定是否存在,可在递归中加计数器,最后验证是否真找到了两个目标
- 若树是 BST,可改用迭代法,利用左小右大性质跳过一半分支,效率更高










