leetcode
leetcode 201 ~ 250
二叉树的最近公共祖先

二叉树的最近公共祖先

难度:

标签:

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

 

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

 

提示:

  • 树中节点数目在范围 [2, 105] 内。
  • -109 <= Node.val <= 109
  • 所有 Node.val 互不相同
  • p != q
  • pq 均存在于给定的二叉树中。

代码结果

运行时间: 68 ms, 内存: 25.5 MB


/*
 * 思路:
 * 使用Java Stream和自定义类来保存遍历状态和查找结果。
 * 1. 通过流式计算遍历二叉树。
 * 2. 使用Optional保存查找结果。
 * 3. 如果左子树和右子树都有结果,返回当前节点。
 * 4. 否则返回有结果的一边。
 */
import java.util.Optional;
import java.util.stream.Stream;
 
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return find(root, p, q).orElse(null);
    }
 
    private Optional<TreeNode> find(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) {
            return Optional.empty();
        }
        if (root == p || root == q) {
            return Optional.of(root);
        }
        Optional<TreeNode> left = find(root.left, p, q);
        Optional<TreeNode> right = find(root.right, p, q);
        if (left.isPresent() && right.isPresent()) {
            return Optional.of(root);
        }
        return left.isPresent() ? left : right;
    }
}
 

解释

方法:

该题解采用递归的方式来查找两个节点 p 和 q 的最近公共祖先。递归函数对当前节点 root 进行如下处理: 1. 如果 root 为空,或者 root 等于 p 或 q,则直接返回 root。 2. 递归查找 root 的左子树和右子树,分别得到左子树和右子树中 p 和 q 的最近公共祖先 left 和 right。 3. 如果 left 和 right 都为空,说明在当前子树中没有找到 p 和 q,返回 None。 4. 如果 left 为空,right 不为空,则说明 p 和 q 都在右子树中,返回 right。 5. 如果 right 为空,left 不为空,则说明 p 和 q 都在左子树中,返回 left。 6. 如果 left 和 right 都不为空,说明 p 和 q 分别在左右子树中,则当前节点 root 就是最近公共祖先,返回 root。

时间复杂度:

平均 O(logn),最坏 O(n)

空间复杂度:

平均 O(logn),最坏 O(n)

代码细节讲解

🦆
递归函数中,如果left和right都不为空时,为何可以断定当前root就是最近公共祖先?
在递归查找过程中,如果当前节点的左子树返回了一个非空节点(left),这意味着左子树中至少包含 p 或 q 中的一个;同样,右子树返回了一个非空节点(right),这意味着右子树中至少包含另一个。因此,如果 left 和 right 都不为空,这表明 p 和 q 分别位于当前节点的两侧,即一个在左子树,另一个在右子树。由于最近公共祖先定义为“对于任意两个节点,以最低节点作为其共同的祖先”,因此当前节点 root 必然是 p 和 q 的最近公共祖先。
🦆
在递归查找过程中,返回None的条件是基于什么考虑?是否存在其他情况下应该返回None但没有涵盖的?
返回 None 的条件是基于当前子树中不存在 p 和 q 的情况。当递归到某个节点时,如果左右子树的递归返回都是 None,这说明在该节点的左右子树中都没有找到 p 或 q。这种设计有效地涵盖了所有必要的情形,即如果 p 和 q 本身就不存在于树中,或者当前递归分支不包含这两个节点。因此,当前设计的返回 None 的逻辑是完备的,没有未涵盖的情况。
🦆
在二叉树的结构不是完全二叉树时,节点p和q的位置对算法的效率影响如何?
在非完全二叉树中,p 和 q 的位置确实可以影响算法的效率。如果 p 和 q 靠近树的根部,算法可能会更快找到最近公共祖先,因为递归深度较浅;相反,如果 p 和 q 位于树的较深层次,尤其是如果它们位于树的不同侧且位置偏低,那么算法需要更深层次的递归调用才能确认它们的位置并最终确定公共祖先。因此,p 和 q 的深度和它们相对于树根的位置直接影响递归调用的次数和算法的运行时间。

相关问题

二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

 

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

 

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。

最小公共区域

最小公共区域