剑指offer-81、⼆叉搜索树的最近公共祖先

题⽬描述

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

  1. 对于该题的最近的公共祖先定义:对于有根树T的两个结点p 、q ,最近公共祖先LCA(T,p,q)表示⼀个结点x ,满⾜x 是p 和q 的祖先且x 的深度尽可能⼤。在这⾥,⼀个节点也可以是它⾃⼰的祖先.
  2. ⼆叉搜索树是若它的左⼦树不空,则左⼦树上所有结点的值均⼩于它的根结点的值; 若它的右⼦树不空,则右⼦树上所有结点的值均⼤于它的根结点的值
  3. 所有节点的值都是唯⼀的。
  4. p 、q 为不同节点且均存在于给定的⼆叉搜索树中。

如果给定以下搜索⼆叉树: {7,1,12,0,4,11,14,#,#,3,5} ,如下图:

示例1 输⼊: {7,1,12,0,4,11,14,#,#,3,5},1,12 输出: 7 说明:节点1 和 节点12的最近公共祖先是7

示例2: 输⼊: {7,1,12,0,4,11,14,#,#,3,5},12,11 输出: 12 说明:因为⼀个节点也可以是它⾃⼰的祖先.所以输出12

思路及解答

迭代遍历

二叉搜索树(BST)的特性,通过迭代查找公共祖先,根据节点值大小关系,决定向左子树或右子树查找

java 复制代码
public class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        
        TreeNode current = root;
        
        while (current != null) {
            // 如果p和q的值都小于当前节点,LCA在左子树
            if (p.val < current.val && q.val < current.val) {
                current = current.left;
            } 
            // 如果p和q的值都大于当前节点,LCA在右子树
            else if (p.val > current.val && q.val > current.val) {
                current = current.right;
            } 
            // 否则当前节点就是LCA
            else {
                return current;
            }
        }
        
        return null; // 未找到
    }
}
  • 时间复杂度:O(h),h为树高,平均O(log n),最坏O(n)
  • 空间复杂度:O(1),只使用常数空间

递归遍历

递归判断节点值关系,决定向左或右递归查找

题⽬已经保证了,两个节点 p , q 都在树上,我们取出根节点 7 ,假设⼩于 7 ,则在左⼦树,如果⼤于7 ,则在右⼦树。

需要查找的两个节点,但凡有⼀个等于根节点,它们的⽗节点就是根节点,因为⼀个节点的⽗节点可以是⾃身(题⽬有说明)。

如果⼀个⼤于根节点,⼀个⼩于更节点,其最近公共祖先也是根节点。如果两个都⼤于,或者两个都⼩于,怎么办?

当然是递归,如果两个都⼩于,那么就取当前的左⼦树进⾏递归,直到符合要求。⽐如查找,3 和 5,由于 3 和 5 都⼩于 7,那么取左⼦树 1 下⾯的进⾏递归:

java 复制代码
class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}
public class Solution68 {
    public int lowestCommonAncestor(TreeNode root, int p, int q) {
        TreeNode result = commonAncestor(root, p, q);
        return result == null ? -1 : result.val;
    }
    public TreeNode commonAncestor(TreeNode root, int p, int q) {
        // 等于空
        if (root == null) {
            return null;
        }
        if (root.val == p || root.val == q) {
            // 有⼀个值等于根节点
            return root;
        }
        // 在左⼦树
        if (p < root.val && q < root.val) {
            return commonAncestor(root.left, p, q);
        } else if (p > root.val && q > root.val) {
            // 两个都在右⼦树
            return commonAncestor(root.right, p, q);
        } else {
            return root;
        }
    }
}
  • 时间复杂度:O(h),递归深度为树高
  • 空间复杂度:O(h),递归调用栈空间

通用二叉树

假设这道题条件改⼀下,如果不是⼆叉搜索树,怎么办?

如果不是⼆叉搜索树,那么我们不能直接判断出它在左⼦树,还是在右⼦树。不如暴⼒点,先在左⼦树中找,如果右⼦树没找到,说明都在左⼦树,如果左⼦树没找到,说明都在右⼦树,如果两个都分别存在,说明当前节点就是他们的⽗节点。

java 复制代码
public class Solution68 {
    public int lowestCommonAncestor(TreeNode root, int p, int q) {
        TreeNode result = commonAncestor(root, p, q);
        return result == null ? -1 : result.val;
    }
    public TreeNode commonAncestor(TreeNode root, int p, int q) {
        if (null == root) {
            return null;
        }
        if (root.val == p || root.val == q) {
            return root;
        }
        TreeNode left = commonAncestor(root.left, p, q);
        TreeNode right = commonAncestor(root.right, p, q);
        if (left == null) {
            return right;
        } else if (right == null) {
            return left;
        } else {
            return root;
        }
    }
}
  • 时间复杂度:O(n),需要遍历整个树
  • 空间复杂度:O(h),递归栈深度
相关推荐
lee_curry7 小时前
第四章 jvm中的垃圾回收器
java·jvm·垃圾收集器
九转成圣8 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
直奔標竿8 小时前
Java开发者AI转型第二十七课!Spring AI 个人知识库实战(六)——全栈闭环收官,解锁前端流式渲染终极技巧
java·开发语言·前端·人工智能·后端·spring
金銀銅鐵9 小时前
[java] 编译之后的记录类(Record Classes)长什么样子(上)
java·jvm·后端
野生技术架构师11 小时前
金三银四面试总结篇,汇总 Java 面试突击班后的面试小册
java·面试·职场和发展
小袁拒绝摆烂11 小时前
多表关联大平层转JSON树形结构
java·json
ja哇12 小时前
大厂面试高频八股
java·面试·职场和发展
yoyo_zzm12 小时前
Laravel6.x新特性全解析
java·spring boot·后端
Nick_zcy12 小时前
小说在线阅读网站和小说管理系统 · 功能全解析
java·后端·python·springboot·ruoyi