26.二叉树的最近公共祖先

发布时间 2023-09-07 20:29:08作者: autumnmoonming

236.二叉树的最近公共祖先

1、概要

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

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

说明:

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

首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。二叉树回溯的过程就是从低到上。

后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。

如何判断一个节点是节点q和节点p的公共祖先呢。

判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)

2、思路

递归

  • 确定递归函数返回值以及参数

需要返回,告诉是否找到两个节点,bool类型就可以。但还要返回最近公共节点,所以遇到p,q返回,不为空也说明找到了。

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
  • 确定终止条件

遇到空,树空了,如果找到p,q,将其返回,中节点处理会用到。

if (root == null || root == p || root == q) { // 递归结束条件
            return root;
        }
  • 确定单层递归逻辑

有返回值,回溯过程需要递归函数的返回值判断。需要遍历树的所有节点

在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)

为什么要遍历整棵树呢?直观上来看,在左边找到最近公共祖先,直接一路返回就可以了。

但事实上还要遍历根节点右子树(即使此时已经找到了目标节点了)

因为在如下代码的后序遍历中,如果想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。

如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解

如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然

image-20230907162137883

图中节点10的左子树返回null,右子树返回目标值7,那么此时节点10的处理逻辑就是把右子树的返回值(最近公共祖先7)返回上去!

那么如果left和right都为空,则返回left或者right都是可以的,也就是返回空。

// 后序遍历
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if(left == null && right == null) { // 若未找到节点 p 或 q
            return null;
        }else if(left == null && right != null) { // 若找到一个节点
            return right;
        }else if(left != null && right == null) { // 若找到一个节点
            return left;
        }else { // 若找到两个节点
            return root;
        }

迭代

没有给出迭代法,因为迭代法不适合模拟回溯的过程。理解递归的解法就够了

3、代码

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q){
            return root;
        }

        TreeNode left = lowestCommonAncestor(root.left,p,q);
        TreeNode right = lowestCommonAncestor(root.right,p,q);

        if(left == null && right== null){// 若未找到节点 p 或 q
            return null;
        }else if(left == null && right != null){ // 若找到一个节点,在右边
            return right;
        }else if(left != null && right == null){// 若找到一个节点,在左边
            return left;
        }else { // 若找到两个节点
            return root;
        }
    }
}