二叉树的最近公共祖先
题目描述
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉树中。
思路
思路一:
题目说明提出p、q为不同节点,且一定存在于二叉树中,那么从root节点开始,一定能找到一条到p或q节点的路径。
因此对树做个遍历,找到root->p和root->q的路径,再对这两个路径求最后一个公共点,就是最近公共祖先
code
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
type res struct {
val *TreeNode
path []*TreeNode
ok bool
}
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
r1, r2 := res{
val: p,
path: make([]*TreeNode, 0),
ok: false,
}, res{
val: q,
path: make([]*TreeNode, 0),
ok: false,
}
findPath(root, &r1, &r2)
i, j := 0, 0
for ; i < len(r1.path) && j < len(r2.path); i, j = i+1, j+1 {
if r1.path[i] != r2.path[j] {
break
}
}
return r1.path[i-1]
}
func findPath(root *TreeNode, r1, r2 *res) {
if root == nil {
return
}
if !r1.ok {
r1.path = append(r1.path, root)
}
if !r2.ok {
r2.path = append(r2.path, root)
}
if root == r1.val {
r1.ok = true
}
if root == r2.val {
r2.ok = true
}
findPath(root.Left, r1, r2)
findPath(root.Right, r1, r2)
if !r1.ok {
r1.path = r1.path[:len(r1.path)-1]
}
if !r2.ok {
r2.path = r2.path[:len(r2.path)-1]
}
}
思路二:
题目说明提出p、q为不同节点,且一定存在于二叉树中。
那么从root节点开始,分为四种情况,
- 其中一个节点为root节点,那么直接返回root
- 两个节点都在左子树,那么将左孩子作为root,递归查找
- 两个节点都在右子树,那么将右孩子作为root,递归查找
- 两个节点分别在左子树和右子树,那么结果为root
对于题目中的样例,
- 情况一:假设p、q分别为5,1,那么对于root-3,我们能在左子树找到节点p,能在右子树找到节点q,因此结果就是root
- 情况二:假设p、q分别为6,2,那么对于root-3,我们只能在左子树找到结果,右子树为nil,因此结果一定在左子树的节点上,继续以节点5为root来看,回到了一的情况中,能在左子树和右子树找到结果,因此结果为节点5
- 情况三:假设p、q分别为0,8,那么对于root-3,我们只能在右子树找到结果,左子树为nil,因此结果一定在右子树的节点上,继续以节点1为root来看,回到了一的情况中,能在左子树和右子树找到结果,因此结果为节点1
总结来说,对于目标节点p和q,以某一节点r为起点,如果我们在左子树和右子树分别找到p和q,那么节点r就是最近公共祖先,如果我们只在某一个子树找到两个节点,则表示当前节点一定不是最近公共祖先,而是在子树中,因此递归往下查找。
code
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == nil {
return nil
}
// 当前节点等于p或q,表示找到了目标节点
if root == p || root == q {
return root
}
// 尝试在左子树和右子树中,查找目标节点,如果找不到就返回nil
left := lowestCommonAncestor(root.Left, p, q)
right := lowestCommonAncestor(root.Right, p, q)
// left和right都不为nil,表示分别在左子树和右子树中找到了节点,因此当前节点就是最近公共祖先。
if left != nil && right != nil {
return root
}
// right为nil,表示两个节点都在左子树找到,因此最近公共祖先在左子树,而不是当前节点。
if left != nil {
return left
}
return right
}