最近公共祖先:左右都带回命中,自己就是
普通二叉树不能按值走路。子树找到 p 或 q 就往上交;左右两边各交来一个,当前节点就是最近的分叉。
递归返回「子树里是否找到 p 或 q」:后序自底向上。若当前节点是 p 或 q,或左右子树各带回一个命中,则当前节点就是 LCA。每棵树只被访问一次。时间 O(n),空间 O(h)。
给定普通二叉树的根,以及树上两个节点 p、q,返回它们的最近公共祖先。祖先可以是自己:p 是 q 的祖宗时,答案就是 p。
主例树 [3, 5, 1, 6, 2, 0, 8, null, null, 7, 4],p=5,q=4。4 挂在 5 下面的 2 的右侧。5 既是 4 的祖先,又是两人里最深的那个公共点,答案是 5。演示先在 4 命中 q,再标出 5 的子树同时握有 p 和 q。
LC235 可以比较 BST 的值决定往哪边走。这里没有序。本文要回答:为什么后序「左右都非空就返回自己」能抓住最深的分叉,以及主例里 p 本身就是祖先时,为什么不必再从 5 往下把 4 找完。
缺口是「这一侧有没有人」,不是路径集合
第一直觉分别找出根到 p、根到 q 的两条路径,从根往下对,最后一个相同节点就是 LCA。主例路径 3-5 和 3-5-2-4,对完是 5,结果对。但每条路径都要存,还要再对一次。缺的是一个可以从下往上交的信号:这棵子树里有没有碰到 p 或 q。
后序先问左、再问右、再看自己。空节点交 nil。当前节点就是 p 或 q,直接把这个节点交上去——它至少代表「这里命中了一个」。左右返回值都非空,说明 p、q 分居两侧,当前节点是它们分叉的地方,也就是 LCA,把当前节点交上去。只有一侧非空,把那一侧的节点继续往上交:可能是其中一个目标,也可能已经是更深的 LCA。
「最近」来自后序。更深的分叉先算完。父节点看到左右都非空时,左右交来的已经是「各自子树里的命中或更低的 LCA」,自己是更高一层的分叉,不会盖过更低的那个。所以第一个「左右都有人」的节点就是最深公共祖先。
p 是 q 的祖先时,代码在 p 处直接返回 p,不会再搜 p 的子树。这不是漏掉 q:q 既然在 p 下面,LCA 就是 p,父节点那一侧只会收到 p,另一侧是 nil,于是把 p 一直交到根。主例正是这种:5 是 p,4 在它下面,答案 5。
用手走主例。先沉到 4,4 就是 q,交 4 上去,演示第一帧。7 交 nil。2 的右侧拿到 4,把 4 交上去。6 交 nil。轮到 5:5 就是 p,按代码直接返回 5。根 3 左侧拿到 5,右侧 1-0-8 里没有 4,把 5 继续交。演示后两帧把 LCA 钉在 5:5 的子树同时拥有 p 和 q。
若 p、q 分居 3 的两侧,比如 p=5、q=1,则 3 的左右都会非空,LCA 是 3。不要在命中 p 之后还去和 q 比大小——没有 BST。也不要前序「先宣布自己是祖先」:那会把根当成所有人的最近祖先。
左右都找到left != nil && right != nil 时,当前节点就是 LCA。单侧非空只是在接力。自己等于 p 或 q 也算命中:主例 5 盖住了下面的 4,答案仍是 5。
Go:后序返回
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {if root == nil || root == p || root == q { return root }left := lowestCommonAncestor(root.Left, p, q)right := lowestCommonAncestor(root.Right, p, q)if left != nil && right != nil { return root }if left != nil { return left }return right}
1空、或自己就是 p/q,立刻返回。主例走到 5 就返回 5,不再往下翻 4。
2必须先问完左右再判断。前序无法知道两侧各有没有人。
3两侧都非空:p、q 分居左右,自己是 LCA。
4只有一侧非空:把那一侧的节点上交。主例根 3 只在左侧拿到 5。
总结
后序汇报:左右都命中则自己是 LCA。主例 4 在 5 下,答案 5。
- 没有 BST 的序,不能按值选边,必须两边都问。
- 自己等于 p 或 q 也是命中。祖先短路时不必搜完另一人。
- 第一个双侧命中的节点最深,因为子树的答案先算完。