BM38 在二叉树中找到两个节点的最近公共祖先
描述
给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。数据范围:树上节点数满足  , 节点值val满足区间 [0,n)要求:时间复杂度 
注:本题保证二叉树中每个节点的val值均不相同。如当输入{3,5,1,6,2,0,8,#,#,7,4},5,1时,二叉树{3,5,1,6,2,0,8,#,#,7,4}如下图所示:
![[二叉树]BM38 在二叉树中找到两个节点的最近公共祖先-中等_最近公共祖先_03](https://file.cfanz.cn/uploads/png/2022/06/16/8/C0O599Z39V.png)
所以节点值为5和节点值为1的节点的最近公共祖先节点的节点值为3,所以对应的输出为3。节点本身可以视为自己的祖先
示例1
输入:
{3,5,1,6,2,0,8,#,#,7,4},5,1复制返回值:
3复制
示例2
输入:
{3,5,1,6,2,0,8,#,#,7,4},2,7复制返回值:
2题解
通过保存查找路径进行判断
对于一颗二叉树的任意2个节点,他们到根结点的路径中第一个相等的节点就是他们最近的公共祖先节点。我们要做的就是找到这 2个节点到根结点的路径。和BM37中的查找二叉树不一样,普通二叉树我们要找到到达某个节点的路径,只能通过前序遍历获取。因此这个题目又变成了我们熟悉的前序遍历的类型了。实现代码如下:
// https://www.nowcoder.com/practice/e0cc33a83afe4530bcec46eba3325116?tpId=295&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj
struct TreeNode
{
  int val;
  struct TreeNode *left;
  struct TreeNode *right;
  TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 通过前序遍历,将指定值的查找路径保存起来
// 通过2次查找,可以对比得到他们的最近公共节点
bool get_path(TreeNode *root, int val, std::vector<TreeNode *> &v)
{
  if (root == nullptr)
  {
    return false;
  }
  v.push_back(root);
  if (root->val == val)
  {
    return true;
  }
  bool finded = get_path(root->left, val, v);
  if (finded)
  {
    return true;
  }
  finded = get_path(root->right, val, v);
  if (finded)
  {
    return true;
  }
  v.pop_back();
  return false;
}
int lowestCommonAncestor(TreeNode *root, int o1, int o2)
{
  std::vector<TreeNode *> path_1;
  std::vector<TreeNode *> path_2;
  get_path(root, o1, path_1);
  get_path(root, o2, path_2);
  int i = 0;
  TreeNode *pre_node = nullptr;
  while (i < path_1.size() && i < path_2.size())
  {
    if (path_1[i] != path_2[i])
    {
      return pre_node->val;
    }
    pre_node = path_1[i];
  }
  return pre_node->val;
}通过一次递归查找
祖先:若节点 o1 在 root的左子树/右子树上,或 o1 = root, 则 root 为 o1 祖先
最近公共祖先:节点 root 为 o1 和 o2 的公共祖先,且 root的左子树/右子树都不为 o1 和 o2的公共祖先,则 root 为 o1 和 o2 的最近公告祖先
思路:
- 1. 通过后序遍历来判断一个节点是否包含o1或者o2
 - 2. 在后续遍历的过程中,先判断左子树,后判断右子树,最后再判断根结点
 - 3. 如果左子树中不包含o1和o2,那么o1和o2要么都在右子树中,要么当前节点就是o1和o2的公共节点
 
- a. 如果当前节点不等于o1和o2,那么右子树就是o1和o2的公共节点,但可能不是最近公共祖先节点,我们要递归查找右子树
 - b. 如果当前节点等于o1或者o2,那么当前节点就是最近公共子节点
 
TreeNode *find(TreeNode *root, int o1, int o2)
{
  if (root == nullptr)
  {
    return nullptr;
  }
  if (root->val == o1 || root->val == o2)
  {
    return root;// 当前节可能是最近公共祖先节点,或者当前节点是o1、o2
  }
  auto left = find(root->left, o1, o2); // 查找左子树
  auto right = find(root->right, o1, o2);// 查找右子树
  if (left == nullptr)// 没有在左子树中找到o1和o2,则右子树一定是他们的公共祖先
  {
    return right;
  }
  if (right == nullptr)
  {
    return left;
  }
  return root;// 左子树和右子树都找到了o1或者o2,则root节点就是他们的公共祖先节点
}
int lowestCommonAncestor_1(TreeNode *root, int o1, int o2)
{
  return find(root, o1, o2)->val;
}










